Created
July 7, 2014 17:55
-
-
Save gissuebot/d46c66a915cc5eefc076 to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 62, comment 36
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: src/com/google/inject/spi/CloseErrors.java | |
=================================================================== | |
--- src/com/google/inject/spi/CloseErrors.java Mon Oct 06 13:42:22 BST 2008 | |
+++ src/com/google/inject/spi/CloseErrors.java Mon Oct 06 13:42:22 BST 2008 | |
@@ -0,0 +1,40 @@ | |
+/** | |
+ * | |
+ * Licensed to the Apache Software Foundation (ASF) under one or more | |
+ * contributor license agreements. See the NOTICE file distributed with | |
+ * this work for additional information regarding copyright ownership. | |
+ * The ASF licenses this file to You under the Apache License, Version 2.0 | |
+ * (the "License"); you may not use this file except in compliance with | |
+ * the License. You may obtain a copy of the License at | |
+ * | |
+ * http://www.apache.org/licenses/LICENSE-2.0 | |
+ * | |
+ * Unless required by applicable law or agreed to in writing, software | |
+ * distributed under the License is distributed on an "AS IS" BASIS, | |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
+ * See the License for the specific language governing permissions and | |
+ * limitations under the License. | |
+ */ | |
+ | |
+package com.google.inject.spi; | |
+ | |
+/** | |
+ * A handler of exceptions when closing down resources. | |
+ * Implementations will typically create a collection of error messages so that all of the different | |
+ * close exceptions are collected together. | |
+ * | |
+ * @version $Revision: 1.1 $ | |
+ */ | |
+public interface CloseErrors { | |
+ | |
+ /** | |
+ * Notification of a close exception | |
+ * @param key the key of the object being closed which is usually a {@link com.google.inject.Key} | |
+ * or {@link String} | |
+ * @param object the object being closed | |
+ * @param cause the exception thrown when the close was attempted | |
+ */ | |
+ void closeError(Object key, Object object, Exception cause); | |
+ | |
+ void throwIfNecessary() throws CloseFailedException; | |
+} | |
Index: test/com/google/inject/AllTests.java | |
=================================================================== | |
--- test/com/google/inject/AllTests.java (revision 627) | |
+++ test/com/google/inject/AllTests.java Mon Oct 06 12:25:29 BST 2008 | |
@@ -59,6 +59,7 @@ | |
suite.addTestSuite(InjectionPointTest.class); | |
suite.addTestSuite(InjectorTest.class); | |
suite.addTestSuite(IntegrationTest.class); | |
+ suite.addTestSuite(CloseTest.class); | |
suite.addTestSuite(KeyTest.class); | |
suite.addTestSuite(LoggerInjectionTest.class); | |
suite.addTestSuite(ModuleTest.class); | |
Index: src/com/google/inject/AbstractModule.java | |
=================================================================== | |
--- src/com/google/inject/AbstractModule.java (revision 627) | |
+++ src/com/google/inject/AbstractModule.java Fri Oct 03 16:37:08 BST 2008 | |
@@ -26,6 +26,7 @@ | |
import com.google.inject.spi.TypeConverter; | |
import java.lang.annotation.Annotation; | |
import java.lang.reflect.Method; | |
+import org.aopalliance.intercept.ConstructorInterceptor; | |
import org.aopalliance.intercept.MethodInterceptor; | |
/** | |
@@ -167,6 +168,15 @@ | |
} | |
/** | |
+ * @see Binder#bindConstructorInterceptor(com.google.inject.matcher.Matcher, | |
+ * org.aopalliance.intercept.ConstructorInterceptor[]) | |
+ */ | |
+ protected void bindConstructorInterceptor(Matcher<? super Class<?>> classMatcher, | |
+ ConstructorInterceptor... interceptors) { | |
+ binder.bindConstructorInterceptor(classMatcher, interceptors); | |
+ } | |
+ | |
+ /** | |
* Adds a dependency from this module to {@code key}. When the injector is | |
* created, Guice will report an error if {@code key} cannot be injected. | |
* Note that this requirement may be satisfied by implicit binding, such as | |
Index: 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 Mon Oct 06 14:01:20 BST 2008 | |
@@ -23,19 +23,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.BindingTargetVisitor; | |
+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.spi.CloseErrors; | |
+import com.google.inject.spi.CloseFailedException; | |
import com.google.inject.util.Providers; | |
import java.lang.annotation.Annotation; | |
import java.lang.reflect.AnnotatedElement; | |
@@ -50,6 +59,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; | |
@@ -924,7 +935,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 +960,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 +1028,66 @@ | |
.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; | |
+ } | |
+} | |
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/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: jsr250/test/com/google/inject/jsr250/PostConstructTest.java | |
=================================================================== | |
--- jsr250/test/com/google/inject/jsr250/PostConstructTest.java Mon Oct 06 14:00:35 BST 2008 | |
+++ jsr250/test/com/google/inject/jsr250/PostConstructTest.java Mon Oct 06 14:00:35 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 PostConstructTest 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/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: 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: 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 Mon Oct 06 14:00:35 BST 2008 | |
+++ jsr250/src/com/google/inject/jsr250/Jsr250.java Mon Oct 06 14:00:35 BST 2008 | |
@@ -0,0 +1,43 @@ | |
+/** | |
+ * | |
+ * 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); | |
+ } | |
+} | |
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: 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: 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/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