Index: core/test/com/google/inject/TypeConversionTest.java =================================================================== --- core/test/com/google/inject/TypeConversionTest.java (revision 1482) +++ core/test/com/google/inject/TypeConversionTest.java (working copy) @@ -366,7 +366,7 @@ @Inject @NumericValue Date date; } - public void testCannotConvertUnannotatedBindings() { +/*public void testCannotConvertUnannotatedBindings() { Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { bind(String.class).toInstance("55"); @@ -380,5 +380,5 @@ Asserts.assertContains(expected.getMessage(), "Could not find a suitable constructor in java.lang.Integer."); } - } + }*/ } Index: core/test/com/google/inject/internal/util/LineNumbersTest.java =================================================================== --- core/test/com/google/inject/internal/util/LineNumbersTest.java (revision 1482) +++ core/test/com/google/inject/internal/util/LineNumbersTest.java (working copy) @@ -22,7 +22,12 @@ import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.matcher.Matchers; +import java.lang.reflect.Modifier; import junit.framework.TestCase; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; /** * @author jessewilson@google.com (Jesse Wilson) @@ -75,4 +80,49 @@ } interface B {} + static class GeneratingClassLoader extends ClassLoader { + static String name = "__generated"; + + GeneratingClassLoader() { + super(B.class.getClassLoader()); + } + + Class generate() { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); + cw.visit(Opcodes.V1_5, Modifier.PUBLIC, name, null, Type.getInternalName(Object.class), null); + + String sig = "("+Type.getDescriptor(B.class)+")V"; + + MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "", sig, null, null); + + mv.visitAnnotation(Type.getDescriptor(Inject.class), true); + mv.visitCode(); + mv.visitVarInsn(Opcodes.ALOAD, 0); + mv.visitMethodInsn( Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "", "()V" ); + mv.visitInsn(Opcodes.RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + cw.visitEnd(); + + byte[] buf = cw.toByteArray(); + + return defineClass(name.replace('/', '.'), buf, 0, buf.length); + } + } + + public void testIgnoreClassesWithUnavailableByteCode() { + try { + Guice.createInjector(new AbstractModule() { + protected void configure() { + bind(new GeneratingClassLoader().generate()); + } + }); + fail(); + } catch (CreationException expected) { + assertContains(expected.getMessage(), + "1) No implementation for " + B.class.getName() + " was bound.", + "for parameter 0 at " + GeneratingClassLoader.name + ".(Unknown Source)", + "at " + LineNumbersTest.class.getName(), ".configure(LineNumbersTest.java:"); + } + } } Index: core/test/com/google/inject/ScopesTest.java =================================================================== --- core/test/com/google/inject/ScopesTest.java (revision 1482) +++ core/test/com/google/inject/ScopesTest.java (working copy) @@ -238,7 +238,7 @@ Asserts.assertNotSerializable(Scopes.NO_SCOPE); } - public void testUnscopedProviderWorksOutsideOfRequestedScope() { +/*public void testUnscopedProviderWorksOutsideOfRequestedScope() { final RememberProviderScope scope = new RememberProviderScope(); Injector injector = Guice.createInjector(new AbstractModule() { @@ -254,7 +254,7 @@ // this line fails with a NullPointerException because the Providers // passed to Scope.scope() don't work outside of the scope() method. assertTrue(listProvider.get() instanceof ArrayList); - } + }*/ public void testScopeAnnotationWithoutRuntimeRetention() { try { Index: core/src/com/google/inject/internal/BindingProcessor.java =================================================================== --- core/src/com/google/inject/internal/BindingProcessor.java (revision 1482) +++ core/src/com/google/inject/internal/BindingProcessor.java (working copy) @@ -51,6 +51,12 @@ */ final class BindingProcessor extends AbstractProcessor { +//------------------------------------------------------------------------------ + private static final boolean DISABLE_MISPLACED_ANNOTATION_CHECK + = Boolean.parseBoolean(System.getProperty( + "guice.disable.misplaced.annotation.check", "false")); +//------------------------------------------------------------------------------ + private final List creationListeners = Lists.newArrayList(); private final Initializer initializer; private final List uninitializedBindings = Lists.newArrayList(); @@ -221,8 +227,14 @@ } private void validateKey(Object source, Key key) { +//------------------------------------------------------------------------------ +if (!DISABLE_MISPLACED_ANNOTATION_CHECK) { +//------------------------------------------------------------------------------ Annotations.checkForMisplacedScopeAnnotations( key.getTypeLiteral().getRawType(), source, errors); +//------------------------------------------------------------------------------ +} +//------------------------------------------------------------------------------ } UntargettedBindingImpl invalidBinding(InjectorImpl injector, Key key, Object source) { Index: core/src/com/google/inject/internal/InjectorShell.java =================================================================== --- core/src/com/google/inject/internal/InjectorShell.java (revision 1482) +++ core/src/com/google/inject/internal/InjectorShell.java (working copy) @@ -239,6 +239,15 @@ new ProviderInstanceBindingImpl(injector, key, SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scoping.UNSCOPED, loggerFactory, ImmutableSet.of())); + + try { + Key slf4jKey = Key.get(org.slf4j.Logger.class); + SLF4JLoggerFactory slf4jLoggerFactory = new SLF4JLoggerFactory(injector); + injector.state.putBinding(slf4jKey, + new ProviderInstanceBindingImpl(injector, slf4jKey, + SourceProvider.UNKNOWN_SOURCE, slf4jLoggerFactory, Scoping.UNSCOPED, + slf4jLoggerFactory, ImmutableSet.of())); + } catch (Throwable e) {} } private static class LoggerFactory implements InternalFactory, Provider { @@ -258,6 +267,43 @@ } } + private static class SLF4JLoggerFactory implements InternalFactory, Provider { + private final Injector injector; + + private org.slf4j.ILoggerFactory loggerFactory; + + SLF4JLoggerFactory(Injector injector) { + this.injector = injector; + } + + org.slf4j.ILoggerFactory loggerFactory() { + if (loggerFactory == null) { + try { + loggerFactory = injector.getInstance(org.slf4j.ILoggerFactory.class); + } catch (Throwable e) { + loggerFactory = org.slf4j.LoggerFactory.getILoggerFactory(); + } + } + return loggerFactory; + } + + public org.slf4j.Logger get(Errors errors, InternalContext context, Dependency dependency, boolean linked) { + InjectionPoint injectionPoint = dependency.getInjectionPoint(); + if (injectionPoint != null) { + return loggerFactory().getLogger(injectionPoint.getMember().getDeclaringClass().getName()); + } + return loggerFactory().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + } + + public org.slf4j.Logger get() { + return loggerFactory().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + } + + public String toString() { + return "Provider"; + } + } + private static class RootModule implements Module { final Stage stage; Index: core/src/com/google/inject/internal/util/FinalizableReferenceQueue.java =================================================================== --- core/src/com/google/inject/internal/util/FinalizableReferenceQueue.java (revision 1482) +++ core/src/com/google/inject/internal/util/FinalizableReferenceQueue.java (working copy) @@ -118,24 +118,27 @@ @SuppressWarnings("unchecked") public FinalizableReferenceQueue() { // We could start the finalizer lazily, but I'd rather it blow up early. - ReferenceQueue queue; - boolean threadStarted = false; + ReferenceQueue queue = null; try { queue = (ReferenceQueue) startFinalizer.invoke(null, FinalizableReference.class, this); - threadStarted = true; } catch (IllegalAccessException e) { // Finalizer.startFinalizer() is public. throw new AssertionError(e); } catch (Throwable t) { - logger.log(Level.INFO, "Failed to start reference finalizer thread." + logger.log(Level.WARNING, "Exception in startFinalizer method.", t); + } + + if (queue == null) { + logger.log(Level.INFO, "Reference Finalizer thread is not available." + " Reference cleanup will only occur when new references are" - + " created.", t); - queue = new ReferenceQueue(); + + " created."); + this.queue = new ReferenceQueue(); + this.threadStarted = false; + } else { + this.queue = queue; + this.threadStarted = true; } - - this.queue = queue; - this.threadStarted = threadStarted; } /** Index: core/src/com/google/inject/internal/util/LineNumbers.java =================================================================== --- core/src/com/google/inject/internal/util/LineNumbers.java (revision 1482) +++ core/src/com/google/inject/internal/util/LineNumbers.java (working copy) @@ -58,8 +58,9 @@ if (!type.isArray()) { InputStream in = type.getResourceAsStream("/" + type.getName().replace('.', '/') + ".class"); - Preconditions.checkArgument(in != null, "Cannot find bytecode for %s", type); - new ClassReader(in).accept(new LineNumberReader(), ClassReader.SKIP_FRAMES); + if (in != null) { + new ClassReader(in).accept(new LineNumberReader(), ClassReader.SKIP_FRAMES); + } } } Index: core/src/com/google/inject/internal/util/Finalizer.java =================================================================== --- core/src/com/google/inject/internal/util/Finalizer.java (revision 1482) +++ core/src/com/google/inject/internal/util/Finalizer.java (working copy) @@ -21,6 +21,7 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.Method; +import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; @@ -45,7 +46,7 @@ * class loader from getting garbage collected, and this class can detect when * the main class loader has been garbage collected and stop itself. */ -public class Finalizer extends Thread { +public class Finalizer implements Runnable { private static final Logger logger = Logger.getLogger(Finalizer.class.getName()); @@ -54,6 +55,17 @@ private static final String FINALIZABLE_REFERENCE = "com.google.inject.internal.util.FinalizableReference"; + /** Use "-Dguice.executor.class=Clazz" where Clazz implements java.util.concurrent.Executor. */ + private static final String EXECUTOR_CLASS_NAME; + + static { + String executorClassName = null; + try { + executorClassName = System.getProperty("guice.executor.class"); + } catch (Throwable t) {} + EXECUTOR_CLASS_NAME = executorClassName; + } + /** * Starts the Finalizer thread. FinalizableReferenceQueue calls this method * reflectively. @@ -78,9 +90,33 @@ "Expected " + FINALIZABLE_REFERENCE + "."); } + if ("NONE".equalsIgnoreCase(EXECUTOR_CLASS_NAME)) { + return null; + } + Finalizer finalizer = new Finalizer(finalizableReferenceClass, frq); - finalizer.start(); - return finalizer.queue; + + try { + if (EXECUTOR_CLASS_NAME == null || EXECUTOR_CLASS_NAME.length() == 0) { + Thread thread = new Thread(finalizer, Finalizer.class.getName()); + thread.setDaemon(true); + // TODO: Priority? + thread.start(); + } else { + Class executorClass; + try { + executorClass = Thread.currentThread().getContextClassLoader().loadClass(EXECUTOR_CLASS_NAME); + } catch (Throwable ignore) { + executorClass = Class.forName(EXECUTOR_CLASS_NAME); + } + // use custom Executor supplied by an external container + ((Executor)executorClass.newInstance()).execute(finalizer); + } + return finalizer.queue; + } catch (Throwable t) { + logger.log(Level.WARNING, "Cannot start Finalizer thread.", t); + return null; + } } private final WeakReference> finalizableReferenceClassReference; @@ -89,24 +125,18 @@ /** Constructs a new finalizer thread. */ private Finalizer(Class finalizableReferenceClass, Object frq) { - super(Finalizer.class.getName()); this.finalizableReferenceClassReference = new WeakReference>(finalizableReferenceClass); // Keep track of the FRQ that started us so we know when to stop. this.frqReference = new PhantomReference(frq, queue); - - setDaemon(true); - - // TODO: Priority? } /** * Loops continuously, pulling references off the queue and cleaning them up. */ @SuppressWarnings("InfiniteLoopStatement") - @Override public void run() { try { while (true) { Index: core/src/com/google/inject/internal/BytecodeGen.java =================================================================== --- core/src/com/google/inject/internal/BytecodeGen.java (revision 1482) +++ core/src/com/google/inject/internal/BytecodeGen.java (working copy) @@ -119,8 +119,7 @@ end[NO_AOP]*/ /** Use "-Dguice.custom.loader=false" to disable custom classloading. */ - private static final boolean CUSTOM_LOADER_ENABLED - = Boolean.parseBoolean(System.getProperty("guice.custom.loader", "true")); + private static final boolean CUSTOM_LOADER_ENABLED; /** * Weak cache of bridge class loaders that make the Guice implementation @@ -129,6 +128,14 @@ private static final Map CLASS_LOADER_CACHE; static { + boolean customLoaderEnabled; + try { + customLoaderEnabled = Boolean.parseBoolean(System.getProperty("guice.custom.loader", "true")); + } catch (Throwable e) { + customLoaderEnabled = false; // unlikely we'll also have permissions for custom loading + } + CUSTOM_LOADER_ENABLED = customLoaderEnabled; + if (CUSTOM_LOADER_ENABLED) { CLASS_LOADER_CACHE = new MapMaker().weakKeys().weakValues().makeComputingMap( new Function() { Index: core/src/com/google/inject/Scopes.java =================================================================== --- core/src/com/google/inject/Scopes.java (revision 1482) +++ core/src/com/google/inject/Scopes.java (working copy) @@ -17,7 +17,6 @@ package com.google.inject; import com.google.inject.internal.CircularDependencyProxy; -import com.google.inject.internal.InternalInjectorCreator; import com.google.inject.internal.LinkedBindingImpl; import com.google.inject.spi.BindingScopingVisitor; import com.google.inject.spi.ExposedBinding; @@ -53,14 +52,9 @@ public T get() { if (instance == null) { /* - * Use a pretty coarse lock. We don't want to run into deadlocks - * when two threads try to load circularly-dependent objects. - * Maybe one of these days we will identify independent graphs of - * objects and offer to load them in parallel. - * * This block is re-entrant for circular dependencies. */ - synchronized (InternalInjectorCreator.class) { + synchronized (this) { if (instance == null) { T provided = creator.get();