Skip to content

Instantly share code, notes, and snippets.

@gissuebot
Created July 7, 2014 18:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gissuebot/71e1b85dcdc10e257a59 to your computer and use it in GitHub Desktop.
Save gissuebot/71e1b85dcdc10e257a59 to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 258, comment 0
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