Created
July 7, 2014 18:07
-
-
Save gissuebot/71e1b85dcdc10e257a59 to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 258, comment 0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: src/com/google/inject/spi/CloseErrors.java | |
=================================================================== | |
--- src/com/google/inject/spi/CloseErrors.java Mon Oct 06 13:42:22 BST 2008 | |
+++ src/com/google/inject/spi/CloseErrors.java Mon Oct 06 13:42:22 BST 2008 | |
@@ -0,0 +1,40 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+/** | |
+ * A handler of exceptions when closing down resources. | |
+ * Implementations will typically create a collection of error messages so that all of the different | |
+ * close exceptions are collected together. | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface CloseErrors { | |
+ | |
+ /** | |
+ * Notification of a close exception | |
+ * @param key the key of the object being closed which is usually a {@link com.google.inject.Key} | |
+ * or {@link String} | |
+ * @param object the object being closed | |
+ * @param cause the exception thrown when the close was attempted | |
+ */ | |
+ void closeError(Object key, Object object, Exception cause); | |
+ | |
+ void throwIfNecessary() throws CloseFailedException; | |
+} | |
Index: test/com/google/inject/AllTests.java | |
=================================================================== | |
--- test/com/google/inject/AllTests.java (revision 627) | |
+++ test/com/google/inject/AllTests.java Mon Oct 06 12:25:29 BST 2008 | |
@@ -59,6 +59,7 @@ | |
suite.addTestSuite(InjectionPointTest.class); | |
suite.addTestSuite(InjectorTest.class); | |
suite.addTestSuite(IntegrationTest.class); | |
+ suite.addTestSuite(CloseTest.class); | |
suite.addTestSuite(KeyTest.class); | |
suite.addTestSuite(LoggerInjectionTest.class); | |
suite.addTestSuite(ModuleTest.class); | |
Index: src/com/google/inject/AbstractModule.java | |
=================================================================== | |
--- src/com/google/inject/AbstractModule.java (revision 627) | |
+++ src/com/google/inject/AbstractModule.java Fri Oct 03 16:37:08 BST 2008 | |
@@ -26,6 +26,7 @@ | |
import com.google.inject.spi.TypeConverter; | |
import java.lang.annotation.Annotation; | |
import java.lang.reflect.Method; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
import org.aopalliance.intercept.MethodInterceptor; | |
/** | |
@@ -167,6 +168,15 @@ | |
} | |
/** | |
+ * @see Binder#bindConstructorInterceptor(com.google.inject.matcher.Matcher, | |
+ * org.aopalliance.intercept.ConstructorInterceptor[]) | |
+ */ | |
+ protected void bindConstructorInterceptor(Matcher<? super Class<?>> classMatcher, | |
+ ConstructorInterceptor... interceptors) { | |
+ binder.bindConstructorInterceptor(classMatcher, interceptors); | |
+ } | |
+ | |
+ /** | |
* Adds a dependency from this module to {@code key}. When the injector is | |
* created, Guice will report an error if {@code key} cannot be injected. | |
* Note that this requirement may be satisfied by implicit binding, such as | |
Index: jsr250/src/com/google/inject/jsr250/ResourceProviderFactory.java | |
=================================================================== | |
--- jsr250/src/com/google/inject/jsr250/ResourceProviderFactory.java Tue Oct 07 15:56:41 BST 2008 | |
+++ jsr250/src/com/google/inject/jsr250/ResourceProviderFactory.java Tue Oct 07 15:56:41 BST 2008 | |
@@ -0,0 +1,105 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jsr250; | |
+ | |
+import com.google.common.base.Objects; | |
+import com.google.inject.Inject; | |
+import com.google.inject.Provider; | |
+import com.google.inject.ProvisionException; | |
+import com.google.inject.spi.AnnotationProviderFactory; | |
+import com.google.inject.spi.InjectionAnnotation; | |
+import java.beans.Introspector; | |
+import java.lang.reflect.AnnotatedElement; | |
+import java.lang.reflect.Field; | |
+import java.lang.reflect.Method; | |
+import javax.annotation.Resource; | |
+import javax.naming.Context; | |
+import javax.naming.NamingException; | |
+ | |
+/** | |
+ * Injects objects from a JNDI context using the {@link Resource} annotation | |
+ * to define the annotation injection point | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+@InjectionAnnotation(Resource.class) | |
+public class ResourceProviderFactory<T> implements AnnotationProviderFactory<T> { | |
+ private final Context context; | |
+ | |
+ // TODO not sure why we absolutely must annotate this single constructor? | |
+ // see http://groups.google.com/group/google-guice/browse_thread/thread/2a252b1a3f7b3779 | |
+ @Inject | |
+ public ResourceProviderFactory(Context context) { | |
+ this.context = context; | |
+ } | |
+ | |
+ public Provider<T> createProvider(AnnotatedElement member) { | |
+ Resource resource = member.getAnnotation(Resource.class); | |
+ Objects.nonNull(resource, "@Resource is missing!"); | |
+ if (resource != null) { | |
+ String name = getJndiName(resource, member); | |
+ return new ResourceProvider<T>(name); | |
+ } | |
+ return null; | |
+ } | |
+ | |
+ /** Deduces the JNDI name from the resource and member according to the JSR 250 rules */ | |
+ String getJndiName(Resource resource, AnnotatedElement member) { | |
+ String answer = resource.name(); | |
+ if (answer == null || answer.length() == 0) { | |
+ if (member instanceof Method) { | |
+ Method method = (Method) member; | |
+ answer = method.getName(); | |
+ if (answer.startsWith("set")) { | |
+ // lets switch the setter to the bean property name | |
+ answer = Introspector.decapitalize(answer.substring(3)); | |
+ } | |
+ } | |
+ else if (member instanceof Field) { | |
+ Field field = (Field) member; | |
+ answer = field.getName(); | |
+ } | |
+ } | |
+ if (answer == null || answer.length() == 0) { | |
+ throw new IllegalArgumentException("No name defined"); | |
+ } | |
+ return answer; | |
+ } | |
+ | |
+ class ResourceProvider<T> implements Provider<T> { | |
+ private final String name; | |
+ | |
+ public ResourceProvider(String name) { | |
+ this.name = name; | |
+ } | |
+ | |
+ public T get() { | |
+ try { | |
+ return (T) context.lookup(name); | |
+ } | |
+ catch (NamingException e) { | |
+ throw new ProvisionException("Failed to find name '" + name + "' in JNDI. Cause: " + e, e); | |
+ } | |
+ } | |
+ | |
+ @Override public String toString() { | |
+ return "ResourceProvider(" + name + ")"; | |
+ } | |
+ } | |
+} | |
Index: src/com/google/inject/spi/ConstructorInterceptorBinding.java | |
=================================================================== | |
--- src/com/google/inject/spi/ConstructorInterceptorBinding.java Fri Oct 03 16:08:58 BST 2008 | |
+++ src/com/google/inject/spi/ConstructorInterceptorBinding.java Fri Oct 03 16:08:58 BST 2008 | |
@@ -0,0 +1,65 @@ | |
+/** | |
+ * Copyright (C) 2008 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+import static com.google.common.base.Preconditions.checkNotNull; | |
+import com.google.inject.matcher.Matcher; | |
+import java.util.Arrays; | |
+import static java.util.Collections.unmodifiableList; | |
+import java.util.List; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
+ | |
+/** | |
+ * Registration of {@link ConstructorInterceptor} instances for matching classes. Instances are created | |
+ * explicitly in a module using {@link com.google.inject.Binder#bindConstructorInterceptor(com.google.inject.matcher.Matcher, org.aopalliance.intercept.ConstructorInterceptor[])} | |
+ * statements: | |
+ * <pre> | |
+ * bindConstructorInterceptor(Matchers.subclassesOf(MyAction.class), | |
+ * new MyPostConstructionInterceptor());</pre> | |
+ * | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ */ | |
+public final class ConstructorInterceptorBinding implements Element { | |
+ private final Object source; | |
+ private final Matcher<? super Class<?>> classMatcher; | |
+ private final List<ConstructorInterceptor> interceptors; | |
+ | |
+ ConstructorInterceptorBinding( | |
+ Object source, | |
+ Matcher<? super Class<?>> classMatcher, | |
+ ConstructorInterceptor[] interceptors) { | |
+ this.source = checkNotNull(source, "source"); | |
+ this.classMatcher = checkNotNull(classMatcher, "classMatcher"); | |
+ this.interceptors = unmodifiableList(Arrays.asList(interceptors.clone())); | |
+ } | |
+ | |
+ public Object getSource() { | |
+ return source; | |
+ } | |
+ | |
+ public Matcher<? super Class<?>> getClassMatcher() { | |
+ return classMatcher; | |
+ } | |
+ | |
+ public List<ConstructorInterceptor> getInterceptors() { | |
+ return interceptors; | |
+ } | |
+ | |
+ public <T> T acceptVisitor(ElementVisitor<T> visitor) { | |
+ return visitor.visitConstructorInterceptorBinding(this); | |
+ } | |
+} | |
\ No newline at end of file | |
Index: src/com/google/inject/InjectorImpl.java | |
=================================================================== | |
--- src/com/google/inject/InjectorImpl.java (revision 627) | |
+++ src/com/google/inject/InjectorImpl.java Tue Oct 07 15:56:41 BST 2008 | |
@@ -16,6 +16,7 @@ | |
package com.google.inject; | |
+import com.google.common.base.Objects; | |
import static com.google.common.base.Preconditions.checkState; | |
import com.google.common.collect.ImmutableList; | |
import com.google.common.collect.ImmutableSet; | |
@@ -23,17 +24,28 @@ | |
import com.google.common.collect.Maps; | |
import com.google.common.collect.Multimap; | |
import com.google.common.collect.Multimaps; | |
+import com.google.common.collect.Sets; | |
import com.google.inject.internal.Annotations; | |
import com.google.inject.internal.BytecodeGen.Visibility; | |
import static com.google.inject.internal.BytecodeGen.newFastClass; | |
import com.google.inject.internal.Classes; | |
+import com.google.inject.internal.CloseErrorsImpl; | |
+import com.google.inject.internal.CloseableProvider; | |
+import com.google.inject.internal.CompositeCloser; | |
import com.google.inject.internal.ConfigurationException; | |
import com.google.inject.internal.Errors; | |
import com.google.inject.internal.ErrorsException; | |
import com.google.inject.internal.FailableCache; | |
import com.google.inject.internal.MatcherAndConverter; | |
import com.google.inject.internal.ToStringBuilder; | |
+import com.google.inject.matcher.Matcher; | |
+import com.google.inject.spi.InjectionAnnotation; | |
+import com.google.inject.spi.AnnotationProviderFactory; | |
import com.google.inject.spi.BindingTargetVisitor; | |
+import com.google.inject.spi.CloseErrors; | |
+import com.google.inject.spi.CloseFailedException; | |
+import com.google.inject.spi.Closeable; | |
+import com.google.inject.spi.Closer; | |
import com.google.inject.spi.Dependency; | |
import com.google.inject.spi.InjectionPoint; | |
import com.google.inject.util.Providers; | |
@@ -50,6 +62,8 @@ | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.Map; | |
+import java.util.Map.Entry; | |
+import java.util.Set; | |
import net.sf.cglib.reflect.FastClass; | |
import net.sf.cglib.reflect.FastMethod; | |
@@ -627,7 +641,7 @@ | |
throws ErrorsException { | |
List<InjectionPoint> injectionPoints = Lists.newArrayList(); | |
try { | |
- InjectionPoint.addForInstanceMethodsAndFields(type, injectionPoints); | |
+ InjectionPoint.addForInstanceMethodsAndFields(annotationProviderFactories(), type, injectionPoints); | |
} catch (ConfigurationException e) { | |
errors.merge(e.getErrorMessages()); | |
} | |
@@ -680,6 +694,18 @@ | |
} | |
} | |
+ InternalFactory<?> createCustomInternalFactory(final AnnotatedElement annotatedElement, final AnnotationProviderFactory<?> customFactory) { | |
+ return new InternalFactoryToProviderAdapter(new Provider() { | |
+ public Object get() { | |
+ Provider<?> provider = customFactory.createProvider(annotatedElement); | |
+ return provider.get(); | |
+ } | |
+ @Override public String toString() { | |
+ return customFactory.toString(); | |
+ } | |
+ }); | |
+ } | |
+ | |
class SingleFieldInjector implements SingleMemberInjector { | |
final Field field; | |
final InternalFactory<?> factory; | |
@@ -694,8 +720,15 @@ | |
// Ewwwww... | |
field.setAccessible(true); | |
+ | |
+ final AnnotationProviderFactory<?> customFactory = injectionPoint.getAnnotationProviderFactory(); | |
+ if (customFactory != null) { | |
+ factory = createCustomInternalFactory(field, customFactory); | |
+ } | |
+ else { | |
- factory = injector.getInternalFactory(dependency.getKey(), errors.withSource(dependency)); | |
- } | |
+ factory = injector.getInternalFactory(dependency.getKey(), errors.withSource(dependency)); | |
+ } | |
+ } | |
public InjectionPoint getInjectionPoint() { | |
return injectionPoint; | |
@@ -746,17 +779,25 @@ | |
SingleParameterInjector<?>[] parameterInjectors | |
= new SingleParameterInjector<?>[parameters.size()]; | |
int index = 0; | |
+ AnnotationProviderFactory<?> customFactory = injectionPoint.getAnnotationProviderFactory(); | |
+ if (parameters.size() == 1 && customFactory != null) { | |
+ InternalFactory<?> internalFactory = createCustomInternalFactory( | |
+ (AnnotatedElement) injectionPoint.getMember(), customFactory); | |
+ parameterInjectors[0] = new SingleParameterInjector(injectionPoint.getDependencies().get(0), internalFactory); | |
+ } | |
+ else { | |
- for (Dependency<?> parameter : parameters) { | |
- errors.pushSource(parameter); | |
- try { | |
- parameterInjectors[index] = createParameterInjector(parameter, errors); | |
- } catch (ErrorsException rethrownBelow) { | |
- // rethrown below | |
- } finally { | |
- errors.popSource(parameter); | |
- } | |
- index++; | |
- } | |
+ for (Dependency<?> parameter : parameters) { | |
+ errors.pushSource(parameter); | |
+ try { | |
+ parameterInjectors[index] = createParameterInjector(parameter, errors); | |
+ } catch (ErrorsException rethrownBelow) { | |
+ // rethrown below | |
+ } finally { | |
+ errors.popSource(parameter); | |
+ } | |
+ index++; | |
+ } | |
+ } | |
errors.throwIfNecessary(); | |
return parameterInjectors; | |
@@ -924,7 +965,7 @@ | |
<T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors) throws ErrorsException { | |
final InternalFactory<? extends T> factory = getInternalFactory(key, errors); | |
- return new Provider<T>() { | |
+ return new CloseableProvider<T>() { | |
public T get() { | |
final Errors errors = new Errors(); | |
try { | |
@@ -949,6 +990,13 @@ | |
} | |
} | |
+ public void close(Closer closer, CloseErrors errors) { | |
+ if (factory instanceof Closeable) { | |
+ Closeable closeable = (Closeable) factory; | |
+ closeable.close(closer, errors); | |
+ } | |
+ } | |
+ | |
@Override public String toString() { | |
return factory.toString(); | |
} | |
@@ -1010,4 +1058,109 @@ | |
.add("bindings", explicitBindings) | |
.toString(); | |
} | |
+ | |
+ /** Iterates through all bindings closing any {@link Closeable} providers which have pre destroy hooks */ | |
+ public void close() throws CloseFailedException { | |
+ Set<Closer> closers = getInstancesOf(Closer.class); | |
+ if (closers.isEmpty()) { | |
+ return; | |
-} | |
+ } | |
+ Closer closer = new CompositeCloser(closers); | |
+ CloseErrorsImpl errors = new CloseErrorsImpl(this); | |
+ | |
+ Set<Entry<Key<?>,Binding<?>>> entries = getBindings().entrySet(); | |
+ for (Entry<Key<?>, Binding<?>> entry : entries) { | |
+ Binding<?> binding = entry.getValue(); | |
+ Provider<?> provider = binding.getProvider(); | |
+ if (provider instanceof Closeable) { | |
+ Closeable closeable = (Closeable) provider; | |
+ closeable.close(closer, errors); | |
+ } | |
+ } | |
+ | |
+ errors.throwIfNecessary(); | |
+ } | |
+ | |
+ /** | |
+ * Returns a collection of all instances of the given base type | |
+ * @param baseClass the base type of objects required | |
+ * @param <T> the base type | |
+ * @return a set of objects returned from this injector | |
+ */ | |
+ public <T> Set<T> getInstancesOf(Class<T> baseClass) { | |
+ Set<T> answer = Sets.newHashSet(); | |
+ Set<Entry<Key<?>, Binding<?>>> entries = getBindings().entrySet(); | |
+ for (Entry<Key<?>, Binding<?>> entry : entries) { | |
+ Key<?> key = entry.getKey(); | |
+ if (baseClass.isAssignableFrom(key.getRawType())) { | |
+ Object value = getInstance(key); | |
+ if (value != null) { | |
+ T castValue = baseClass.cast(value); | |
+ answer.add(castValue); | |
+ } | |
+ } | |
+ } | |
+ return answer; | |
+ } | |
+ | |
+ /** | |
+ * Returns a collection of all instances matching the given matcher | |
+ * @param matcher matches the types to return instances | |
+ * @return a set of objects returned from this injector | |
+ */ | |
+ public <T> Set<T> getInstancesOf(Matcher<Class<T>> matcher) { | |
+ Set<T> answer = Sets.newHashSet(); | |
+ Set<Entry<Key<?>, Binding<?>>> entries = getBindings().entrySet(); | |
+ for (Entry<Key<?>, Binding<?>> entry : entries) { | |
+ Key<?> key = entry.getKey(); | |
+ if (matcher.matches((Class<T>) key.getRawType())) { | |
+ Object value = getInstance(key); | |
+ answer.add((T) value); | |
+ } | |
+ } | |
+ return answer; | |
+ } | |
+ | |
+ | |
+ /** | |
+ * Finds all the instances of the {@link com.google.inject.spi.AnnotationProviderFactory} instances | |
+ * which are registered to provide custom annotation driven injection points | |
+ */ | |
+ public Map<Class<? extends Annotation>, AnnotationProviderFactory> annotationProviderFactories() { | |
+ Map<Class<? extends Annotation>, AnnotationProviderFactory> answer = Maps.newHashMap(); | |
+ Set<Entry<Key<?>, Binding<?>>> entries = getBindings().entrySet(); | |
+ for (Entry<Key<?>, Binding<?>> entry : entries) { | |
+ final Key<?> key = entry.getKey(); | |
+ final Class<?> type = key.getRawType(); | |
+ if (AnnotationProviderFactory.class.isAssignableFrom(type)) { | |
+ InjectionAnnotation injectionAnnotation = type.getAnnotation(InjectionAnnotation.class); | |
+ if (injectionAnnotation != null) { | |
+ Class<? extends Annotation> annotationType = injectionAnnotation.value(); | |
+ | |
+ // let avoid injecting anything yet | |
+ AnnotationProviderFactory factory = new AnnotationProviderFactory() { | |
+ public Class getAnnotationType() { | |
+ return type; | |
+ } | |
+ | |
+ public Provider createProvider(final AnnotatedElement member) { | |
+ return new Provider() { | |
+ public Object get() { | |
+ AnnotationProviderFactory factory = (AnnotationProviderFactory) getInstance(key); | |
+ Objects.nonNull(factory, "Should have an instance for key " + key); | |
+ return factory.createProvider(member).get(); | |
+ } | |
+ | |
+ @Override public String toString() { | |
+ return "AnnotationProvider:" + key + " using " + type.getName(); | |
+ } | |
+ }; | |
+ } | |
+ }; | |
+ answer.put(annotationType, factory); | |
+ } | |
+ } | |
+ } | |
+ return answer; | |
+ } | |
+} | |
Index: src/com/google/inject/internal/Errors.java | |
=================================================================== | |
--- src/com/google/inject/internal/Errors.java (revision 627) | |
+++ src/com/google/inject/internal/Errors.java Mon Oct 06 12:03:18 BST 2008 | |
@@ -322,7 +322,7 @@ | |
return addMessage(null, messageFormat, arguments); | |
} | |
- private Errors addMessage(Throwable cause, String messageFormat, Object... arguments) { | |
+ public Errors addMessage(Throwable cause, String messageFormat, Object... arguments) { | |
String message = format(messageFormat, arguments); | |
addMessage(new Message(stripDuplicates(sources), message, cause)); | |
return this; | |
Index: src/com/google/inject/internal/CompositeCloser.java | |
=================================================================== | |
--- src/com/google/inject/internal/CompositeCloser.java Mon Oct 06 12:31:40 BST 2008 | |
+++ src/com/google/inject/internal/CompositeCloser.java Mon Oct 06 12:31:40 BST 2008 | |
@@ -0,0 +1,41 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.internal; | |
+ | |
+import com.google.inject.spi.Closer; | |
+ | |
+/** | |
+ * A Composite implementation of {@link Closer} | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ */ | |
+public class CompositeCloser implements Closer { | |
+ private final Iterable<Closer> closers; | |
+ | |
+ public CompositeCloser(Iterable<Closer> closers) { | |
+ this.closers = closers; | |
+ } | |
+ | |
+ public void close(Object object) throws Throwable { | |
+ for (Closer closer : closers) { | |
+ closer.close(object); | |
+ } | |
+ } | |
+} | |
Index: src/com/google/inject/internal/ModuleBinding.java | |
=================================================================== | |
--- src/com/google/inject/internal/ModuleBinding.java (revision 627) | |
+++ src/com/google/inject/internal/ModuleBinding.java Tue Oct 07 14:32:25 BST 2008 | |
@@ -38,6 +38,7 @@ | |
import com.google.inject.spi.Message; | |
import com.google.inject.util.Providers; | |
import java.lang.annotation.Annotation; | |
+import java.util.Collections; | |
import java.util.List; | |
import java.util.Set; | |
@@ -206,7 +207,8 @@ | |
// lookup the injection points, adding any errors to the binder's errors list | |
List<InjectionPoint> injectionPointsList = Lists.newArrayList(); | |
try { | |
- InjectionPoint.addForInstanceMethodsAndFields(instance.getClass(), injectionPointsList); | |
+ // TODO need the map? | |
+ InjectionPoint.addForInstanceMethodsAndFields(Collections.EMPTY_MAP, instance.getClass(), injectionPointsList); | |
} catch (ConfigurationException e) { | |
for (Message message : e.getErrorMessages()) { | |
binder.addError(message); | |
@@ -224,7 +226,8 @@ | |
// lookup the injection points, adding any errors to the binder's errors list | |
List<InjectionPoint> injectionPointsList = Lists.newArrayList(); | |
try { | |
- InjectionPoint.addForInstanceMethodsAndFields(provider.getClass(), injectionPointsList); | |
+ // TODO need the map? | |
+ InjectionPoint.addForInstanceMethodsAndFields(Collections.EMPTY_MAP, provider.getClass(), injectionPointsList); | |
} catch (ConfigurationException e) { | |
for (Message message : e.getErrorMessages()) { | |
binder.addError(message); | |
Index: src/com/google/inject/ProviderToInternalFactoryAdapter.java | |
=================================================================== | |
--- src/com/google/inject/ProviderToInternalFactoryAdapter.java (revision 627) | |
+++ src/com/google/inject/ProviderToInternalFactoryAdapter.java Mon Oct 06 12:50:42 BST 2008 | |
@@ -18,12 +18,15 @@ | |
import com.google.inject.internal.Errors; | |
import com.google.inject.internal.ErrorsException; | |
+import com.google.inject.spi.Closeable; | |
import com.google.inject.spi.Dependency; | |
+import com.google.inject.spi.Closer; | |
+import com.google.inject.spi.CloseErrors; | |
/** | |
* @author crazybob@google.com (Bob Lee) | |
*/ | |
-class ProviderToInternalFactoryAdapter<T> implements Provider<T> { | |
+class ProviderToInternalFactoryAdapter<T> implements Provider<T>, Closeable { | |
private final InjectorImpl injector; | |
private final InternalFactory<? extends T> internalFactory; | |
@@ -50,6 +53,13 @@ | |
} | |
} | |
+ public void close(Closer closer, CloseErrors errors) { | |
+ if (internalFactory instanceof Closeable) { | |
+ Closeable closeable = (Closeable) internalFactory; | |
+ closeable.close(closer, errors); | |
+ } | |
+ } | |
+ | |
@Override public String toString() { | |
return internalFactory.toString(); | |
} | |
Index: src/com/google/inject/spi/CloseFailedException.java | |
=================================================================== | |
--- src/com/google/inject/spi/CloseFailedException.java Mon Oct 06 12:03:18 BST 2008 | |
+++ src/com/google/inject/spi/CloseFailedException.java Mon Oct 06 12:03:18 BST 2008 | |
@@ -0,0 +1,39 @@ | |
+/** | |
+ * Copyright (C) 2008 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+import com.google.inject.internal.Errors; | |
+import java.io.IOException; | |
+import java.util.List; | |
+ | |
+/** | |
+ * Indicates that an attempt to close an injector or scope failed closing one or more bindings. | |
+ * | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ */ | |
+public class CloseFailedException extends IOException { | |
+ private final List<Message> messages; | |
+ | |
+ public CloseFailedException(List<Message> messages) { | |
+ super(Errors.format("Close errors", messages)); | |
+ this.messages = messages; | |
+ } | |
+ | |
+ public List<Message> getMessages() { | |
+ return messages; | |
+ } | |
+} | |
\ No newline at end of file | |
Index: jsr250/src/com/google/inject/jsr250/AnnotatedMethodCache.java | |
=================================================================== | |
--- jsr250/src/com/google/inject/jsr250/AnnotatedMethodCache.java Mon Oct 06 14:03:00 BST 2008 | |
+++ jsr250/src/com/google/inject/jsr250/AnnotatedMethodCache.java Mon Oct 06 14:03:00 BST 2008 | |
@@ -0,0 +1,78 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jsr250; | |
+ | |
+import com.sun.tools.corba.se.idl.InvalidArgument; | |
+import java.lang.annotation.Annotation; | |
+import java.lang.reflect.Method; | |
+import java.util.Collections; | |
+import java.util.Map; | |
+import java.util.WeakHashMap; | |
+ | |
+/** | |
+ * A cache which maintains which method is annotated by a given annotation for each class | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+class AnnotatedMethodCache { | |
+ private final Class<? extends Annotation> annotationType; | |
+ private Map<Class<?>, Method> methodCache = Collections | |
+ .synchronizedMap(new WeakHashMap<Class<?>, Method>()); | |
+ | |
+ public AnnotatedMethodCache(Class<? extends Annotation> annotationType) { | |
+ this.annotationType = annotationType; | |
+ } | |
+ | |
+ /** | |
+ * Looks up the method which is annotated for the given type | |
+ */ | |
+ public Method getMethod(Class<?> type) throws InvalidArgument { | |
+ // if we are invoked concurrently it doesn't matter if we look up the method | |
+ // concurrently - its the same instance that will be overwritten in the map | |
+ Method method = methodCache.get(type); | |
+ if (method == null) { | |
+ method = findMethodWithAnnotation(type, annotationType); | |
+ if (method != null) { | |
+ if (method.getParameterTypes().length != 0) { | |
+ throw new InvalidArgument("Method should have no arguments for @PostConstruct " + method); | |
+ } | |
+ methodCache.put(type, method); | |
+ } | |
+ } | |
+ return method; | |
+ } | |
+ | |
+ protected Method findMethodWithAnnotation(Class<?> type, | |
+ Class<? extends Annotation> annotationType) { | |
+ Method[] methods = type.getDeclaredMethods(); | |
+ for (Method method : methods) { | |
+ Annotation fromElement = method.getAnnotation(annotationType); | |
+ if (fromElement != null) { | |
+ return method; | |
+ } | |
+ } | |
+ if (!Object.class.equals(type)) { | |
+ Class superclass = type.getSuperclass(); | |
+ if (superclass != null) { | |
+ return findMethodWithAnnotation(superclass, annotationType); | |
+ } | |
+ } | |
+ return null; | |
+ } | |
+} | |
Index: src/com/google/inject/InterceptorBindingProcessor.java | |
=================================================================== | |
--- src/com/google/inject/InterceptorBindingProcessor.java (revision 627) | |
+++ src/com/google/inject/InterceptorBindingProcessor.java Fri Oct 03 16:24:04 BST 2008 | |
@@ -18,6 +18,7 @@ | |
import com.google.inject.internal.Errors; | |
import com.google.inject.spi.InterceptorBinding; | |
+import com.google.inject.spi.ConstructorInterceptorBinding; | |
/** | |
* Handles {@link Binder#bindInterceptor} commands. | |
@@ -40,6 +41,12 @@ | |
return true; | |
} | |
+ @Override public Boolean visitConstructorInterceptorBinding(ConstructorInterceptorBinding command) { | |
+ proxyFactoryBuilder.constructorIntercept( | |
+ command.getClassMatcher(), command.getInterceptors()); | |
+ return true; | |
+ } | |
+ | |
ProxyFactory createProxyFactory() { | |
return proxyFactoryBuilder.create(); | |
} | |
Index: src/com/google/inject/spi/Closers.java | |
=================================================================== | |
--- src/com/google/inject/spi/Closers.java Mon Oct 06 13:42:22 BST 2008 | |
+++ src/com/google/inject/spi/Closers.java Mon Oct 06 13:42:22 BST 2008 | |
@@ -0,0 +1,50 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+/** | |
+ * Some helper methods for working with the {@link Closer} interface | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public final class Closers { | |
+ | |
+ /** | |
+ * Closes the given object with the Closer if the object is not null | |
+ * | |
+ * @param key the Key or String name of the object to be closed | |
+ * @param objectToBeClosed the object that is going to be closed | |
+ * @param closer the strategy used to close the object | |
+ * @param errors the handler of exceptions if they occur | |
+ */ | |
+ public static void close(Object key, Object objectToBeClosed, Closer closer, CloseErrors errors) { | |
+ if (objectToBeClosed != null) { | |
+ try { | |
+ closer.close(objectToBeClosed); | |
+ } | |
+ catch (Exception e) { | |
+ errors.closeError(key, objectToBeClosed, e); | |
+ } | |
+ catch (Throwable throwable) { | |
+ throwable | |
+ .printStackTrace(); //To change body of catch statement use File | Settings | File Templates. | |
+ } | |
+ } | |
+ } | |
+} | |
Index: servlet/src/com/google/inject/servlet/ServletScopes.java | |
=================================================================== | |
--- servlet/src/com/google/inject/servlet/ServletScopes.java (revision 627) | |
+++ servlet/src/com/google/inject/servlet/ServletScopes.java Mon Oct 06 13:27:25 BST 2008 | |
@@ -19,6 +19,11 @@ | |
import com.google.inject.Key; | |
import com.google.inject.Provider; | |
import com.google.inject.Scope; | |
+import com.google.inject.internal.CloseableProvider; | |
+import com.google.inject.spi.CloseErrors; | |
+import com.google.inject.spi.Closer; | |
+import com.google.inject.spi.Closers; | |
+import java.util.Enumeration; | |
import javax.servlet.http.HttpServletRequest; | |
import javax.servlet.http.HttpSession; | |
@@ -37,7 +42,7 @@ | |
public static final Scope REQUEST = new Scope() { | |
public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { | |
final String name = key.toString(); | |
- return new Provider<T>() { | |
+ return new CloseableProvider<T>() { | |
public T get() { | |
HttpServletRequest request = GuiceFilter.getRequest(); | |
synchronized (request) { | |
@@ -51,6 +56,18 @@ | |
} | |
} | |
+ public void close(Closer closer, CloseErrors errors) { | |
+ HttpServletRequest request = GuiceFilter.getRequest(); | |
+ synchronized (request) { | |
+ @SuppressWarnings("unchecked") Enumeration names = request.getAttributeNames(); | |
+ while (names.hasMoreElements()) { | |
+ String name = (String) names.nextElement(); | |
+ Object value = request.getAttribute(name); | |
+ Closers.close(name, value, closer, errors); | |
+ } | |
+ } | |
+ } | |
+ | |
public String toString() { | |
return String.format("%s[%s]", creator, REQUEST); | |
} | |
@@ -68,7 +85,7 @@ | |
public static final Scope SESSION = new Scope() { | |
public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { | |
final String name = key.toString(); | |
- return new Provider<T>() { | |
+ return new CloseableProvider<T>() { | |
public T get() { | |
HttpSession session = GuiceFilter.getRequest().getSession(); | |
synchronized (session) { | |
@@ -81,6 +98,19 @@ | |
return t; | |
} | |
} | |
+ | |
+ public void close(Closer closer, CloseErrors errors) { | |
+ HttpSession session = GuiceFilter.getRequest().getSession(); | |
+ synchronized (session) { | |
+ @SuppressWarnings("unchecked") Enumeration names = session.getAttributeNames(); | |
+ while (names.hasMoreElements()) { | |
+ String name = (String) names.nextElement(); | |
+ Object value = session.getAttribute(name); | |
+ Closers.close(name, value, closer, errors); | |
+ } | |
+ } | |
+ } | |
+ | |
public String toString() { | |
return String.format("%s[%s]", creator, SESSION); | |
} | |
Index: spring/src/com/google/inject/spring/DisposableBeanCloser.java | |
=================================================================== | |
--- spring/src/com/google/inject/spring/DisposableBeanCloser.java Mon Oct 06 13:51:06 BST 2008 | |
+++ spring/src/com/google/inject/spring/DisposableBeanCloser.java Mon Oct 06 13:51:06 BST 2008 | |
@@ -0,0 +1,42 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spring; | |
+ | |
+import com.google.inject.spi.Closer; | |
+import org.springframework.beans.factory.DisposableBean; | |
+ | |
+/** | |
+ * Invokes the {@link org.springframework.beans.factory.DisposableBean#destroy()} on any beans | |
+ * which are registered in a singleton scope when the injector is closed. | |
+ * <p> | |
+ * To install this lifecycle call the {@link SpringIntegration#bindLifecycle(com.google.inject.Binder)} method | |
+ * from your module. | |
+ * | |
+ * @see com.google.inject.Injector#close() | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public class DisposableBeanCloser implements Closer { | |
+ public void close(Object object) throws Throwable { | |
+ if (object instanceof DisposableBean) { | |
+ DisposableBean disposableBean = (DisposableBean) object; | |
+ disposableBean.destroy(); | |
+ } | |
+ } | |
+} | |
Index: src/com/google/inject/Scopes.java | |
=================================================================== | |
--- src/com/google/inject/Scopes.java (revision 627) | |
+++ src/com/google/inject/Scopes.java Mon Oct 06 13:28:31 BST 2008 | |
@@ -16,6 +16,11 @@ | |
package com.google.inject; | |
+import com.google.inject.internal.CloseableProvider; | |
+import com.google.inject.spi.CloseErrors; | |
+import com.google.inject.spi.Closer; | |
+import com.google.inject.spi.Closers; | |
+ | |
/** | |
* Built in scope implementations. | |
* | |
@@ -29,8 +34,8 @@ | |
* One instance per {@link Injector}. Also see {@code @}{@link Singleton}. | |
*/ | |
public static final Scope SINGLETON = new Scope() { | |
- public <T> Provider<T> scope(Key<T> key, final Provider<T> creator) { | |
- return new Provider<T>() { | |
+ public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) { | |
+ return new CloseableProvider<T>() { | |
private volatile T instance; | |
@@ -53,6 +58,13 @@ | |
return instance; | |
} | |
+ public void close(Closer closer, CloseErrors errors) { | |
+ synchronized (Injector.class) { | |
+ Closers.close(key, instance, closer, errors); | |
+ instance = null; | |
+ } | |
+ } | |
+ | |
public String toString() { | |
return String.format("%s[%s]", creator, SINGLETON); | |
} | |
Index: src/com/google/inject/internal/CloseErrorsImpl.java | |
=================================================================== | |
--- src/com/google/inject/internal/CloseErrorsImpl.java Mon Oct 06 13:42:22 BST 2008 | |
+++ src/com/google/inject/internal/CloseErrorsImpl.java Mon Oct 06 13:42:22 BST 2008 | |
@@ -0,0 +1,47 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.internal; | |
+ | |
+import com.google.inject.spi.CloseErrors; | |
+import com.google.inject.spi.CloseFailedException; | |
+ | |
+/** | |
+ * The default implementation of @{link CloseErrors} | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public class CloseErrorsImpl implements CloseErrors { | |
+ final Errors errors; | |
+ | |
+ public CloseErrorsImpl(Object source) { | |
+ this.errors = new Errors(source); | |
+ } | |
+ | |
+ public void closeError(Object key, Object object, Exception cause) { | |
+ errors.addMessage(cause, "Failed to close object %s with key %s", object, key); | |
+ } | |
+ | |
+ public void throwIfNecessary() throws CloseFailedException { | |
+ if (!errors.hasErrors()) { | |
+ return; | |
+ } | |
+ | |
+ throw new CloseFailedException(errors.getMessages()); | |
+ } | |
+} | |
Index: src/com/google/inject/internal/CloseableProvider.java | |
=================================================================== | |
--- src/com/google/inject/internal/CloseableProvider.java Mon Oct 06 13:42:22 BST 2008 | |
+++ src/com/google/inject/internal/CloseableProvider.java Mon Oct 06 13:42:22 BST 2008 | |
@@ -0,0 +1,31 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.internal; | |
+ | |
+import com.google.inject.Provider; | |
+import com.google.inject.spi.Closeable; | |
+ | |
+/** | |
+ * A simple interface which makes it a little easier to create anonymous classes which | |
+ * are both a {@link Provider} and implement {@link com.google.inject.spi.Closeable} | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface CloseableProvider<T> extends Provider<T>, Closeable { | |
+} | |
Index: src/com/google/inject/ProxyFactoryBuilder.java | |
=================================================================== | |
--- src/com/google/inject/ProxyFactoryBuilder.java (revision 627) | |
+++ src/com/google/inject/ProxyFactoryBuilder.java Fri Oct 03 16:24:04 BST 2008 | |
@@ -22,6 +22,7 @@ | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
import org.aopalliance.intercept.MethodInterceptor; | |
/** | |
@@ -32,6 +33,7 @@ | |
class ProxyFactoryBuilder { | |
final List<MethodAspect> methodAspects = Lists.newArrayList(); | |
+ final List<ConstructorAspect> constructorAspects = Lists.newArrayList(); | |
/** | |
* Applies the given method interceptor to the methods matched by the class and method matchers. | |
@@ -55,8 +57,26 @@ | |
return intercept(classMatcher, methodMatcher, Arrays.asList(interceptors)); | |
} | |
+ /** | |
+ * Applies the given constructor interceptor to the methods matched by the class and method matchers. | |
+ * | |
+ * @param classMatcher matches classes the interceptor should apply to. For example: {@code | |
+ * only(Runnable.class)}. | |
+ * @param interceptors to apply | |
+ */ | |
+ public ProxyFactoryBuilder constructorIntercept(Matcher<? super Class<?>> classMatcher, | |
+ List<ConstructorInterceptor> interceptors) { | |
+ constructorAspects.add(new ConstructorAspect(classMatcher, interceptors)); | |
+ return this; | |
+ } | |
+ | |
+ public ProxyFactoryBuilder constructorIntercept(Matcher<? super Class<?>> classMatcher, | |
+ ConstructorInterceptor... interceptors) { | |
+ return constructorIntercept(classMatcher, Arrays.asList(interceptors)); | |
+ } | |
+ | |
/** Creates a {@code ProxyFactory}. */ | |
public ProxyFactory create() { | |
- return new ProxyFactory(new ArrayList<MethodAspect>(methodAspects)); | |
+ return new ProxyFactory(new ArrayList<MethodAspect>(methodAspects), new ArrayList<ConstructorAspect>(constructorAspects)); | |
} | |
} | |
Index: jsr250/src/com/google/inject/jsr250/PreDestroyCloser.java | |
=================================================================== | |
--- jsr250/src/com/google/inject/jsr250/PreDestroyCloser.java Mon Oct 06 14:01:20 BST 2008 | |
+++ jsr250/src/com/google/inject/jsr250/PreDestroyCloser.java Mon Oct 06 14:01:20 BST 2008 | |
@@ -0,0 +1,51 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jsr250; | |
+ | |
+import com.google.inject.spi.Closer; | |
+import java.lang.reflect.InvocationTargetException; | |
+import java.lang.reflect.Method; | |
+import javax.annotation.PreDestroy; | |
+ | |
+/** | |
+ * Supports the {@link javax.annotation.PreDestroy} annotation lifecycle from JSR250. | |
+ * <p> | |
+ * To install this interceptor call the {@link Jsr250#bind(com.google.inject.Binder)} method | |
+ * from your module. | |
+ * | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public class PreDestroyCloser implements Closer { | |
+ | |
+ private AnnotatedMethodCache methodCache = new AnnotatedMethodCache(PreDestroy.class); | |
+ | |
+ public void close(Object object) throws Throwable { | |
+ Class<? extends Object> type = object.getClass(); | |
+ Method method = methodCache.getMethod(type); | |
+ if (method != null) { | |
+ if (method != null) { | |
+ try { | |
+ method.invoke(object); | |
+ } | |
+ catch (InvocationTargetException e) { | |
+ throw e.getTargetException(); | |
+ } | |
+ } | |
+ } | |
+ } | |
+} | |
\ No newline at end of file | |
Index: src/com/google/inject/InternalFactoryToProviderAdapter.java | |
=================================================================== | |
--- src/com/google/inject/InternalFactoryToProviderAdapter.java (revision 627) | |
+++ src/com/google/inject/InternalFactoryToProviderAdapter.java Mon Oct 06 12:50:42 BST 2008 | |
@@ -20,12 +20,15 @@ | |
import com.google.inject.internal.Errors; | |
import com.google.inject.internal.ErrorsException; | |
import com.google.inject.internal.SourceProvider; | |
+import com.google.inject.spi.Closeable; | |
import com.google.inject.spi.Dependency; | |
+import com.google.inject.spi.Closer; | |
+import com.google.inject.spi.CloseErrors; | |
/** | |
* @author crazybob@google.com (Bob Lee) | |
*/ | |
-class InternalFactoryToProviderAdapter<T> implements InternalFactory<T> { | |
+class InternalFactoryToProviderAdapter<T> implements InternalFactory<T>, Closeable { | |
private final Provider<? extends T> provider; | |
private final Object source; | |
@@ -52,6 +55,13 @@ | |
} | |
} | |
+ public void close(Closer closer, CloseErrors errors) { | |
+ if (provider instanceof Closeable) { | |
+ Closeable closeable = (Closeable) provider; | |
+ closeable.close(closer, errors); | |
+ } | |
+ } | |
+ | |
@Override public String toString() { | |
return provider.toString(); | |
} | |
Index: src/com/google/inject/Injector.java | |
=================================================================== | |
--- src/com/google/inject/Injector.java (revision 627) | |
+++ src/com/google/inject/Injector.java Mon Oct 06 13:46:17 BST 2008 | |
@@ -16,6 +16,7 @@ | |
package com.google.inject; | |
+import com.google.inject.spi.CloseFailedException; | |
import java.util.List; | |
import java.util.Map; | |
@@ -120,4 +121,13 @@ | |
* dependencies ahead of time. | |
*/ | |
<T> T getInstance(Class<T> type); | |
+ | |
+ /** | |
+ * Closes down any singleton resources in this injector by applying any bound | |
+ * {@link com.google.inject.spi.Closer} strategy implementations to the singleton objects. | |
+ * | |
+ * Typically all objects that can be will be closed and a single exception will be thrown | |
+ * indicating all of the exceptions that occurred while closing all of the resources. | |
+ */ | |
+ void close() throws CloseFailedException; | |
} | |
Index: src/com/google/inject/spi/InjectionAnnotation.java | |
=================================================================== | |
--- src/com/google/inject/spi/InjectionAnnotation.java Tue Oct 07 15:56:09 BST 2008 | |
+++ src/com/google/inject/spi/InjectionAnnotation.java Tue Oct 07 15:56:09 BST 2008 | |
@@ -0,0 +1,36 @@ | |
+/** | |
+ * Copyright (C) 2008 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+import java.lang.annotation.Annotation; | |
+import java.lang.annotation.Documented; | |
+import java.lang.annotation.ElementType; | |
+import java.lang.annotation.Retention; | |
+import static java.lang.annotation.RetentionPolicy.RUNTIME; | |
+import java.lang.annotation.Target; | |
+ | |
+/** | |
+ * Marks a class with the annotation used as the custom injection point | |
+ * | |
+ * @version $Revision: 1.1 $ */ | |
+@Target({ ElementType.TYPE}) | |
+@Retention(RUNTIME) | |
+@Documented | |
+public @interface InjectionAnnotation { | |
+ Class<? extends Annotation> value(); | |
+} | |
Index: src/com/google/inject/CreationTimeMemberInjector.java | |
=================================================================== | |
--- src/com/google/inject/CreationTimeMemberInjector.java (revision 627) | |
+++ src/com/google/inject/CreationTimeMemberInjector.java Tue Oct 07 14:25:03 BST 2008 | |
@@ -20,7 +20,9 @@ | |
import com.google.common.collect.Maps; | |
import com.google.inject.internal.Errors; | |
import com.google.inject.internal.ErrorsException; | |
+import com.google.inject.spi.AnnotationProviderFactory; | |
import com.google.inject.spi.InjectionPoint; | |
+import java.lang.annotation.Annotation; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.Set; | |
@@ -60,6 +62,14 @@ | |
} | |
/** | |
+ * Finds all the instances of the {@link com.google.inject.spi.AnnotationProviderFactory} instances | |
+ * which are registered to provide custom annotation driven injection points | |
+ */ | |
+ public Map<Class<? extends Annotation>, AnnotationProviderFactory> annotationProviderFactories() { | |
+ return injector.annotationProviderFactories(); | |
+ } | |
+ | |
+ /** | |
* Prepares member injectors for all injected instances. This prompts Guice to do static analysis | |
* on the injected instances. | |
*/ | |
Index: src/com/google/inject/spi/AnnotationProviderFactory.java | |
=================================================================== | |
--- src/com/google/inject/spi/AnnotationProviderFactory.java Tue Oct 07 15:56:41 BST 2008 | |
+++ src/com/google/inject/spi/AnnotationProviderFactory.java Tue Oct 07 15:56:41 BST 2008 | |
@@ -0,0 +1,42 @@ | |
+/** | |
+ * Copyright (C) 2008 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+import com.google.inject.Provider; | |
+import java.lang.reflect.AnnotatedElement; | |
+ | |
+/** | |
+ * A hook to allow frameworks to register an annotation based injection point as an alternative | |
+ * injection point to {@link com.google.inject.Inject} such as using @Resource for JSR 250 | |
+ * or @PersistenceContext for JPA. | |
+ * <p> | |
+ * When creating classes implementing this interface you should annotate it with | |
+ * {@link InjectionAnnotation} to mark the annotation which is used to define the injection point. | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface AnnotationProviderFactory<T> { | |
+ | |
+ /** | |
+ * Creates the provider for the injected values at the given member | |
+ * | |
+ * @param member the member to inject | |
+ * @return the value to inject into this injection point | |
+ */ | |
+ Provider<T> createProvider(AnnotatedElement member); | |
+ | |
+} | |
Index: test/com/google/inject/CloseTest.java | |
=================================================================== | |
--- test/com/google/inject/CloseTest.java Mon Oct 06 13:37:11 BST 2008 | |
+++ test/com/google/inject/CloseTest.java Mon Oct 06 13:37:11 BST 2008 | |
@@ -0,0 +1,69 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject; | |
+ | |
+import com.google.inject.spi.Closer; | |
+import junit.framework.TestCase; | |
+ | |
+/** | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ */ | |
+public class CloseTest extends TestCase { | |
+ | |
+ public void testClosing() throws Exception { | |
+ final MyCloser counter = new MyCloser(); | |
+ | |
+ Injector injector = Guice.createInjector(new AbstractModule() { | |
+ protected void configure() { | |
+ bind(Foo.class); | |
+ bind(MyCloser.class).toInstance(counter); | |
+ } | |
+ }); | |
+ | |
+ Foo foo = injector.getInstance(Key.get(Foo.class)); | |
+ foo.foo(); | |
+ assertTrue(foo.invoked); | |
+ | |
+ foo = injector.getInstance(Foo.class); | |
+ foo.foo(); | |
+ assertTrue(foo.invoked); | |
+ | |
+ injector.close(); | |
+ | |
+ assertEquals(1, counter.count); | |
+ assertSame(foo, counter.closeObject); | |
+ } | |
+ | |
+ @Singleton | |
+ static class Foo { | |
+ boolean invoked; | |
+ public void foo() { | |
+ invoked = true; | |
+ } | |
+ } | |
+ | |
+ static class MyCloser implements Closer { | |
+ | |
+ int count; | |
+ Object closeObject; | |
+ | |
+ public void close(Object object) throws Throwable { | |
+ count++; | |
+ this.closeObject = object; | |
+ } | |
+ } | |
+} | |
\ No newline at end of file | |
Index: build.xml | |
=================================================================== | |
--- build.xml (revision 627) | |
+++ build.xml Sat Oct 04 10:36:05 BST 2008 | |
@@ -16,7 +16,8 @@ | |
<mkdir dir="${build.dir}/dist"/> | |
<jarjar jarfile="${build.dir}/dist/guice-${version}.jar" | |
manifest="${build.dir}/META-INF/MANIFEST.MF"> | |
- <fileset dir="${build.dir}/classes"/> | |
+ <fileset dir="${build.dir}/classes"/> | |
+ <fileset dir="${build.dir}/../extensions/multibindings/build/classes"/> | |
<zipfileset src="lib/build/cglib-2.2.jar"/> | |
<zipfileset src="lib/build/asm-3.1.jar"/> | |
<zipfileset src="lib/build/google-collect-snapshot-20080530.jar"/> | |
@@ -32,6 +33,7 @@ | |
<ant antfile="servlet/build.xml" target="distjars" inheritAll="false"/> | |
<ant antfile="spring/build.xml" target="distjars" inheritAll="false"/> | |
<ant antfile="struts2/plugin/build.xml" target="distjars" inheritAll="false"/> | |
+ <ant antfile="jsr250/build.xml" target="distjars" inheritAll="false"/> | |
<ant antfile="extensions/assistedinject/build.xml" target="distjars" inheritAll="false"/> | |
<ant antfile="extensions/throwingproviders/build.xml" target="distjars" inheritAll="false"/> | |
<ant antfile="extensions/multibindings/build.xml" target="distjars" inheritAll="false"/> | |
@@ -47,6 +49,9 @@ | |
<fileset dir="struts2/plugin/build" includes="*.jar"/> | |
</copy> | |
<copy toDir="${build.dir}/dist"> | |
+ <fileset dir="jsr250/build" includes="*.jar"/> | |
+ </copy> | |
+ <copy toDir="${build.dir}/dist"> | |
<fileset dir="extensions/assistedinject/build" includes="*.jar"/> | |
</copy> | |
<copy toDir="${build.dir}/dist"> | |
@@ -92,6 +97,7 @@ | |
<pathelement location="lib/build/servlet-api-2.5.jar"/> | |
<pathelement location="lib/build/easymock.jar"/> | |
<pathelement location="lib/build/google-collect-snapshot-20080530.jar"/> | |
+ <pathelement location="lib/build/jsr250-api-1.0.jar"/> | |
</classpath> | |
<arg value="com.google.inject.AllTests"/> | |
<syspropertyset> | |
@@ -112,6 +118,7 @@ | |
<pathelement location="${src.dir}"/> | |
<pathelement location="${servlet.src.dir}"/> | |
<pathelement location="${spring.src.dir}"/> | |
+ <pathelement location="${jsr250.src.dir}"/> | |
<pathelement location="${assistedinject.src.dir}"/> | |
<pathelement location="${throwingproviders.src.dir}"/> | |
<pathelement location="${multibindings.src.dir}"/> | |
@@ -132,6 +139,7 @@ | |
description="Remove generated files."> | |
<ant dir="servlet" antfile="build.xml" target="clean"/> | |
<ant dir="spring" antfile="build.xml" target="clean"/> | |
+ <ant dir="jsr250" antfile="build.xml" target="clean"/> | |
<ant dir="struts2/plugin" antfile="build.xml" target="clean"/> | |
<ant dir="extensions/assistedinject" antfile="build.xml" target="clean"/> | |
<ant dir="extensions/throwingproviders" antfile="build.xml" target="clean"/> | |
Index: src/com/google/inject/matcher/Matchers.java | |
=================================================================== | |
--- src/com/google/inject/matcher/Matchers.java (revision 627) | |
+++ src/com/google/inject/matcher/Matchers.java Fri Oct 03 17:44:22 BST 2008 | |
@@ -177,6 +177,84 @@ | |
} | |
/** | |
+ * Returns a matcher which matches classes which have a public method annotated | |
+ * with a given annotation. | |
+ */ | |
+ public static Matcher<AnnotatedElement> methodAnnotatedWith( | |
+ final Annotation annotation) { | |
+ return new MethodAnnotatedWith(annotation); | |
+ } | |
+ | |
+ /** | |
+ * Returns a matcher which matches classes which have a public method annotated | |
+ * with a given annotation. | |
+ */ | |
+ public static Matcher<AnnotatedElement> methodAnnotatedWith( | |
+ final Class<? extends Annotation> annotationType) { | |
+ return new MethodAnnotatedWith(annotationType); | |
+ } | |
+ | |
+ | |
+ private static class MethodAnnotatedWith extends AbstractMatcher<AnnotatedElement> | |
+ implements Serializable { | |
+ private final Class<? extends Annotation> annotationType; | |
+ | |
+ public MethodAnnotatedWith(Annotation annotation) { | |
+ checkNotNull(annotation, "annotation"); | |
+ this.annotationType = annotation.annotationType(); | |
+ checkForRuntimeRetention(annotationType); | |
+ } | |
+ | |
+ public MethodAnnotatedWith(Class<? extends Annotation> annotationType) { | |
+ checkNotNull(annotationType, "annotationType"); | |
+ this.annotationType = annotationType; | |
+ checkForRuntimeRetention(annotationType); | |
+ } | |
+ | |
+ public boolean matches(AnnotatedElement element) { | |
+ if (element instanceof Class) { | |
+ Class type = (Class) element; | |
+ if (matchesClass(type)) { | |
+ return true; | |
+ } | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ protected boolean matchesClass(Class type) { | |
+ Method[] methods = type.getDeclaredMethods(); | |
+ for (Method method : methods) { | |
+ Annotation fromElement = method.getAnnotation(annotationType); | |
+ if (fromElement != null) { | |
+ return true; | |
+ } | |
+ } | |
+ if (!Object.class.equals(type)) { | |
+ Class superclass = type.getSuperclass(); | |
+ if (superclass != null) { | |
+ return matchesClass(superclass); | |
+ } | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ @Override public boolean equals(Object other) { | |
+ return other instanceof MethodAnnotatedWith | |
+ && ((MethodAnnotatedWith) other).annotationType.equals(annotationType); | |
+ } | |
+ | |
+ @Override public int hashCode() { | |
+ return 37 * annotationType.hashCode(); | |
+ } | |
+ | |
+ @Override public String toString() { | |
+ return "methodAnnotatedWith(" + annotationType + ")"; | |
+ } | |
+ | |
+ private static final long serialVersionUID = 0; | |
+ } | |
+ | |
+ /** | |
* Returns a matcher which matches subclasses of the given type (as well as | |
* the given type). | |
*/ | |
Index: src/com/google/inject/ProxyFactory.java | |
=================================================================== | |
--- src/com/google/inject/ProxyFactory.java (revision 627) | |
+++ src/com/google/inject/ProxyFactory.java Fri Oct 03 16:43:32 BST 2008 | |
@@ -28,6 +28,7 @@ | |
import java.lang.reflect.Constructor; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
+import java.lang.reflect.AccessibleObject; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Map; | |
@@ -38,6 +39,8 @@ | |
import net.sf.cglib.reflect.FastClass; | |
import net.sf.cglib.reflect.FastConstructor; | |
import org.aopalliance.intercept.MethodInterceptor; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
+import org.aopalliance.intercept.ConstructorInvocation; | |
/** | |
* Proxies classes applying interceptors to methods as specified in | |
@@ -48,10 +51,12 @@ | |
class ProxyFactory implements ConstructionProxyFactory { | |
final List<MethodAspect> methodAspects; | |
+ final List<ConstructorAspect> constructorAspects; | |
final ConstructionProxyFactory defaultFactory; | |
- ProxyFactory(List<MethodAspect> methodAspects) { | |
+ ProxyFactory(List<MethodAspect> methodAspects, List<ConstructorAspect> constructorAspects) { | |
this.methodAspects = methodAspects; | |
+ this.constructorAspects = constructorAspects; | |
defaultFactory = new DefaultConstructionProxyFactory(); | |
} | |
@@ -83,7 +88,7 @@ | |
} | |
} | |
if (applicableAspects.isEmpty()) { | |
- return defaultFactory.get(errors, injectionPoint); | |
+ return wrapConstructorProxy(defaultFactory.get(errors, injectionPoint)); | |
} | |
// Get list of methods from cglib. | |
@@ -116,7 +121,7 @@ | |
} | |
if (!anyMatched) { | |
// not test-covered | |
- return defaultFactory.get(errors, injectionPoint); | |
+ return wrapConstructorProxy(defaultFactory.get(errors, injectionPoint)); | |
} | |
// Create callbacks. | |
@@ -150,7 +155,7 @@ | |
// Store callbacks. | |
Enhancer.registerStaticCallbacks(proxied, callbacks); | |
- return createConstructionProxy(proxied, injectionPoint); | |
+ return wrapConstructorProxy(createConstructionProxy(proxied, injectionPoint)); | |
} | |
/** | |
@@ -169,15 +174,76 @@ | |
public T newInstance(Object... arguments) throws InvocationTargetException { | |
return (T) fastConstructor.newInstance(arguments); | |
} | |
+ | |
public InjectionPoint getInjectionPoint() { | |
return injectionPoint; | |
} | |
+ | |
public Constructor<T> getConstructor() { | |
return standardConstructor; | |
} | |
}; | |
} | |
+ private <T> ConstructionProxy<T> wrapConstructorProxy(final ConstructionProxy<?> constructionProxy) { | |
+ if (constructorAspects.isEmpty()) { | |
+ return (ConstructionProxy<T>) constructionProxy; | |
+ } | |
+ else { | |
+ // lets wrap the construction proxy to process the interceptors | |
+ return new ConstructionProxy<T>() { | |
+ public T newInstance(final Object... arguments) throws InvocationTargetException { | |
+ @SuppressWarnings("unchecked") | |
+ T answer = (T) constructionProxy.newInstance(arguments); | |
+ for (ConstructorAspect aspect : constructorAspects) { | |
+ List<ConstructorInterceptor> interceptors = aspect.interceptors(); | |
+ for (ConstructorInterceptor interceptor : interceptors) { | |
+ final T currentValue = answer; | |
+ ConstructorInvocation invocation = new ConstructorInvocation() { | |
+ public Constructor getConstructor() { | |
+ return constructionProxy.getConstructor(); | |
+ } | |
+ | |
+ public Object[] getArguments() { | |
+ return arguments; | |
+ } | |
+ | |
+ public Object proceed() throws Throwable { | |
+ return currentValue; | |
+ } | |
+ | |
+ public Object getThis() { | |
+ // TODO | |
+ return currentValue; | |
+ } | |
+ | |
+ public AccessibleObject getStaticPart() { | |
+ // TODO | |
+ return null; | |
+ } | |
+ }; | |
+ try { | |
+ answer = (T) interceptor.construct(invocation); | |
+ } | |
+ catch (Throwable throwable) { | |
+ throw new InvocationTargetException(throwable); | |
+ } | |
+ } | |
+ } | |
+ return answer; | |
+ } | |
+ | |
+ public InjectionPoint getInjectionPoint() { | |
+ return constructionProxy.getInjectionPoint(); | |
+ } | |
+ | |
+ public Constructor<T> getConstructor() { | |
+ return (Constructor<T>) constructionProxy.getConstructor(); | |
+ } | |
+ }; | |
+ } | |
+ } | |
+ | |
static class MethodInterceptorsPair { | |
final Method method; | |
Index: src/com/google/inject/spi/Closer.java | |
=================================================================== | |
--- src/com/google/inject/spi/Closer.java Mon Oct 06 13:42:22 BST 2008 | |
+++ src/com/google/inject/spi/Closer.java Mon Oct 06 13:42:22 BST 2008 | |
@@ -0,0 +1,37 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+/** | |
+ * Represents a strategy for closing an object down such as | |
+ * using the @PreDestroy lifecycle from JSR 250, | |
+ * invoking {@link java.io.Closeable#close()} | |
+ * or using the DisposableBean interface from Spring. | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface Closer { | |
+ /** | |
+ * Closes the given object | |
+ * | |
+ * @param object the object to be closed | |
+ * @throws Exception if the close operation caused some exception to occur | |
+ */ | |
+ void close(Object object) throws Throwable; | |
+} | |
Index: jsr250/src/com/google/inject/jsr250/PostConstructInterceptor.java | |
=================================================================== | |
--- jsr250/src/com/google/inject/jsr250/PostConstructInterceptor.java Mon Oct 06 14:00:35 BST 2008 | |
+++ jsr250/src/com/google/inject/jsr250/PostConstructInterceptor.java Mon Oct 06 14:00:35 BST 2008 | |
@@ -0,0 +1,57 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jsr250; | |
+ | |
+import java.lang.reflect.Method; | |
+import java.lang.reflect.InvocationTargetException; | |
+import javax.annotation.PostConstruct; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
+import org.aopalliance.intercept.ConstructorInvocation; | |
+ | |
+/** | |
+ * Supports the {@link PostConstruct} annotation lifecycle from JSR250. | |
+ * <p> | |
+ * To install this interceptor call the {@link Jsr250#bind(com.google.inject.Binder)} method | |
+ * from your module. | |
+ * | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public class PostConstructInterceptor implements ConstructorInterceptor { | |
+ | |
+ private AnnotatedMethodCache methodCache = new AnnotatedMethodCache(PostConstruct.class); | |
+ | |
+ public Object construct(ConstructorInvocation invocation) throws Throwable { | |
+ Object value = invocation.proceed(); | |
+ if (value != null) { | |
+ Class<?> type = value.getClass(); | |
+ | |
+ Method method = methodCache.getMethod(type); | |
+ | |
+ if (method != null) { | |
+ try { | |
+ method.invoke(value); | |
+ } | |
+ catch (InvocationTargetException e) { | |
+ throw e.getTargetException(); | |
+ } | |
+ } | |
+ } | |
+ return value; | |
+ } | |
+ | |
+} | |
\ No newline at end of file | |
Index: jsr250/test/com/google/inject/jsr250/LifecycleTest.java | |
=================================================================== | |
--- jsr250/test/com/google/inject/jsr250/LifecycleTest.java Tue Oct 07 11:53:08 BST 2008 | |
+++ jsr250/test/com/google/inject/jsr250/LifecycleTest.java Tue Oct 07 11:53:08 BST 2008 | |
@@ -0,0 +1,82 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jsr250; | |
+ | |
+import com.google.inject.AbstractModule; | |
+import com.google.inject.CreationException; | |
+import com.google.inject.Guice; | |
+import com.google.inject.Inject; | |
+import com.google.inject.Injector; | |
+import com.google.inject.Singleton; | |
+import com.google.inject.spi.CloseFailedException; | |
+import javax.annotation.PostConstruct; | |
+import javax.annotation.PreDestroy; | |
+import junit.framework.TestCase; | |
+ | |
+/** @author james.strachan@gmail.com (James Strachan) */ | |
+public class LifecycleTest extends TestCase { | |
+ | |
+ public void testBeanInitialised() throws CreationException, CloseFailedException { | |
+ Injector injector = Guice.createInjector(new AbstractModule() { | |
+ protected void configure() { | |
+ Jsr250.bind(binder()); | |
+ | |
+ bind(MyBean.class).in(Singleton.class); | |
+ } | |
+ }); | |
+ | |
+ MyBean bean = injector.getInstance(MyBean.class); | |
+ assertNotNull("Should have instantiated the bean", bean); | |
+ assertTrue("The post construct lifecycle should have been invoked on bean", bean.postConstruct); | |
+ | |
+ AnotherBean another = bean.another; | |
+ assertNotNull("Should have instantiated the another", another); | |
+ assertTrue("The post construct lifecycle should have been invoked on another", another.postConstruct); | |
+ | |
+ | |
+ assertFalse("The pre destroy lifecycle not should have been invoked on bean", bean.preDestroy); | |
+ injector.close(); | |
+ assertTrue("The pre destroy lifecycle should have been invoked on bean", bean.preDestroy); | |
+ } | |
+ | |
+ static class MyBean { | |
+ @Inject | |
+ public AnotherBean another; | |
+ | |
+ public boolean postConstruct; | |
+ public boolean preDestroy; | |
+ | |
+ @PostConstruct | |
+ public void postConstruct() throws Exception { | |
+ postConstruct = true; | |
+ } | |
+ | |
+ @PreDestroy | |
+ public void preDestroy() throws Exception { | |
+ preDestroy = true; | |
+ } | |
+ } | |
+ | |
+ static class AnotherBean { | |
+ public boolean postConstruct; | |
+ | |
+ @PostConstruct | |
+ public void postConstruct() throws Exception { | |
+ postConstruct = true; | |
+ } | |
+ } | |
+} | |
\ No newline at end of file | |
Index: src/com/google/inject/InjectionRequestProcessor.java | |
=================================================================== | |
--- src/com/google/inject/InjectionRequestProcessor.java (revision 627) | |
+++ src/com/google/inject/InjectionRequestProcessor.java Tue Oct 07 14:32:25 BST 2008 | |
@@ -23,10 +23,14 @@ | |
import com.google.inject.internal.ConfigurationException; | |
import com.google.inject.internal.Errors; | |
import com.google.inject.internal.ErrorsException; | |
+import com.google.inject.spi.AnnotationProviderFactory; | |
import com.google.inject.spi.InjectionPoint; | |
import com.google.inject.spi.InjectionRequest; | |
import com.google.inject.spi.StaticInjectionRequest; | |
+import java.lang.annotation.Annotation; | |
+import java.util.Collections; | |
import java.util.List; | |
+import java.util.Map; | |
/** | |
* Handles {@link Binder#requestInjection} and {@link Binder#requestStaticInjection} commands. | |
@@ -54,8 +58,9 @@ | |
@Override public Boolean visitInjectionRequest(InjectionRequest command) { | |
List<InjectionPoint> injectionPointsList = Lists.newArrayList(); | |
try { | |
- InjectionPoint.addForInstanceMethodsAndFields( | |
- command.getInstance().getClass(), injectionPointsList); | |
+ Map<Class<? extends Annotation>,AnnotationProviderFactory> customInjections = memberInjector | |
+ .annotationProviderFactories(); | |
+ InjectionPoint.addForInstanceMethodsAndFields(customInjections, command.getInstance().getClass(), injectionPointsList); | |
} catch (ConfigurationException e) { | |
errors.merge(e.getErrorMessages()); | |
} | |
@@ -92,7 +97,8 @@ | |
Errors errorsForMember = errors.withSource(source); | |
List<InjectionPoint> injectionPoints = Lists.newArrayList(); | |
try { | |
- InjectionPoint.addForStaticMethodsAndFields(type, injectionPoints); | |
+ // TODO need the map? | |
+ InjectionPoint.addForStaticMethodsAndFields(Collections.EMPTY_MAP, type, injectionPoints); | |
} catch (ConfigurationException e) { | |
errors.merge(e.getErrorMessages()); | |
} | |
Index: spring/src/com/google/inject/spring/InitializingBeanInterceptor.java | |
=================================================================== | |
--- spring/src/com/google/inject/spring/InitializingBeanInterceptor.java Mon Oct 06 13:51:06 BST 2008 | |
+++ spring/src/com/google/inject/spring/InitializingBeanInterceptor.java Mon Oct 06 13:51:06 BST 2008 | |
@@ -0,0 +1,42 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spring; | |
+ | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
+import org.aopalliance.intercept.ConstructorInvocation; | |
+import org.springframework.beans.factory.InitializingBean; | |
+ | |
+/** | |
+ * Invokes the Spring post construction lifecycle on classes implementing {@link InitializingBean}. | |
+ * <p> | |
+ * To install this interceptor call the {@link SpringIntegration#bindLifecycle(com.google.inject.Binder)} method | |
+ * from your module. | |
+ * | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public class InitializingBeanInterceptor implements ConstructorInterceptor { | |
+ | |
+ public Object construct(ConstructorInvocation invocation) throws Throwable { | |
+ Object value = invocation.proceed(); | |
+ if (value instanceof InitializingBean) { | |
+ InitializingBean bean = (InitializingBean) value; | |
+ bean.afterPropertiesSet(); | |
+ } | |
+ return value; | |
+ } | |
+} | |
Index: jsr250/src/com/google/inject/jsr250/package-info.java | |
=================================================================== | |
--- jsr250/src/com/google/inject/jsr250/package-info.java Fri Oct 03 17:26:31 BST 2008 | |
+++ jsr250/src/com/google/inject/jsr250/package-info.java Fri Oct 03 17:26:31 BST 2008 | |
@@ -0,0 +1,20 @@ | |
+/* | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+/** | |
+ * JSR 250 integration | |
+ */ | |
+package com.google.inject.jsr250; | |
\ No newline at end of file | |
Index: jsr250/build.properties | |
=================================================================== | |
--- jsr250/build.properties Fri Oct 03 17:17:37 BST 2008 | |
+++ jsr250/build.properties Fri Oct 03 17:17:37 BST 2008 | |
@@ -0,0 +1,5 @@ | |
+src.dir=src | |
+test.dir=test | |
+build.dir=build | |
+test.class=com.google.inject.jsr250.Jsr250Test | |
+module=com.google.inject.jsr250 | |
Index: src/com/google/inject/Binder.java | |
=================================================================== | |
--- src/com/google/inject/Binder.java (revision 627) | |
+++ src/com/google/inject/Binder.java Fri Oct 03 16:08:58 BST 2008 | |
@@ -25,6 +25,7 @@ | |
import java.lang.annotation.Annotation; | |
import java.lang.reflect.Method; | |
import org.aopalliance.intercept.MethodInterceptor; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
/** | |
* Collects configuration information (primarily <i>bindings</i>) which will be | |
@@ -200,6 +201,16 @@ | |
Matcher<? super Method> methodMatcher, MethodInterceptor... interceptors); | |
/** | |
+ * Binds a constructor interceptor to matching classes. | |
+ * | |
+ * @param classMatcher matches classes the interceptor should apply to. For | |
+ * example: {@code only(Runnable.class)}. | |
+ * @param interceptors to bind | |
+ */ | |
+ void bindConstructorInterceptor(Matcher<? super Class<?>> classMatcher, | |
+ ConstructorInterceptor... interceptors); | |
+ | |
+ /** | |
* Binds a scope to an annotation. | |
*/ | |
void bindScope(Class<? extends Annotation> annotationType, Scope scope); | |
Index: src/com/google/inject/spi/ModuleWriter.java | |
=================================================================== | |
--- src/com/google/inject/spi/ModuleWriter.java (revision 627) | |
+++ src/com/google/inject/spi/ModuleWriter.java Fri Oct 03 16:11:43 BST 2008 | |
@@ -30,6 +30,7 @@ | |
import java.util.List; | |
import java.util.Set; | |
import org.aopalliance.intercept.MethodInterceptor; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
/** | |
* Creates a Module from a collection of component elements. | |
@@ -69,6 +70,11 @@ | |
return null; | |
} | |
+ public Void visitConstructorInterceptorBinding(ConstructorInterceptorBinding element) { | |
+ writeBindConstructorInterceptor(binder, element); | |
+ return null; | |
+ } | |
+ | |
public Void visitScopeBinding(ScopeBinding element) { | |
writeBindScope(binder, element); | |
return null; | |
@@ -116,6 +122,13 @@ | |
interceptors.toArray(new MethodInterceptor[interceptors.size()])); | |
} | |
+ protected void writeBindConstructorInterceptor(final Binder binder, final ConstructorInterceptorBinding element) { | |
+ List<ConstructorInterceptor> interceptors = element.getInterceptors(); | |
+ binder.withSource(element.getSource()).bindConstructorInterceptor( | |
+ element.getClassMatcher(), | |
+ interceptors.toArray(new ConstructorInterceptor[interceptors.size()])); | |
+ } | |
+ | |
protected void writeBindScope(final Binder binder, final ScopeBinding element) { | |
binder.withSource(element.getSource()).bindScope( | |
element.getAnnotationType(), element.getScope()); | |
Index: test/com/google/inject/IntegrationTest.java | |
=================================================================== | |
--- test/com/google/inject/IntegrationTest.java (revision 627) | |
+++ test/com/google/inject/IntegrationTest.java Fri Oct 03 16:37:08 BST 2008 | |
@@ -18,8 +18,10 @@ | |
import static com.google.inject.matcher.Matchers.any; | |
import junit.framework.TestCase; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
import org.aopalliance.intercept.MethodInterceptor; | |
import org.aopalliance.intercept.MethodInvocation; | |
+import org.aopalliance.intercept.ConstructorInvocation; | |
/** | |
* @author crazybob@google.com (Bob Lee) | |
@@ -64,4 +66,36 @@ | |
} | |
} | |
+ public void testConstructorInterceptor() throws Exception { | |
+ final PostConstructInterceptor counter = new PostConstructInterceptor(); | |
+ | |
+ Injector injector = Guice.createInjector(new AbstractModule() { | |
+ protected void configure() { | |
+ bind(Foo.class); | |
+ bindConstructorInterceptor(any(), counter); | |
-} | |
+ } | |
+ }); | |
+ | |
+ Foo foo = injector.getInstance(Key.get(Foo.class)); | |
+ foo.foo(); | |
+ assertTrue(foo.invoked); | |
+ assertEquals(1, counter.count); | |
+ | |
+ foo = injector.getInstance(Foo.class); | |
+ foo.foo(); | |
+ assertTrue(foo.invoked); | |
+ assertEquals(2, counter.count); | |
+ | |
+ } | |
+ | |
+ static class PostConstructInterceptor implements ConstructorInterceptor { | |
+ | |
+ int count; | |
+ | |
+ public Object construct(ConstructorInvocation constructorInvocation) throws Throwable { | |
+ count++; | |
+ return constructorInvocation.proceed(); | |
+ } | |
+ } | |
+ | |
+} | |
Index: jsr250/src/com/google/inject/jsr250/Jsr250.java | |
=================================================================== | |
--- jsr250/src/com/google/inject/jsr250/Jsr250.java Tue Oct 07 13:51:48 BST 2008 | |
+++ jsr250/src/com/google/inject/jsr250/Jsr250.java Tue Oct 07 13:51:48 BST 2008 | |
@@ -0,0 +1,44 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jsr250; | |
+ | |
+import com.google.inject.Binder; | |
+import com.google.inject.matcher.Matchers; | |
+import javax.annotation.PostConstruct; | |
+ | |
+/** | |
+ * Helper method for installing the JSR 250 lifecycle to a binder | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public final class Jsr250 { | |
+ | |
+ /** | |
+ * Binds the JSR 250 lifecycles to the current binder | |
+ * | |
+ * @param binder the binder to install | |
+ */ | |
+ public static void bind(Binder binder) { | |
+ binder.bindConstructorInterceptor(Matchers.methodAnnotatedWith(PostConstruct.class), | |
+ new PostConstructInterceptor()); | |
+ | |
+ binder.bind(PreDestroyCloser.class); | |
+ binder.bind(ResourceProviderFactory.class); | |
+ } | |
+} | |
Index: src/com/google/inject/InjectorBuilder.java | |
=================================================================== | |
--- src/com/google/inject/InjectorBuilder.java (revision 627) | |
+++ src/com/google/inject/InjectorBuilder.java Mon Oct 06 13:46:35 BST 2008 | |
@@ -30,6 +30,7 @@ | |
import com.google.inject.spi.Element; | |
import com.google.inject.spi.Elements; | |
import com.google.inject.spi.InjectionPoint; | |
+import com.google.inject.spi.CloseFailedException; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
@@ -321,5 +322,9 @@ | |
throw new UnsupportedOperationException( | |
"Injector.getInstance(Class<T>) is not supported in Stage.TOOL"); | |
} | |
+ public void close() throws CloseFailedException { | |
+ throw new UnsupportedOperationException( | |
+ "Injector.close() is not supported in Stage.TOOL"); | |
- } | |
-} | |
+ } | |
+ } | |
+} | |
Index: src/com/google/inject/spi/ElementVisitor.java | |
=================================================================== | |
--- src/com/google/inject/spi/ElementVisitor.java (revision 627) | |
+++ src/com/google/inject/spi/ElementVisitor.java Fri Oct 03 16:06:50 BST 2008 | |
@@ -38,6 +38,11 @@ | |
V visitInterceptorBinding(InterceptorBinding interceptorBinding); | |
/** | |
+ * Visit a registration of constructor interceptors for matching classes. | |
+ */ | |
+ V visitConstructorInterceptorBinding(ConstructorInterceptorBinding constructorInterceptorBinding); | |
+ | |
+ /** | |
* Visit a registration of a scope annotation with the scope that implements it. | |
*/ | |
V visitScopeBinding(ScopeBinding scopeBinding); | |
Index: servlet/src/com/google/inject/servlet/GuiceServletContextListener.java | |
=================================================================== | |
--- servlet/src/com/google/inject/servlet/GuiceServletContextListener.java (revision 627) | |
+++ servlet/src/com/google/inject/servlet/GuiceServletContextListener.java Mon Oct 06 14:29:32 BST 2008 | |
@@ -17,6 +17,7 @@ | |
package com.google.inject.servlet; | |
import com.google.inject.Injector; | |
+import com.google.inject.spi.CloseFailedException; | |
import javax.servlet.ServletContext; | |
import javax.servlet.ServletContextEvent; | |
import javax.servlet.ServletContextListener; | |
@@ -39,6 +40,16 @@ | |
public void contextDestroyed(ServletContextEvent servletContextEvent) { | |
ServletContext servletContext = servletContextEvent.getServletContext(); | |
+ Object value = servletContext.getAttribute(INJECTOR_NAME); | |
+ if (value instanceof Injector) { | |
+ Injector injector = (Injector) value; | |
+ try { | |
+ injector.close(); | |
+ } | |
+ catch (CloseFailedException e) { | |
+ servletContext.log("Failed to close injector. Reason: " + e, e); | |
+ } | |
+ } | |
servletContext.removeAttribute(INJECTOR_NAME); | |
} | |
Index: spring/src/com/google/inject/spring/SpringIntegration.java | |
=================================================================== | |
--- spring/src/com/google/inject/spring/SpringIntegration.java (revision 627) | |
+++ spring/src/com/google/inject/spring/SpringIntegration.java Mon Oct 06 13:51:06 BST 2008 | |
@@ -20,9 +20,11 @@ | |
import com.google.inject.Binder; | |
import com.google.inject.Inject; | |
import com.google.inject.Provider; | |
+import com.google.inject.matcher.Matchers; | |
import com.google.inject.name.Names; | |
import org.springframework.beans.factory.BeanFactory; | |
import org.springframework.beans.factory.ListableBeanFactory; | |
+import org.springframework.beans.factory.InitializingBean; | |
/** | |
* Integrates Guice with Spring. | |
@@ -32,7 +34,19 @@ | |
public class SpringIntegration { | |
private SpringIntegration() {} | |
+ | |
/** | |
+ * Binds the Spring lifecycles to the current binder | |
+ * | |
+ * @param binder the binder to install | |
+ */ | |
+ public static void bindLifecycle(Binder binder) { | |
+ binder.bindConstructorInterceptor(Matchers.subclassesOf(InitializingBean.class), | |
+ new InitializingBeanInterceptor()); | |
+ binder.bind(DisposableBeanCloser.class); | |
+ } | |
+ | |
+ /** | |
* Creates a provider which looks up objects from Spring using the given name. | |
* Expects a binding to {@link | |
* org.springframework.beans.factory.BeanFactory}. Example usage: | |
Index: build.properties | |
=================================================================== | |
--- build.properties (revision 627) | |
+++ build.properties Fri Oct 03 17:21:04 BST 2008 | |
@@ -3,6 +3,7 @@ | |
test.dir=test | |
servlet.src.dir=servlet/src | |
spring.src.dir=spring/src | |
+jsr250.src.dir=jsr250/src | |
assistedinject.src.dir=extensions/assistedinject/src | |
commands.src.dir=extensions/commands/src | |
throwingproviders.src.dir=extensions/throwingproviders/src | |
@@ -12,6 +13,7 @@ | |
com.google.inject.matcher,com.google.inject.servlet,com.google.inject.name,\ | |
com.google.inject.tools.jmx,com.google.inject.binder,com.google.inject.jndi,\ | |
com.google.inject.spring,\ | |
+ com.google.inject.jsr250,\ | |
com.google.inject.assistedinject,\ | |
com.google.inject.throwingproviders,\ | |
com.google.inject.multibindings,\ | |
Index: jsr250/test/com/google/inject/jsr250/ResourceTest.java | |
=================================================================== | |
--- jsr250/test/com/google/inject/jsr250/ResourceTest.java Tue Oct 07 15:38:26 BST 2008 | |
+++ jsr250/test/com/google/inject/jsr250/ResourceTest.java Tue Oct 07 15:38:26 BST 2008 | |
@@ -0,0 +1,79 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jsr250; | |
+ | |
+import com.google.inject.AbstractModule; | |
+import com.google.inject.CreationException; | |
+import com.google.inject.Guice; | |
+import com.google.inject.Injector; | |
+import com.google.inject.Provides; | |
+import com.google.inject.Singleton; | |
+import com.google.inject.jndi.internal.JndiContext; | |
+import com.google.inject.spi.CloseFailedException; | |
+import javax.annotation.Resource; | |
+import javax.naming.Context; | |
+import junit.framework.TestCase; | |
+ | |
+/** @author james.strachan@gmail.com (James Strachan) */ | |
+public class ResourceTest extends TestCase { | |
+ | |
+ public void testResourceInjection() throws CreationException, CloseFailedException { | |
+ Injector injector = Guice.createInjector(new AbstractModule() { | |
+ protected void configure() { | |
+ Jsr250.bind(binder()); | |
+ | |
+ bind(MyBean.class).in(Singleton.class); | |
+ } | |
+ | |
+ @Provides | |
+ public Context createJndiContext() throws Exception { | |
+ JndiContext answer = new JndiContext(); | |
+ answer.bind("foo", new AnotherBean("Foo")); | |
+ answer.bind("xyz", new AnotherBean("XYZ")); | |
+ return answer; | |
+ } | |
+ }); | |
+ | |
+ MyBean bean = injector.getInstance(MyBean.class); | |
+ assertNotNull("Should have instantiated the bean", bean); | |
+ assertNotNull("Should have injected a foo", bean.foo); | |
+ assertNotNull("Should have injected a bar", bean.bar); | |
+ | |
+ assertEquals("Should have injected correct foo", "Foo", bean.foo.name); | |
+ assertEquals("Should have injected correct bar", "XYZ", bean.bar.name); | |
+ } | |
+ | |
+ public static class MyBean { | |
+ @Resource | |
+ public AnotherBean foo; | |
+ | |
+ public AnotherBean bar; | |
+ | |
+ @Resource(name = "xyz") | |
+ public void bar(AnotherBean bar) { | |
+ this.bar = bar; | |
+ } | |
+ } | |
+ | |
+ static class AnotherBean { | |
+ public String name = "undefined"; | |
+ | |
+ AnotherBean(String name) { | |
+ this.name = name; | |
+ } | |
+ } | |
+} | |
\ No newline at end of file | |
Index: src/com/google/inject/AbstractProcessor.java | |
=================================================================== | |
--- src/com/google/inject/AbstractProcessor.java (revision 627) | |
+++ src/com/google/inject/AbstractProcessor.java Fri Oct 03 16:09:58 BST 2008 | |
@@ -26,6 +26,7 @@ | |
import com.google.inject.spi.ScopeBinding; | |
import com.google.inject.spi.StaticInjectionRequest; | |
import com.google.inject.spi.TypeConverterBinding; | |
+import com.google.inject.spi.ConstructorInterceptorBinding; | |
import java.util.Iterator; | |
import java.util.List; | |
@@ -70,6 +71,10 @@ | |
return false; | |
} | |
+ public Boolean visitConstructorInterceptorBinding(ConstructorInterceptorBinding constructorInterceptorBinding) { | |
+ return false; | |
+ } | |
+ | |
public Boolean visitScopeBinding(ScopeBinding scopeBinding) { | |
return false; | |
} | |
Index: src/com/google/inject/jndi/internal/JndiContext.java | |
=================================================================== | |
--- src/com/google/inject/jndi/internal/JndiContext.java Tue Oct 07 13:49:12 BST 2008 | |
+++ src/com/google/inject/jndi/internal/JndiContext.java Tue Oct 07 13:49:12 BST 2008 | |
@@ -0,0 +1,448 @@ | |
+/** | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.jndi.internal; | |
+ | |
+import com.google.inject.Injector; | |
+import com.google.inject.Key; | |
+import static com.google.inject.name.Names.named; | |
+import java.io.Serializable; | |
+import java.util.HashMap; | |
+import java.util.Hashtable; | |
+import java.util.Iterator; | |
+import java.util.Map; | |
+import javax.naming.Binding; | |
+import javax.naming.CompositeName; | |
+import javax.naming.Context; | |
+import javax.naming.LinkRef; | |
+import javax.naming.Name; | |
+import javax.naming.NameClassPair; | |
+import javax.naming.NameNotFoundException; | |
+import javax.naming.NameParser; | |
+import javax.naming.NamingEnumeration; | |
+import javax.naming.NamingException; | |
+import javax.naming.NotContextException; | |
+import javax.naming.OperationNotSupportedException; | |
+import javax.naming.Reference; | |
+import javax.naming.spi.NamingManager; | |
+ | |
+/** | |
+ * A default JNDI context | |
+ * | |
+ * @version $Revision:$ | |
+ */ | |
+public class JndiContext implements Context, Serializable { | |
+ public static final String SEPARATOR = "/"; | |
+ protected static final NameParser NAME_PARSER = new NameParser() { | |
+ public Name parse(String name) throws NamingException { | |
+ return new CompositeName(name); | |
+ } | |
+ }; | |
+ private static final long serialVersionUID = -5754338187296859149L; | |
+ | |
+ private final Hashtable environment; // environment for this context | |
+ private final Map bindings; // bindings at my level | |
+ private final Map treeBindings; // all bindings under me | |
+ private boolean frozen; | |
+ private String nameInNamespace = ""; | |
+ public static final String POSTFIX = ".class"; | |
+ | |
+ public JndiContext() throws Exception { | |
+ this(new Hashtable(), new Hashtable()); | |
+ } | |
+ | |
+ public JndiContext(Hashtable env, Injector injector) throws Exception { | |
+ this(env, createBindingsMapFromEnvironment(env, injector)); | |
+ } | |
+ | |
+ public JndiContext(Hashtable environment, Map bindings) { | |
+ if (environment == null) { | |
+ this.environment = new Hashtable(); | |
+ } | |
+ else { | |
+ this.environment = new Hashtable(environment); | |
+ } | |
+ this.bindings = bindings; | |
+ treeBindings = new HashMap(); | |
+ } | |
+ | |
+ public JndiContext(Hashtable environment, Map bindings, String nameInNamespace) { | |
+ this(environment, bindings); | |
+ this.nameInNamespace = nameInNamespace; | |
+ } | |
+ | |
+ protected JndiContext(JndiContext clone, Hashtable env) { | |
+ this.bindings = clone.bindings; | |
+ this.treeBindings = clone.treeBindings; | |
+ this.environment = new Hashtable(env); | |
+ } | |
+ | |
+ protected JndiContext(JndiContext clone, Hashtable env, String nameInNamespace) { | |
+ this(clone, env); | |
+ this.nameInNamespace = nameInNamespace; | |
+ } | |
+ | |
+ /** | |
+ * A helper method to create the JNDI bindings from the input environment properties using | |
+ * $foo.class to point to a class name with $foo.* being properties set on the injected bean | |
+ */ | |
+ public static Map createBindingsMapFromEnvironment(Hashtable env, Injector injector) | |
+ throws Exception { | |
+ Map answer = new HashMap(env); | |
+ | |
+ if (injector != null) { | |
+ for (Object object : env.entrySet()) { | |
+ Map.Entry entry = (Map.Entry) object; | |
+ Object key = entry.getKey(); | |
+ Object value = entry.getValue(); | |
+ | |
+ if (key instanceof String && value instanceof String) { | |
+ // TODO figure out a better binding to automatically expose the entries using IoC from injector | |
+ String keyText = (String) key; | |
+ String valueText = (String) value; | |
+ if (keyText.endsWith(POSTFIX)) { | |
+ String name = keyText.substring(0, keyText.length() - POSTFIX.length()); | |
+ Class<Object> type = Object.class; | |
+ Object bean = injector.getInstance(Key.get(type, named(name))); | |
+ if (bean != null) { | |
+ answer.put(name, bean); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ return answer; | |
+ } | |
+ | |
+ public void freeze() { | |
+ frozen = true; | |
+ } | |
+ | |
+ boolean isFrozen() { | |
+ return frozen; | |
+ } | |
+ | |
+ /** | |
+ * internalBind is intended for use only during setup or possibly by suitably synchronized | |
+ * superclasses. It binds every possible lookup into a map in each context. To do this, each | |
+ * context strips off one name segment and if necessary creates a new context for it. Then it asks | |
+ * that context to bind the remaining name. It returns a map containing all the bindings from the | |
+ * next context, plus the context it just created (if it in fact created it). (the names are | |
+ * suitably extended by the segment originally lopped off). | |
+ */ | |
+ protected Map internalBind(String name, Object value) throws NamingException { | |
+ assert name != null && name.length() > 0; | |
+ assert !frozen; | |
+ | |
+ Map newBindings = new HashMap(); | |
+ int pos = name.indexOf('/'); | |
+ if (pos == -1) { | |
+ if (treeBindings.put(name, value) != null) { | |
+ throw new NamingException("Something already bound at " + name); | |
+ } | |
+ bindings.put(name, value); | |
+ newBindings.put(name, value); | |
+ } | |
+ else { | |
+ String segment = name.substring(0, pos); | |
+ assert segment != null; | |
+ assert !segment.equals(""); | |
+ Object o = treeBindings.get(segment); | |
+ if (o == null) { | |
+ o = newContext(); | |
+ treeBindings.put(segment, o); | |
+ bindings.put(segment, o); | |
+ newBindings.put(segment, o); | |
+ } | |
+ else if (!(o instanceof JndiContext)) { | |
+ throw new NamingException("Something already bound where a subcontext should go"); | |
+ } | |
+ JndiContext defaultContext = (JndiContext) o; | |
+ String remainder = name.substring(pos + 1); | |
+ Map subBindings = defaultContext.internalBind(remainder, value); | |
+ for (Iterator iterator = subBindings.entrySet().iterator(); iterator.hasNext();) { | |
+ Map.Entry entry = (Map.Entry) iterator.next(); | |
+ String subName = segment + "/" + (String) entry.getKey(); | |
+ Object bound = entry.getValue(); | |
+ treeBindings.put(subName, bound); | |
+ newBindings.put(subName, bound); | |
+ } | |
+ } | |
+ return newBindings; | |
+ } | |
+ | |
+ protected JndiContext newContext() { | |
+ try { | |
+ return new JndiContext(); | |
+ } | |
+ catch (Exception e) { | |
+ throw new IllegalArgumentException(e); | |
+ } | |
+ } | |
+ | |
+ public Object addToEnvironment(String propName, Object propVal) throws NamingException { | |
+ return environment.put(propName, propVal); | |
+ } | |
+ | |
+ public Hashtable getEnvironment() throws NamingException { | |
+ return (Hashtable) environment.clone(); | |
+ } | |
+ | |
+ public Object removeFromEnvironment(String propName) throws NamingException { | |
+ return environment.remove(propName); | |
+ } | |
+ | |
+ public Object lookup(String name) throws NamingException { | |
+ if (name.length() == 0) { | |
+ return this; | |
+ } | |
+ Object result = treeBindings.get(name); | |
+ if (result == null) { | |
+ result = bindings.get(name); | |
+ } | |
+ if (result == null) { | |
+ int pos = name.indexOf(':'); | |
+ if (pos > 0) { | |
+ String scheme = name.substring(0, pos); | |
+ Context ctx = NamingManager.getURLContext(scheme, environment); | |
+ if (ctx == null) { | |
+ throw new NamingException("scheme " + scheme + " not recognized"); | |
+ } | |
+ return ctx.lookup(name); | |
+ } | |
+ else { | |
+ // Split out the first name of the path | |
+ // and look for it in the bindings map. | |
+ CompositeName path = new CompositeName(name); | |
+ | |
+ if (path.size() == 0) { | |
+ return this; | |
+ } | |
+ else { | |
+ String first = path.get(0); | |
+ Object value = bindings.get(first); | |
+ if (value == null) { | |
+ throw new NameNotFoundException(name); | |
+ } | |
+ else if (value instanceof Context && path.size() > 1) { | |
+ Context subContext = (Context) value; | |
+ value = subContext.lookup(path.getSuffix(1)); | |
+ } | |
+ return value; | |
+ } | |
+ } | |
+ } | |
+ if (result instanceof LinkRef) { | |
+ LinkRef ref = (LinkRef) result; | |
+ result = lookup(ref.getLinkName()); | |
+ } | |
+ if (result instanceof Reference) { | |
+ try { | |
+ result = NamingManager.getObjectInstance(result, null, null, this.environment); | |
+ } | |
+ catch (NamingException e) { | |
+ throw e; | |
+ } | |
+ catch (Exception e) { | |
+ throw (NamingException) new NamingException("could not look up : " + name).initCause(e); | |
+ } | |
+ } | |
+ if (result instanceof JndiContext) { | |
+ String prefix = getNameInNamespace(); | |
+ if (prefix.length() > 0) { | |
+ prefix = prefix + SEPARATOR; | |
+ } | |
+ result = new JndiContext((JndiContext) result, environment, prefix + name); | |
+ } | |
+ return result; | |
+ } | |
+ | |
+ public Object lookup(Name name) throws NamingException { | |
+ return lookup(name.toString()); | |
+ } | |
+ | |
+ public Object lookupLink(String name) throws NamingException { | |
+ return lookup(name); | |
+ } | |
+ | |
+ public Name composeName(Name name, Name prefix) throws NamingException { | |
+ Name result = (Name) prefix.clone(); | |
+ result.addAll(name); | |
+ return result; | |
+ } | |
+ | |
+ public String composeName(String name, String prefix) throws NamingException { | |
+ CompositeName result = new CompositeName(prefix); | |
+ result.addAll(new CompositeName(name)); | |
+ return result.toString(); | |
+ } | |
+ | |
+ public NamingEnumeration list(String name) throws NamingException { | |
+ Object o = lookup(name); | |
+ if (o == this) { | |
+ return new ListEnumeration(); | |
+ } | |
+ else if (o instanceof Context) { | |
+ return ((Context) o).list(""); | |
+ } | |
+ else { | |
+ throw new NotContextException(); | |
+ } | |
+ } | |
+ | |
+ public NamingEnumeration listBindings(String name) throws NamingException { | |
+ Object o = lookup(name); | |
+ if (o == this) { | |
+ return new ListBindingEnumeration(); | |
+ } | |
+ else if (o instanceof Context) { | |
+ return ((Context) o).listBindings(""); | |
+ } | |
+ else { | |
+ throw new NotContextException(); | |
+ } | |
+ } | |
+ | |
+ public Object lookupLink(Name name) throws NamingException { | |
+ return lookupLink(name.toString()); | |
+ } | |
+ | |
+ public NamingEnumeration list(Name name) throws NamingException { | |
+ return list(name.toString()); | |
+ } | |
+ | |
+ public NamingEnumeration listBindings(Name name) throws NamingException { | |
+ return listBindings(name.toString()); | |
+ } | |
+ | |
+ public void bind(Name name, Object value) throws NamingException { | |
+ bind(name.toString(), value); | |
+ } | |
+ | |
+ public void bind(String name, Object value) throws NamingException { | |
+ if (isFrozen()) { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ else { | |
+ internalBind(name, value); | |
+ } | |
+ } | |
+ | |
+ public void close() throws NamingException { | |
+ // ignore | |
+ } | |
+ | |
+ public Context createSubcontext(Name name) throws NamingException { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ | |
+ public Context createSubcontext(String name) throws NamingException { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ | |
+ public void destroySubcontext(Name name) throws NamingException { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ | |
+ public void destroySubcontext(String name) throws NamingException { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ | |
+ public String getNameInNamespace() throws NamingException { | |
+ return nameInNamespace; | |
+ } | |
+ | |
+ public NameParser getNameParser(Name name) throws NamingException { | |
+ return NAME_PARSER; | |
+ } | |
+ | |
+ public NameParser getNameParser(String name) throws NamingException { | |
+ return NAME_PARSER; | |
+ } | |
+ | |
+ public void rebind(Name name, Object value) throws NamingException { | |
+ bind(name, value); | |
+ } | |
+ | |
+ public void rebind(String name, Object value) throws NamingException { | |
+ bind(name, value); | |
+ } | |
+ | |
+ public void rename(Name oldName, Name newName) throws NamingException { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ | |
+ public void rename(String oldName, String newName) throws NamingException { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ | |
+ public void unbind(Name name) throws NamingException { | |
+ throw new OperationNotSupportedException(); | |
+ } | |
+ | |
+ public void unbind(String name) throws NamingException { | |
+ bindings.remove(name); | |
+ treeBindings.remove(name); | |
+ } | |
+ | |
+ private abstract class LocalNamingEnumeration implements NamingEnumeration { | |
+ private Iterator i = bindings.entrySet().iterator(); | |
+ | |
+ public boolean hasMore() throws NamingException { | |
+ return i.hasNext(); | |
+ } | |
+ | |
+ public boolean hasMoreElements() { | |
+ return i.hasNext(); | |
+ } | |
+ | |
+ protected Map.Entry getNext() { | |
+ return (Map.Entry) i.next(); | |
+ } | |
+ | |
+ public void close() throws NamingException { | |
+ } | |
+ } | |
+ | |
+ private class ListEnumeration extends LocalNamingEnumeration { | |
+ ListEnumeration() { | |
+ } | |
+ | |
+ public Object next() throws NamingException { | |
+ return nextElement(); | |
+ } | |
+ | |
+ public Object nextElement() { | |
+ Map.Entry entry = getNext(); | |
+ return new NameClassPair((String) entry.getKey(), entry.getValue().getClass().getName()); | |
+ } | |
+ } | |
+ | |
+ private class ListBindingEnumeration extends LocalNamingEnumeration { | |
+ ListBindingEnumeration() { | |
+ } | |
+ | |
+ public Object next() throws NamingException { | |
+ return nextElement(); | |
+ } | |
+ | |
+ public Object nextElement() { | |
+ Map.Entry entry = getNext(); | |
+ return new Binding((String) entry.getKey(), entry.getValue()); | |
+ } | |
+ } | |
+} | |
Index: spring/test/com/google/inject/spring/LifecycleTest.java | |
=================================================================== | |
--- spring/test/com/google/inject/spring/LifecycleTest.java Mon Oct 06 13:53:23 BST 2008 | |
+++ spring/test/com/google/inject/spring/LifecycleTest.java Mon Oct 06 13:53:23 BST 2008 | |
@@ -0,0 +1,63 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spring; | |
+ | |
+import com.google.inject.AbstractModule; | |
+import com.google.inject.CreationException; | |
+import com.google.inject.Guice; | |
+import com.google.inject.Injector; | |
+import com.google.inject.Singleton; | |
+import com.google.inject.spi.CloseFailedException; | |
+import junit.framework.TestCase; | |
+import org.springframework.beans.factory.DisposableBean; | |
+import org.springframework.beans.factory.InitializingBean; | |
+ | |
+/** @author james.strachan@gmail.com (James Strachan) */ | |
+public class LifecycleTest extends TestCase { | |
+ | |
+ public void testBeanInitialised() throws CreationException, CloseFailedException { | |
+ Injector injector = Guice.createInjector(new AbstractModule() { | |
+ protected void configure() { | |
+ SpringIntegration.bindLifecycle(binder()); | |
+ | |
+ bind(MyBean.class).in(Singleton.class); | |
+ } | |
+ }); | |
+ | |
+ MyBean bean = injector.getInstance(MyBean.class); | |
+ assertNotNull("Should have instantiated the bean", bean); | |
+ assertTrue("Should have properties set on bean", bean.propertiesSet); | |
+ | |
+ assertFalse("Should not be destroyed yet", bean.destroyed); | |
+ injector.close(); | |
+ | |
+ assertTrue("Should have destroyed the bean", bean.destroyed); | |
+ } | |
+ | |
+ static class MyBean implements InitializingBean, DisposableBean { | |
+ public boolean propertiesSet = false; | |
+ public boolean destroyed; | |
+ | |
+ public void afterPropertiesSet() throws Exception { | |
+ propertiesSet = true; | |
+ } | |
+ | |
+ public void destroy() throws Exception { | |
+ destroyed = true; | |
+ } | |
+ } | |
+} | |
\ No newline at end of file | |
Index: jsr250/build.xml | |
=================================================================== | |
--- jsr250/build.xml Fri Oct 03 17:16:44 BST 2008 | |
+++ jsr250/build.xml Fri Oct 03 17:16:44 BST 2008 | |
@@ -0,0 +1,22 @@ | |
+<?xml version="1.0"?> | |
+ | |
+<project name="guice-jsr250" basedir="." default="jar"> | |
+ | |
+ <import file="../common.xml"/> | |
+ | |
+ <path id="compile.classpath"> | |
+ <fileset dir="../lib" includes="*.jar"/> | |
+ <fileset dir="../lib/build" includes="*.jar"/> | |
+ <fileset dir="../build/dist" includes="*.jar"/> | |
+ </path> | |
+ | |
+ <target name="jar" depends="compile, manifest" | |
+ description="Build jar."> | |
+ <mkdir dir="${build.dir}"/> | |
+ <jar destfile="${build.dir}/${ant.project.name}-${version}.jar" | |
+ manifest="${build.dir}/META-INF/MANIFEST.MF"> | |
+ <fileset dir="${build.dir}/classes"/> | |
+ </jar> | |
+ </target> | |
+ | |
+</project> | |
Index: src/com/google/inject/spi/Closeable.java | |
=================================================================== | |
--- src/com/google/inject/spi/Closeable.java Mon Oct 06 11:38:53 BST 2008 | |
+++ src/com/google/inject/spi/Closeable.java Mon Oct 06 11:38:53 BST 2008 | |
@@ -0,0 +1,28 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+/** | |
+ * Represents a scope or provider which can be closed | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface Closeable { | |
+ void close(Closer closer, CloseErrors errors); | |
+} | |
Index: src/com/google/inject/spi/InjectionPoint.java | |
=================================================================== | |
--- src/com/google/inject/spi/InjectionPoint.java (revision 627) | |
+++ src/com/google/inject/spi/InjectionPoint.java Tue Oct 07 15:05:17 BST 2008 | |
@@ -40,6 +40,9 @@ | |
import java.util.Collection; | |
import java.util.Iterator; | |
import java.util.List; | |
+import java.util.Map; | |
+import java.util.Set; | |
+import java.util.Map.Entry; | |
/** | |
* A constructor, field or method that can receive injections. Typically this is a member with the | |
@@ -53,38 +56,54 @@ | |
private final boolean optional; | |
private final Member member; | |
private final ImmutableList<Dependency<?>> dependencies; | |
+ private final AnnotationProviderFactory<?> annotationProviderFactory; | |
+ static InjectionPoint customInjectionPoint(Member member, AnnotationProviderFactory<?> annotationProviderFactory) { | |
+ if (member instanceof Field) { | |
+ return new InjectionPoint((Field) member, false, annotationProviderFactory); | |
+ } | |
+ else { | |
+ return new InjectionPoint((Method) member, false, annotationProviderFactory); | |
+ } | |
+ } | |
+ | |
private InjectionPoint(Member member, | |
ImmutableList<Dependency<?>> dependencies, boolean optional) { | |
this.member = member; | |
this.dependencies = dependencies; | |
this.optional = optional; | |
+ this.annotationProviderFactory = null; | |
} | |
- InjectionPoint(Method method) { | |
+ InjectionPoint(Method method, boolean optional, | |
+ AnnotationProviderFactory<?> annotationProviderFactory) { | |
this.member = method; | |
+ this.optional = optional; | |
+ this.annotationProviderFactory = annotationProviderFactory; | |
- Inject inject = method.getAnnotation(Inject.class); | |
- this.optional = inject.optional(); | |
- | |
this.dependencies = forMember(method, method.getGenericParameterTypes(), | |
method.getParameterAnnotations()); | |
} | |
+ InjectionPoint(Method method) { | |
+ this(method, method.getAnnotation(Inject.class).optional(), null); | |
+ } | |
+ | |
InjectionPoint(Constructor<?> constructor) { | |
this.member = constructor; | |
this.optional = false; | |
+ this.annotationProviderFactory = null; | |
// TODO(jessewilson): make sure that if @Inject it exists, its not optional | |
this.dependencies = forMember(constructor, constructor.getGenericParameterTypes(), | |
constructor.getParameterAnnotations()); | |
} | |
- InjectionPoint(Field field) { | |
+ InjectionPoint(Field field, boolean optional, | |
+ AnnotationProviderFactory<?> annotationProviderFactory) { | |
this.member = field; | |
+ this.optional = optional; | |
+ this.annotationProviderFactory = annotationProviderFactory; | |
- Inject inject = field.getAnnotation(Inject.class); | |
- this.optional = inject.optional(); | |
- | |
Annotation[] annotations = field.getAnnotations(); | |
Errors errors = new Errors(field); | |
@@ -100,6 +119,10 @@ | |
newDependency(key, Nullability.allowsNull(annotations), -1)); | |
} | |
+ InjectionPoint(Field field) { | |
+ this(field, field.getAnnotation(Inject.class).optional(), null); | |
+ } | |
+ | |
private ImmutableList<Dependency<?>> forMember(Member member, Type[] genericParameterTypes, | |
Annotation[][] annotations) { | |
Errors errors = new Errors(member); | |
@@ -155,6 +178,17 @@ | |
return optional; | |
} | |
+ /** | |
+ * Returns the custom annotation provider factory for custom injection points or null for regular | |
+ * @Inject injection points | |
+ * | |
+ * @return the custom annotation provider factory for custom injection points or null for regular | |
+ * injection points | |
+ */ | |
+ public AnnotationProviderFactory<?> getAnnotationProviderFactory() { | |
+ return annotationProviderFactory; | |
+ } | |
+ | |
@Override public boolean equals(Object o) { | |
return o instanceof InjectionPoint | |
&& member == ((InjectionPoint) o).member; | |
@@ -235,10 +269,10 @@ | |
* field with multiple binding annotations. When such an exception is thrown, the valid | |
* injection points are still added to the collection. | |
*/ | |
- public static void addForStaticMethodsAndFields(Class<?> type, Collection<InjectionPoint> sink) { | |
+ public static void addForStaticMethodsAndFields(Map<Class<? extends Annotation>,AnnotationProviderFactory> customInjections, Class<?> type, Collection<InjectionPoint> sink) { | |
Errors errors = new Errors(); | |
- addInjectionPoints(type, Factory.FIELDS, true, sink, errors); | |
- addInjectionPoints(type, Factory.METHODS, true, sink, errors); | |
+ addInjectionPoints(customInjections, type, Factory.FIELDS, true, sink, errors); | |
+ addInjectionPoints(customInjections, type, Factory.METHODS, true, sink, errors); | |
ConfigurationException.throwNewIfNonEmpty(errors); | |
} | |
@@ -251,16 +285,18 @@ | |
* field with multiple binding annotations. When such an exception is thrown, the valid | |
* injection points are still added to the collection. | |
*/ | |
- public static void addForInstanceMethodsAndFields(Class<?> type, | |
+ public static void addForInstanceMethodsAndFields(Map<Class<? extends Annotation>,AnnotationProviderFactory> customInjections, Class<?> type, | |
Collection<InjectionPoint> sink) { | |
// TODO (crazybob): Filter out overridden members. | |
Errors errors = new Errors(); | |
- addInjectionPoints(type, Factory.FIELDS, false, sink, errors); | |
- addInjectionPoints(type, Factory.METHODS, false, sink, errors); | |
+ addInjectionPoints(customInjections, type, Factory.FIELDS, false, sink, errors); | |
+ addInjectionPoints(customInjections, type, Factory.METHODS, false, sink, errors); | |
ConfigurationException.throwNewIfNonEmpty(errors); | |
} | |
- private static <M extends Member & AnnotatedElement> void addInjectionPoints(Class<?> type, | |
+ private static <M extends Member & AnnotatedElement> void addInjectionPoints( | |
+ Map<Class<? extends Annotation>,AnnotationProviderFactory> customInjections, | |
+ Class<?> type, | |
Factory<M> factory, boolean statics, Collection<InjectionPoint> injectionPoints, | |
Errors errors) { | |
if (type == Object.class) { | |
@@ -268,13 +304,15 @@ | |
} | |
// Add injectors for superclass first. | |
- addInjectionPoints(type.getSuperclass(), factory, statics, injectionPoints, errors); | |
+ addInjectionPoints(customInjections, type.getSuperclass(), factory, statics, injectionPoints, errors); | |
// Add injectors for all members next | |
- addInjectorsForMembers(type, factory, statics, injectionPoints, errors); | |
+ addInjectorsForMembers(customInjections, type, factory, statics, injectionPoints, errors); | |
} | |
- private static <M extends Member & AnnotatedElement> void addInjectorsForMembers(Class<?> type, | |
+ private static <M extends Member & AnnotatedElement> void addInjectorsForMembers( | |
+ Map<Class<? extends Annotation>,AnnotationProviderFactory> customInjections, | |
+ Class<?> type, | |
Factory<M> factory, boolean statics, Collection<InjectionPoint> injectionPoints, | |
Errors errors) { | |
for (M member : factory.getMembers(type)) { | |
@@ -283,19 +321,39 @@ | |
} | |
Inject inject = member.getAnnotation(Inject.class); | |
- if (inject == null) { | |
+ if (inject != null) { | |
+ try { | |
+ injectionPoints.add(factory.create(member)); | |
+ } catch (ConfigurationException e) { | |
+ if (!inject.optional()) { | |
+ errors.merge(e.getErrorMessages()); | |
+ } | |
+ } | |
continue; | |
} | |
+ if (!customInjections.isEmpty()) { | |
+ Set<Entry<Class<? extends Annotation>,AnnotationProviderFactory>> entries = customInjections | |
+ .entrySet(); | |
+ for (Entry<Class<? extends Annotation>, AnnotationProviderFactory> entry : entries) { | |
+ Class<? extends Annotation> annotationType = entry.getKey(); | |
+ AnnotationProviderFactory annotationProviderFactory = entry.getValue(); | |
+ Annotation annotation = member.getAnnotation(annotationType); | |
+ if (annotation != null) { | |
- try { | |
+ try { | |
- injectionPoints.add(factory.create(member)); | |
+ InjectionPoint injectionPoint = InjectionPoint.customInjectionPoint(member, annotationProviderFactory); | |
+ injectionPoints.add(injectionPoint); | |
- } catch (ConfigurationException e) { | |
- if (!inject.optional()) { | |
- errors.merge(e.getErrorMessages()); | |
- } | |
- } | |
+ } catch (ConfigurationException e) { | |
+ if (!inject.optional()) { | |
+ errors.merge(e.getErrorMessages()); | |
+ } | |
+ } | |
+ break; | |
- } | |
- } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
private static boolean isStatic(Member member) { | |
return Modifier.isStatic(member.getModifiers()); | |
Index: src/com/google/inject/spi/Elements.java | |
=================================================================== | |
--- src/com/google/inject/spi/Elements.java (revision 627) | |
+++ src/com/google/inject/spi/Elements.java Fri Oct 03 16:06:50 BST 2008 | |
@@ -40,6 +40,7 @@ | |
import java.util.Collections; | |
import java.util.List; | |
import java.util.Set; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
import org.aopalliance.intercept.MethodInterceptor; | |
/** | |
@@ -134,6 +135,12 @@ | |
elements.add(new InterceptorBinding(getSource(), classMatcher, methodMatcher, interceptors)); | |
} | |
+ public void bindConstructorInterceptor( | |
+ Matcher<? super Class<?>> classMatcher, | |
+ ConstructorInterceptor... interceptors) { | |
+ elements.add(new ConstructorInterceptorBinding(getSource(), classMatcher, interceptors)); | |
+ } | |
+ | |
public void bindScope(Class<? extends Annotation> annotationType, Scope scope) { | |
elements.add(new ScopeBinding(getSource(), annotationType, scope)); | |
} | |
Index: src/com/google/inject/spi/DefaultElementVisitor.java | |
=================================================================== | |
--- src/com/google/inject/spi/DefaultElementVisitor.java (revision 627) | |
+++ src/com/google/inject/spi/DefaultElementVisitor.java Fri Oct 03 16:12:12 BST 2008 | |
@@ -48,6 +48,10 @@ | |
return visitElement(command); | |
} | |
+ public V visitConstructorInterceptorBinding(ConstructorInterceptorBinding command) { | |
+ return visitElement(command); | |
+ } | |
+ | |
public V visitScopeBinding(ScopeBinding command) { | |
return visitElement(command); | |
} | |
Index: src/com/google/inject/ConstructorAspect.java | |
=================================================================== | |
--- src/com/google/inject/ConstructorAspect.java Sat Oct 04 13:43:31 BST 2008 | |
+++ src/com/google/inject/ConstructorAspect.java Sat Oct 04 13:43:31 BST 2008 | |
@@ -0,0 +1,47 @@ | |
+/** | |
+ * Copyright (C) 2006 Google Inc. | |
+ * | |
+ * Licensed under the Apache License, Version 2.0 (the "License"); | |
+ * you may not use this file except in compliance with the License. | |
+ * You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject; | |
+ | |
+import static com.google.common.base.Preconditions.checkNotNull; | |
+import com.google.inject.matcher.Matcher; | |
+import java.util.List; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
+ | |
+/** | |
+ * Ties a matcher to a constructor interceptor. | |
+ * | |
+ * @author james.strachan@gmail.com (James Strachan) | |
+ */ | |
+class ConstructorAspect { | |
+ | |
+ final Matcher<? super Class<?>> classMatcher; | |
+ final List<ConstructorInterceptor> interceptors; | |
+ | |
+ ConstructorAspect(Matcher<? super Class<?>> classMatcher, | |
+ List<ConstructorInterceptor> interceptors) { | |
+ this.classMatcher = checkNotNull(classMatcher, "class matcher"); | |
+ this.interceptors = checkNotNull(interceptors, "interceptors"); | |
+ } | |
+ | |
+ boolean matches(Class<?> clazz) { | |
+ return classMatcher.matches(clazz); | |
+ } | |
+ | |
+ List<ConstructorInterceptor> interceptors() { | |
+ return interceptors; | |
+ } | |
+} | |
\ No newline at end of file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment