You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2011/12/02 17:33:45 UTC
svn commit: r1209569 [20/50] - in /struts/struts2/branches/STRUTS_3_X:
apps/blank/src/main/java/example/ apps/blank/src/test/java/example/
apps/jboss-blank/src/main/java/example/
apps/jboss-blank/src/test/java/example/ apps/mailreader/src/main/java/mai...
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ContainerImpl.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ContainerImpl.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ContainerImpl.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ContainerImpl.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,621 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+import org.apache.struts2.xwork2.inject.util.ReferenceCache;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.Map.Entry;
+import java.security.AccessControlException;
+
+/**
+ * Default {@link Container} implementation.
+ *
+ * @see ContainerBuilder
+ * @author crazybob@google.com (Bob Lee)
+ */
+class ContainerImpl implements Container {
+
+ final Map<Key<?>, InternalFactory<?>> factories;
+ final Map<Class<?>,Set<String>> factoryNamesByType;
+
+ ContainerImpl(Map<Key<?>, InternalFactory<?>> factories) {
+ this.factories = factories;
+ Map<Class<?>,Set<String>> map = new HashMap<Class<?>,Set<String>>();
+ for (Key<?> key : factories.keySet()) {
+ Set<String> names = map.get(key.getType());
+ if (names == null) {
+ names = new HashSet<String>();
+ map.put(key.getType(), names);
+ }
+ names.add(key.getName());
+ }
+
+ for (Entry<Class<?>,Set<String>> entry : map.entrySet()) {
+ entry.setValue(Collections.unmodifiableSet(entry.getValue()));
+ }
+
+ this.factoryNamesByType = Collections.unmodifiableMap(map);
+ }
+
+ @SuppressWarnings("unchecked")
+ <T> InternalFactory<? extends T> getFactory(Key<T> key) {
+ return (InternalFactory<T>) factories.get(key);
+ }
+
+ /**
+ * Field and method injectors.
+ */
+ final Map<Class<?>, List<Injector>> injectors =
+ new ReferenceCache<Class<?>, List<Injector>>() {
+ @Override
+ protected List<Injector> create(Class<?> key) {
+ List<Injector> injectors = new ArrayList<Injector>();
+ addInjectors(key, injectors);
+ return injectors;
+ }
+ };
+
+ /**
+ * Recursively adds injectors for fields and methods from the given class to
+ * the given list. Injects parent classes before sub classes.
+ */
+ void addInjectors(Class clazz, List<Injector> injectors) {
+ if (clazz == Object.class) {
+ return;
+ }
+
+ // Add injectors for superclass first.
+ addInjectors(clazz.getSuperclass(), injectors);
+
+ // TODO (crazybob): Filter out overridden members.
+ addInjectorsForFields(clazz.getDeclaredFields(), false, injectors);
+ addInjectorsForMethods(clazz.getDeclaredMethods(), false, injectors);
+ }
+
+ void injectStatics(List<Class<?>> staticInjections) {
+ final List<Injector> injectors = new ArrayList<Injector>();
+
+ for (Class<?> clazz : staticInjections) {
+ addInjectorsForFields(clazz.getDeclaredFields(), true, injectors);
+ addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors);
+ }
+
+ callInContext(new ContextualCallable<Void>() {
+ public Void call(InternalContext context) {
+ for (Injector injector : injectors) {
+ injector.inject(context, null);
+ }
+ return null;
+ }
+ });
+ }
+
+ void addInjectorsForMethods(Method[] methods, boolean statics,
+ List<Injector> injectors) {
+ addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
+ new InjectorFactory<Method>() {
+ public Injector create(ContainerImpl container, Method method,
+ String name) throws MissingDependencyException {
+ return new MethodInjector(container, method, name);
+ }
+ });
+ }
+
+ void addInjectorsForFields(Field[] fields, boolean statics,
+ List<Injector> injectors) {
+ addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
+ new InjectorFactory<Field>() {
+ public Injector create(ContainerImpl container, Field field,
+ String name) throws MissingDependencyException {
+ return new FieldInjector(container, field, name);
+ }
+ });
+ }
+
+ <M extends Member & AnnotatedElement> void addInjectorsForMembers(
+ List<M> members, boolean statics, List<Injector> injectors,
+ InjectorFactory<M> injectorFactory) {
+ for (M member : members) {
+ if (isStatic(member) == statics) {
+ Inject inject = member.getAnnotation(Inject.class);
+ if (inject != null) {
+ try {
+ injectors.add(injectorFactory.create(this, member, inject.value()));
+ } catch (MissingDependencyException e) {
+ if (inject.required()) {
+ throw new DependencyException(e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ interface InjectorFactory<M extends Member & AnnotatedElement> {
+ Injector create(ContainerImpl container, M member, String name)
+ throws MissingDependencyException;
+ }
+
+ private boolean isStatic(Member member) {
+ return Modifier.isStatic(member.getModifiers());
+ }
+
+ static class FieldInjector implements Injector {
+
+ final Field field;
+ final InternalFactory<?> factory;
+ final ExternalContext<?> externalContext;
+
+ public FieldInjector(ContainerImpl container, Field field, String name)
+ throws MissingDependencyException {
+ this.field = field;
+ if (!field.isAccessible()) {
+ SecurityManager sm = System.getSecurityManager();
+ try {
+ if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
+ field.setAccessible(true);
+ } catch(AccessControlException e) {
+ throw new DependencyException("Security manager in use, could not access field: "
+ + field.getDeclaringClass().getName() + "(" + field.getName() + ")", e);
+ }
+ }
+
+ Key<?> key = Key.newInstance(field.getType(), name);
+ factory = container.getFactory(key);
+ if (factory == null) {
+ throw new MissingDependencyException(
+ "No mapping found for dependency " + key + " in " + field + ".");
+ }
+
+ this.externalContext = ExternalContext.newInstance(field, key, container);
+ }
+
+ public void inject(InternalContext context, Object o) {
+ ExternalContext<?> previous = context.getExternalContext();
+ context.setExternalContext(externalContext);
+ try {
+ field.set(o, factory.create(context));
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } finally {
+ context.setExternalContext(previous);
+ }
+ }
+ }
+
+ /**
+ * Gets parameter injectors.
+ *
+ * @param member to which the parameters belong
+ * @param annotations on the parameters
+ * @param parameterTypes parameter types
+ * @return injections
+ */
+ <M extends AccessibleObject & Member> ParameterInjector<?>[]
+ getParametersInjectors(M member,
+ Annotation[][] annotations, Class[] parameterTypes, String defaultName)
+ throws MissingDependencyException {
+ List<ParameterInjector<?>> parameterInjectors =
+ new ArrayList<ParameterInjector<?>>();
+
+ Iterator<Annotation[]> annotationsIterator =
+ Arrays.asList(annotations).iterator();
+ for (Class<?> parameterType : parameterTypes) {
+ Inject annotation = findInject(annotationsIterator.next());
+ String name = annotation == null ? defaultName : annotation.value();
+ Key<?> key = Key.newInstance(parameterType, name);
+ parameterInjectors.add(createParameterInjector(key, member));
+ }
+
+ return toArray(parameterInjectors);
+ }
+
+ <T> ParameterInjector<T> createParameterInjector(
+ Key<T> key, Member member) throws MissingDependencyException {
+ InternalFactory<? extends T> factory = getFactory(key);
+ if (factory == null) {
+ throw new MissingDependencyException(
+ "No mapping found for dependency " + key + " in " + member + ".");
+ }
+
+ ExternalContext<T> externalContext =
+ ExternalContext.newInstance(member, key, this);
+ return new ParameterInjector<T>(externalContext, factory);
+ }
+
+ @SuppressWarnings("unchecked")
+ private ParameterInjector<?>[] toArray(
+ List<ParameterInjector<?>> parameterInjections) {
+ return parameterInjections.toArray(
+ new ParameterInjector[parameterInjections.size()]);
+ }
+
+ /**
+ * Finds the {@link Inject} annotation in an array of annotations.
+ */
+ Inject findInject(Annotation[] annotations) {
+ for (Annotation annotation : annotations) {
+ if (annotation.annotationType() == Inject.class) {
+ return Inject.class.cast(annotation);
+ }
+ }
+ return null;
+ }
+
+ static class MethodInjector implements Injector {
+
+ final Method method;
+ final ParameterInjector<?>[] parameterInjectors;
+
+ public MethodInjector(ContainerImpl container, Method method, String name)
+ throws MissingDependencyException {
+ this.method = method;
+ if (!method.isAccessible()) {
+ SecurityManager sm = System.getSecurityManager();
+ try {
+ if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
+ method.setAccessible(true);
+ } catch(AccessControlException e) {
+ throw new DependencyException("Security manager in use, could not access method: "
+ + name + "(" + method.getName() + ")", e);
+ }
+ }
+
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ if (parameterTypes.length == 0) {
+ throw new DependencyException(
+ method + " has no parameters to inject.");
+ }
+ parameterInjectors = container.getParametersInjectors(
+ method, method.getParameterAnnotations(), parameterTypes, name);
+ }
+
+ public void inject(InternalContext context, Object o) {
+ try {
+ method.invoke(o, getParameters(method, context, parameterInjectors));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ Map<Class<?>, ConstructorInjector> constructors =
+ new ReferenceCache<Class<?>, ConstructorInjector>() {
+ @Override
+ @SuppressWarnings("unchecked")
+ protected ConstructorInjector<?> create(Class<?> implementation) {
+ return new ConstructorInjector(ContainerImpl.this, implementation);
+ }
+ };
+
+ static class ConstructorInjector<T> {
+
+ final Class<T> implementation;
+ final List<Injector> injectors;
+ final Constructor<T> constructor;
+ final ParameterInjector<?>[] parameterInjectors;
+
+ ConstructorInjector(ContainerImpl container, Class<T> implementation) {
+ this.implementation = implementation;
+
+ constructor = findConstructorIn(implementation);
+ if (!constructor.isAccessible()) {
+ SecurityManager sm = System.getSecurityManager();
+ try {
+ if (sm != null) sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
+ constructor.setAccessible(true);
+ } catch(AccessControlException e) {
+ throw new DependencyException("Security manager in use, could not access constructor: "
+ + implementation.getName() + "(" + constructor.getName() + ")", e);
+ }
+ }
+
+ MissingDependencyException exception = null;
+ Inject inject = null;
+ ParameterInjector<?>[] parameters = null;
+
+ try {
+ inject = constructor.getAnnotation(Inject.class);
+ parameters = constructParameterInjector(inject, container, constructor);
+ } catch (MissingDependencyException e) {
+ exception = e;
+ }
+ parameterInjectors = parameters;
+
+ if ( exception != null) {
+ if ( inject != null && inject.required()) {
+ throw new DependencyException(exception);
+ }
+ }
+ injectors = container.injectors.get(implementation);
+ }
+
+ ParameterInjector<?>[] constructParameterInjector(
+ Inject inject, ContainerImpl container, Constructor<T> constructor) throws MissingDependencyException{
+ return constructor.getParameterTypes().length == 0
+ ? null // default constructor.
+ : container.getParametersInjectors(
+ constructor,
+ constructor.getParameterAnnotations(),
+ constructor.getParameterTypes(),
+ inject.value()
+ );
+ }
+
+ @SuppressWarnings("unchecked")
+ private Constructor<T> findConstructorIn(Class<T> implementation) {
+ Constructor<T> found = null;
+ Constructor<T>[] declaredConstructors = (Constructor<T>[]) implementation
+ .getDeclaredConstructors();
+ for(Constructor<T> constructor : declaredConstructors) {
+ if (constructor.getAnnotation(Inject.class) != null) {
+ if (found != null) {
+ throw new DependencyException("More than one constructor annotated"
+ + " with @Inject found in " + implementation + ".");
+ }
+ found = constructor;
+ }
+ }
+ if (found != null) {
+ return found;
+ }
+
+ // If no annotated constructor is found, look for a no-arg constructor
+ // instead.
+ try {
+ return implementation.getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ throw new DependencyException("Could not find a suitable constructor"
+ + " in " + implementation.getName() + ".");
+ }
+ }
+
+ /**
+ * Construct an instance. Returns {@code Object} instead of {@code T}
+ * because it may return a proxy.
+ */
+ Object construct(InternalContext context, Class<? super T> expectedType) {
+ ConstructionContext<T> constructionContext =
+ context.getConstructionContext(this);
+
+ // We have a circular reference between constructors. Return a proxy.
+ if (constructionContext.isConstructing()) {
+ // TODO (crazybob): if we can't proxy this object, can we proxy the
+ // other object?
+ return constructionContext.createProxy(expectedType);
+ }
+
+ // If we're re-entering this factory while injecting fields or methods,
+ // return the same instance. This prevents infinite loops.
+ T t = constructionContext.getCurrentReference();
+ if (t != null) {
+ return t;
+ }
+
+ try {
+ // First time through...
+ constructionContext.startConstruction();
+ try {
+ Object[] parameters =
+ getParameters(constructor, context, parameterInjectors);
+ t = constructor.newInstance(parameters);
+ constructionContext.setProxyDelegates(t);
+ } finally {
+ constructionContext.finishConstruction();
+ }
+
+ // Store reference. If an injector re-enters this factory, they'll
+ // get the same reference.
+ constructionContext.setCurrentReference(t);
+
+ // Inject fields and methods.
+ for (Injector injector : injectors) {
+ injector.inject(context, t);
+ }
+
+ return t;
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ } finally {
+ constructionContext.removeCurrentReference();
+ }
+ }
+ }
+
+ static class ParameterInjector<T> {
+
+ final ExternalContext<T> externalContext;
+ final InternalFactory<? extends T> factory;
+
+ public ParameterInjector(ExternalContext<T> externalContext,
+ InternalFactory<? extends T> factory) {
+ this.externalContext = externalContext;
+ this.factory = factory;
+ }
+
+ T inject(Member member, InternalContext context) {
+ ExternalContext<?> previous = context.getExternalContext();
+ context.setExternalContext(externalContext);
+ try {
+ return factory.create(context);
+ } finally {
+ context.setExternalContext(previous);
+ }
+ }
+ }
+
+ private static Object[] getParameters(Member member, InternalContext context,
+ ParameterInjector[] parameterInjectors) {
+ if (parameterInjectors == null) {
+ return null;
+ }
+
+ Object[] parameters = new Object[parameterInjectors.length];
+ for (int i = 0; i < parameters.length; i++) {
+ parameters[i] = parameterInjectors[i].inject(member, context);
+ }
+ return parameters;
+ }
+
+ void inject(Object o, InternalContext context) {
+ List<Injector> injectors = this.injectors.get(o.getClass());
+ for (Injector injector : injectors) {
+ injector.inject(context, o);
+ }
+ }
+
+ <T> T inject(Class<T> implementation, InternalContext context) {
+ try {
+ ConstructorInjector<T> constructor = getConstructor(implementation);
+ return implementation.cast(
+ constructor.construct(context, implementation));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ <T> T getInstance(Class<T> type, String name, InternalContext context) {
+ ExternalContext<?> previous = context.getExternalContext();
+ Key<T> key = Key.newInstance(type, name);
+ context.setExternalContext(ExternalContext.newInstance(null, key, this));
+ try {
+ InternalFactory o = getFactory(key);
+ if (o != null) {
+ return getFactory(key).create(context);
+ } else {
+ return null;
+ }
+ } finally {
+ context.setExternalContext(previous);
+ }
+ }
+
+ <T> T getInstance(Class<T> type, InternalContext context) {
+ return getInstance(type, DEFAULT_NAME, context);
+ }
+
+ public void inject(final Object o) {
+ callInContext(new ContextualCallable<Void>() {
+ public Void call(InternalContext context) {
+ inject(o, context);
+ return null;
+ }
+ });
+ }
+
+ public <T> T inject(final Class<T> implementation) {
+ return callInContext(new ContextualCallable<T>() {
+ public T call(InternalContext context) {
+ return inject(implementation, context);
+ }
+ });
+ }
+
+ public <T> T getInstance(final Class<T> type, final String name) {
+ return callInContext(new ContextualCallable<T>() {
+ public T call(InternalContext context) {
+ return getInstance(type, name, context);
+ }
+ });
+ }
+
+ public <T> T getInstance(final Class<T> type) {
+ return callInContext(new ContextualCallable<T>() {
+ public T call(InternalContext context) {
+ return getInstance(type, context);
+ }
+ });
+ }
+
+ public Set<String> getInstanceNames(final Class<?> type) {
+ return factoryNamesByType.get(type);
+ }
+
+ ThreadLocal<Object[]> localContext =
+ new ThreadLocal<Object[]>() {
+ @Override
+ protected Object[] initialValue() {
+ return new Object[1];
+ }
+ };
+
+ /**
+ * Looks up thread local context. Creates (and removes) a new context if
+ * necessary.
+ */
+ <T> T callInContext(ContextualCallable<T> callable) {
+ Object[] reference = localContext.get();
+ if (reference[0] == null) {
+ reference[0] = new InternalContext(this);
+ try {
+ return callable.call((InternalContext)reference[0]);
+ } finally {
+ // Only remove the context if this call created it.
+ reference[0] = null;
+ }
+ } else {
+ // Someone else will clean up this context.
+ return callable.call((InternalContext)reference[0]);
+ }
+ }
+
+ interface ContextualCallable<T> {
+ T call(InternalContext context);
+ }
+
+ /**
+ * Gets a constructor function for a given implementation class.
+ */
+ @SuppressWarnings("unchecked")
+ <T> ConstructorInjector<T> getConstructor(Class<T> implementation) {
+ return constructors.get(implementation);
+ }
+
+ final ThreadLocal<Object> localScopeStrategy =
+ new ThreadLocal<Object>();
+
+ public void setScopeStrategy(Scope.Strategy scopeStrategy) {
+ this.localScopeStrategy.set(scopeStrategy);
+ }
+
+ public void removeScopeStrategy() {
+ this.localScopeStrategy.remove();
+ }
+
+ /**
+ * Injects a field or method in a given object.
+ */
+ interface Injector extends Serializable {
+ void inject(InternalContext context, Object o);
+ }
+
+ static class MissingDependencyException extends Exception {
+
+ MissingDependencyException(String message) {
+ super(message);
+ }
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Context.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Context.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Context.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Context.java Fri Dec 2 16:33:03 2011
@@ -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 org.apache.struts2.xwork2.inject;
+
+import java.lang.reflect.Member;
+
+/**
+ * Context of the current injection.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public interface Context {
+
+ /**
+ * Gets the {@link Container}.
+ */
+ Container getContainer();
+
+ /**
+ * Gets the current scope strategy. See {@link
+ * Container#setScopeStrategy(Scope.Strategy)}.
+ *
+ * @throws IllegalStateException if no strategy has been set
+ */
+ Scope.Strategy getScopeStrategy();
+
+ /**
+ * Gets the field, method or constructor which is being injected. Returns
+ * {@code null} if the object currently being constructed is pre-loaded as
+ * a singleton or requested from {@link Container#getInstance(Class)}.
+ */
+ Member getMember();
+
+ /**
+ * Gets the type of the field or parameter which is being injected.
+ */
+ Class<?> getType();
+
+ /**
+ * Gets the name of the injection specified by {@link Inject#value()}.
+ */
+ String getName();
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/DependencyException.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/DependencyException.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/DependencyException.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/DependencyException.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,37 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+/**
+ * Thrown when a dependency is misconfigured.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public class DependencyException extends RuntimeException {
+
+ public DependencyException(String message) {
+ super(message);
+ }
+
+ public DependencyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DependencyException(Throwable cause) {
+ super(cause);
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ExternalContext.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ExternalContext.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ExternalContext.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/ExternalContext.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,74 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+import java.lang.reflect.Member;
+import java.util.LinkedHashMap;
+
+/**
+ * An immutable snapshot of the current context which is safe to
+ * expose to client code.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+class ExternalContext<T> implements Context {
+
+ final Member member;
+ final Key<T> key;
+ final ContainerImpl container;
+
+ public ExternalContext(Member member, Key<T> key, ContainerImpl container) {
+ this.member = member;
+ this.key = key;
+ this.container = container;
+ }
+
+ public Class<T> getType() {
+ return key.getType();
+ }
+
+ public Scope.Strategy getScopeStrategy() {
+ return (Scope.Strategy) container.localScopeStrategy.get();
+ }
+
+ public Container getContainer() {
+ return container;
+ }
+
+ public Member getMember() {
+ return member;
+ }
+
+ public String getName() {
+ return key.getName();
+ }
+
+ @Override
+ public String toString() {
+ return "Context" + new LinkedHashMap<String, Object>() {{
+ put("member", member);
+ put("type", getType());
+ put("name", getName());
+ put("container", container);
+ }}.toString();
+ }
+
+ static <T> ExternalContext<T> newInstance(Member member, Key<T> key,
+ ContainerImpl container) {
+ return new ExternalContext<T>(member, key, container);
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Factory.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Factory.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Factory.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Factory.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,34 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+/**
+ * A custom factory. Creates objects which will be injected.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public interface Factory<T> {
+
+ /**
+ * Creates an object to be injected.
+ *
+ * @param context of this injection
+ * @return instance to be injected
+ * @throws Exception if unable to create object
+ */
+ T create(Context context) throws Exception;
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Inject.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Inject.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Inject.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Inject.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,46 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+import static org.apache.struts2.xwork2.inject.Container.DEFAULT_NAME;
+
+import static java.lang.annotation.ElementType.*;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * <p>Annotates members and parameters which should have their value[s]
+ * injected.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+@Target({METHOD, CONSTRUCTOR, FIELD, PARAMETER})
+@Retention(RUNTIME)
+public @interface Inject {
+
+ /**
+ * Dependency name. Defaults to {@link Container#DEFAULT_NAME}.
+ */
+ String value() default DEFAULT_NAME;
+
+ /**
+ * Whether or not injection is required. Applicable only to methods and
+ * fields (not constructors or parameters).
+ */
+ boolean required() default true;
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalContext.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalContext.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalContext.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalContext.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,80 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Internal context. Used to coordinate injections and support circular
+ * dependencies.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+class InternalContext {
+
+ final ContainerImpl container;
+ final Map<Object, ConstructionContext<?>> constructionContexts =
+ new HashMap<Object, ConstructionContext<?>>();
+ Scope.Strategy scopeStrategy;
+ ExternalContext<?> externalContext;
+
+ InternalContext(ContainerImpl container) {
+ this.container = container;
+ }
+
+ public Container getContainer() {
+ return container;
+ }
+
+ ContainerImpl getContainerImpl() {
+ return container;
+ }
+
+ Scope.Strategy getScopeStrategy() {
+ if (scopeStrategy == null) {
+ scopeStrategy = (Scope.Strategy) container.localScopeStrategy.get();
+
+ if (scopeStrategy == null) {
+ throw new IllegalStateException("Scope strategy not set. "
+ + "Please call Container.setScopeStrategy().");
+ }
+ }
+
+ return scopeStrategy;
+ }
+
+ @SuppressWarnings("unchecked")
+ <T> ConstructionContext<T> getConstructionContext(Object key) {
+ ConstructionContext<T> constructionContext =
+ (ConstructionContext<T>) constructionContexts.get(key);
+ if (constructionContext == null) {
+ constructionContext = new ConstructionContext<T>();
+ constructionContexts.put(key, constructionContext);
+ }
+ return constructionContext;
+ }
+
+ @SuppressWarnings("unchecked")
+ <T> ExternalContext<T> getExternalContext() {
+ return (ExternalContext<T>) externalContext;
+ }
+
+ void setExternalContext(ExternalContext<?> externalContext) {
+ this.externalContext = externalContext;
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalFactory.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalFactory.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalFactory.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/InternalFactory.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,35 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+import java.io.Serializable;
+
+/**
+ * Creates objects which will be injected.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+interface InternalFactory<T> extends Serializable {
+
+ /**
+ * Creates an object to be injected.
+ *
+ * @param context of this injection
+ * @return instance to be injected
+ */
+ T create(InternalContext context);
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Key.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Key.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Key.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Key.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,77 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+/**
+ * Dependency mapping key. Uniquely identified by the required type and name.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+class Key<T> {
+
+ final Class<T> type;
+ final String name;
+ final int hashCode;
+
+ private Key(Class<T> type, String name) {
+ if (type == null) {
+ throw new NullPointerException("Type is null.");
+ }
+ if (name == null) {
+ throw new NullPointerException("Name is null.");
+ }
+
+ this.type = type;
+ this.name = name;
+
+ hashCode = type.hashCode() * 31 + name.hashCode();
+ }
+
+ Class<T> getType() {
+ return type;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Key)) {
+ return false;
+ }
+ if (o == this) {
+ return true;
+ }
+ Key other = (Key) o;
+ return name.equals(other.name) && type.equals(other.type);
+ }
+
+ @Override
+ public String toString() {
+ return "[type=" + type.getName() + ", name='" + name + "']";
+ }
+
+ static <T> Key<T> newInstance(Class<T> type, String name) {
+ return new Key<T>(type, name);
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scope.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scope.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scope.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scope.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,217 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Scope of an injected objects.
+ *
+ * @author crazybob
+ */
+public enum Scope {
+
+ /**
+ * One instance per injection.
+ */
+ DEFAULT {
+ @Override
+ <T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name,
+ InternalFactory<? extends T> factory) {
+ return factory;
+ }
+ },
+
+ /**
+ * One instance per container.
+ */
+ SINGLETON {
+ @Override
+ <T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name,
+ final InternalFactory<? extends T> factory) {
+ return new InternalFactory<T>() {
+ T instance;
+ public T create(InternalContext context) {
+ synchronized (context.getContainer()) {
+ if (instance == null) {
+ instance = factory.create(context);
+ }
+ return instance;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return factory.toString();
+ }
+ };
+ }
+ },
+
+ /**
+ * One instance per thread.
+ *
+ * <p><b>Note:</b> if a thread local object strongly references its {@link
+ * Container}, neither the {@code Container} nor the object will be
+ * eligible for garbage collection, i.e. memory leak.
+ */
+ THREAD {
+ @Override
+ <T> InternalFactory<? extends T> scopeFactory(Class<T> type, String name,
+ final InternalFactory<? extends T> factory) {
+ return new InternalFactory<T>() {
+ final ThreadLocal<T> threadLocal = new ThreadLocal<T>();
+ public T create(final InternalContext context) {
+ T t = threadLocal.get();
+ if (t == null) {
+ t = factory.create(context);
+ threadLocal.set(t);
+ }
+ return t;
+ }
+
+ @Override
+ public String toString() {
+ return factory.toString();
+ }
+ };
+ }
+ },
+
+ /**
+ * One instance per request.
+ */
+ REQUEST {
+ @Override
+ <T> InternalFactory<? extends T> scopeFactory(final Class<T> type,
+ final String name, final InternalFactory<? extends T> factory) {
+ return new InternalFactory<T>() {
+ public T create(InternalContext context) {
+ Strategy strategy = context.getScopeStrategy();
+ try {
+ return strategy.findInRequest(
+ type, name, toCallable(context, factory));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return factory.toString();
+ }
+ };
+ }
+ },
+
+ /**
+ * One instance per session.
+ */
+ SESSION {
+ @Override
+ <T> InternalFactory<? extends T> scopeFactory(final Class<T> type,
+ final String name, final InternalFactory<? extends T> factory) {
+ return new InternalFactory<T>() {
+ public T create(InternalContext context) {
+ Strategy strategy = context.getScopeStrategy();
+ try {
+ return strategy.findInSession(
+ type, name, toCallable(context, factory));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return factory.toString();
+ }
+ };
+ }
+ },
+
+ /**
+ * One instance per wizard.
+ */
+ WIZARD {
+ @Override
+ <T> InternalFactory<? extends T> scopeFactory(final Class<T> type,
+ final String name, final InternalFactory<? extends T> factory) {
+ return new InternalFactory<T>() {
+ public T create(InternalContext context) {
+ Strategy strategy = context.getScopeStrategy();
+ try {
+ return strategy.findInWizard(
+ type, name, toCallable(context, factory));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return factory.toString();
+ }
+ };
+ }
+ };
+
+ <T> Callable<? extends T> toCallable(final InternalContext context,
+ final InternalFactory<? extends T> factory) {
+ return new Callable<T>() {
+ public T call() throws Exception {
+ return factory.create(context);
+ }
+ };
+ }
+
+ /**
+ * Wraps factory with scoping logic.
+ */
+ abstract <T> InternalFactory<? extends T> scopeFactory(
+ Class<T> type, String name, InternalFactory<? extends T> factory);
+
+ /**
+ * Pluggable scoping strategy. Enables users to provide custom
+ * implementations of request, session, and wizard scopes. Implement and
+ * pass to {@link
+ * Container#setScopeStrategy(Scope.Strategy)}.
+ */
+ public interface Strategy {
+
+ /**
+ * Finds an object for the given type and name in the request scope.
+ * Creates a new object if necessary using the given factory.
+ */
+ <T> T findInRequest(Class<T> type, String name,
+ Callable<? extends T> factory) throws Exception;
+
+ /**
+ * Finds an object for the given type and name in the session scope.
+ * Creates a new object if necessary using the given factory.
+ */
+ <T> T findInSession(Class<T> type, String name,
+ Callable<? extends T> factory) throws Exception;
+
+ /**
+ * Finds an object for the given type and name in the wizard scope.
+ * Creates a new object if necessary using the given factory.
+ */
+ <T> T findInWizard(Class<T> type, String name,
+ Callable<? extends T> factory) throws Exception;
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scoped.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scoped.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scoped.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/Scoped.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,37 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ * Annotates a scoped implementation class.
+ *
+ * @author crazybob
+ */
+@Target(ElementType.TYPE)
+@Retention(RUNTIME)
+public @interface Scoped {
+
+ /**
+ * Scope.
+ */
+ Scope value();
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizablePhantomReference.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizablePhantomReference.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizablePhantomReference.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizablePhantomReference.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,35 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+import java.lang.ref.PhantomReference;
+
+/**
+ * Phantom reference with a {@link FinalizableReference#finalizeReferent() finalizeReferent()} method which a
+ * background thread invokes after the garbage collector reclaims the
+ * referent. This is a simpler alternative to using a {@link
+ * java.lang.ref.ReferenceQueue}.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public abstract class FinalizablePhantomReference<T>
+ extends PhantomReference<T> implements FinalizableReference {
+
+ protected FinalizablePhantomReference(T referent) {
+ super(referent, FinalizableReferenceQueue.getInstance());
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReference.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReference.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReference.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReference.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,32 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+/**
+ * Package-private interface implemented by references that have code to run
+ * after garbage collection of their referents.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+interface FinalizableReference {
+
+ /**
+ * Invoked on a background thread after the referent has been garbage
+ * collected.
+ */
+ void finalizeReferent();
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReferenceQueue.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReferenceQueue.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReferenceQueue.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableReferenceQueue.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,77 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Starts a background thread that cleans up after reclaimed referents.
+ *
+ * @author Bob Lee (crazybob@google.com)
+ */
+class FinalizableReferenceQueue extends ReferenceQueue<Object> {
+
+ private static final Logger logger =
+ Logger.getLogger(FinalizableReferenceQueue.class.getName());
+
+ private FinalizableReferenceQueue() {}
+
+ void cleanUp(Reference reference) {
+ try {
+ ((FinalizableReference) reference).finalizeReferent();
+ } catch (Throwable t) {
+ deliverBadNews(t);
+ }
+ }
+
+ void deliverBadNews(Throwable t) {
+ logger.log(Level.SEVERE, "Error cleaning up after reference.", t);
+ }
+
+ void start() {
+ Thread thread = new Thread("FinalizableReferenceQueue") {
+ @Override
+ public void run() {
+ while (true) {
+ try {
+ cleanUp(remove());
+ } catch (InterruptedException e) { /* ignore */ }
+ }
+ }
+ };
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ static ReferenceQueue<Object> instance = createAndStart();
+
+ static FinalizableReferenceQueue createAndStart() {
+ FinalizableReferenceQueue queue = new FinalizableReferenceQueue();
+ queue.start();
+ return queue;
+ }
+
+ /**
+ * Gets instance.
+ */
+ public static ReferenceQueue<Object> getInstance() {
+ return instance;
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableSoftReference.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableSoftReference.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableSoftReference.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableSoftReference.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,34 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+import java.lang.ref.SoftReference;
+
+/**
+ * Soft reference with a {@link FinalizableReference#finalizeReferent() finalizeReferent()} method which a background
+ * thread invokes after the garbage collector reclaims the referent. This is a
+ * simpler alternative to using a {@link java.lang.ref.ReferenceQueue}.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public abstract class FinalizableSoftReference<T> extends SoftReference<T>
+ implements FinalizableReference {
+
+ protected FinalizableSoftReference(T referent) {
+ super(referent, FinalizableReferenceQueue.getInstance());
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableWeakReference.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableWeakReference.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableWeakReference.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/FinalizableWeakReference.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,34 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Weak reference with a {@link FinalizableReference#finalizeReferent() finalizeReferent()} method which a background
+ * thread invokes after the garbage collector reclaims the referent. This is a
+ * simpler alternative to using a {@link java.lang.ref.ReferenceQueue}.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public abstract class FinalizableWeakReference<T> extends WeakReference<T>
+ implements FinalizableReference {
+
+ protected FinalizableWeakReference(T referent) {
+ super(referent, FinalizableReferenceQueue.getInstance());
+ }
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/Function.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/Function.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/Function.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/Function.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,44 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+/**
+ * A Function provides a transformation on an object and returns the resulting
+ * object. For example, a {@code StringToIntegerFunction} may implement
+ * <code>Function<String,Integer></code> and transform integers in String
+ * format to Integer format.
+ *
+ * <p>The transformation on the source object does not necessarily result in
+ * an object of a different type. For example, a
+ * {@code FarenheitToCelciusFunction} may implement
+ * <code>Function<Float,Float></code>.
+ *
+ * <p>Implementors of Function which may cause side effects upon evaluation are
+ * strongly encouraged to state this fact clearly in their API documentation.
+ */
+public interface Function<F,T> {
+
+ /**
+ * Applies the function to an object of type {@code F}, resulting in an object
+ * of type {@code T}. Note that types {@code F} and {@code T} may or may not
+ * be the same.
+ *
+ * @param from The source object.
+ * @return The resulting object.
+ */
+ T apply(F from);
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceCache.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceCache.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceCache.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceCache.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,184 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+import static org.apache.struts2.xwork2.inject.util.ReferenceType.STRONG;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.concurrent.*;
+
+/**
+ * Extends {@link ReferenceMap} to support lazy loading values by overriding
+ * {@link #create(Object)}.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+public abstract class ReferenceCache<K, V> extends ReferenceMap<K, V> {
+
+ private static final long serialVersionUID = 0;
+
+ transient ConcurrentMap<Object, Future<V>> futures =
+ new ConcurrentHashMap<Object, Future<V>>();
+
+ transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<Future<V>>();
+
+ public ReferenceCache(ReferenceType keyReferenceType,
+ ReferenceType valueReferenceType) {
+ super(keyReferenceType, valueReferenceType);
+ }
+
+ /**
+ * Equivalent to {@code new ReferenceCache(STRONG, STRONG)}.
+ */
+ public ReferenceCache() {
+ super(STRONG, STRONG);
+ }
+
+ /**
+ * Override to lazy load values. Use as an alternative to {@link
+ * #put(Object,Object)}. Invoked by getter if value isn't already cached.
+ * Must not return {@code null}. This method will not be called again until
+ * the garbage collector reclaims the returned value.
+ */
+ protected abstract V create(K key);
+
+ V internalCreate(K key) {
+ try {
+ FutureTask<V> futureTask = new FutureTask<V>(
+ new CallableCreate(key));
+
+ // use a reference so we get the same equality semantics.
+ Object keyReference = referenceKey(key);
+ Future<V> future = futures.putIfAbsent(keyReference, futureTask);
+ if (future == null) {
+ // winning thread.
+ try {
+ if (localFuture.get() != null) {
+ throw new IllegalStateException(
+ "Nested creations within the same cache are not allowed.");
+ }
+ localFuture.set(futureTask);
+ futureTask.run();
+ V value = futureTask.get();
+ putStrategy().execute(this,
+ keyReference, referenceValue(keyReference, value));
+ return value;
+ } finally {
+ localFuture.remove();
+ futures.remove(keyReference);
+ }
+ } else {
+ // wait for winning thread.
+ return future.get();
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ }
+ throw new RuntimeException(cause);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * If this map does not contain an entry for the given key and {@link
+ * #create(Object)} has been overridden, this method will create a new
+ * value, put it in the map, and return it.
+ *
+ * @throws NullPointerException if {@link #create(Object)} returns null.
+ * @throws java.util.concurrent.CancellationException if the creation is
+ * cancelled. See {@link #cancel()}.
+ */
+ @SuppressWarnings("unchecked")
+ @Override public V get(final Object key) {
+ V value = super.get(key);
+ return (value == null)
+ ? internalCreate((K) key)
+ : value;
+ }
+
+ /**
+ * Cancels the current {@link #create(Object)}. Throws {@link
+ * java.util.concurrent.CancellationException} to all clients currently
+ * blocked on {@link #get(Object)}.
+ */
+ protected void cancel() {
+ Future<V> future = localFuture.get();
+ if (future == null) {
+ throw new IllegalStateException("Not in create().");
+ }
+ future.cancel(false);
+ }
+
+ class CallableCreate implements Callable<V> {
+
+ K key;
+
+ public CallableCreate(K key) {
+ this.key = key;
+ }
+
+ public V call() {
+ // try one more time (a previous future could have come and gone.)
+ V value = internalGet(key);
+ if (value != null) {
+ return value;
+ }
+
+ // create value.
+ value = create(key);
+ if (value == null) {
+ throw new NullPointerException(
+ "create(K) returned null for: " + key);
+ }
+ return value;
+ }
+ }
+
+ /**
+ * Returns a {@code ReferenceCache} delegating to the specified {@code
+ * function}. The specified function must not return {@code null}.
+ */
+ public static <K, V> ReferenceCache<K, V> of(
+ ReferenceType keyReferenceType,
+ ReferenceType valueReferenceType,
+ final Function<? super K, ? extends V> function) {
+ ensureNotNull(function);
+ return new ReferenceCache<K, V>(keyReferenceType, valueReferenceType) {
+ @Override
+ protected V create(K key) {
+ return function.apply(key);
+ }
+ private static final long serialVersionUID = 0;
+ };
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ this.futures = new ConcurrentHashMap<Object, Future<V>>();
+ this.localFuture = new ThreadLocal<Future<V>>();
+ }
+
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceMap.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceMap.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceMap.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceMap.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,614 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * Concurrent hash map that wraps keys and/or values in soft or weak
+ * references. Does not support null keys or values. Uses identity equality
+ * for weak and soft keys.
+ *
+ * <p>The concurrent semantics of {@link ConcurrentHashMap} combined with the
+ * fact that the garbage collector can asynchronously reclaim and clean up
+ * after keys and values at any time can lead to some racy semantics. For
+ * example, {@link #size()} returns an upper bound on the size, i.e. the actual
+ * size may be smaller in cases where the key or value has been reclaimed but
+ * the map entry has not been cleaned up yet.
+ *
+ * <p>Another example: If {@link #get(Object)} cannot find an existing entry
+ * for a key, it will try to create one. This operation is not atomic. One
+ * thread could {@link #put(Object, Object)} a value between the time another
+ * thread running {@code get()} checks for an entry and decides to create one.
+ * In this case, the newly created value will replace the put value in the
+ * map. Also, two threads running {@code get()} concurrently can potentially
+ * create duplicate values for a given key.
+ *
+ * <p>In other words, this class is great for caching but not atomicity.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ */
+@SuppressWarnings("unchecked")
+public class ReferenceMap<K, V> implements Map<K, V>, Serializable {
+
+ private static final long serialVersionUID = 0;
+
+ transient ConcurrentMap<Object, Object> delegate;
+
+ final ReferenceType keyReferenceType;
+ final ReferenceType valueReferenceType;
+
+ /**
+ * Concurrent hash map that wraps keys and/or values based on specified
+ * reference types.
+ *
+ * @param keyReferenceType key reference type
+ * @param valueReferenceType value reference type
+ */
+ public ReferenceMap(ReferenceType keyReferenceType,
+ ReferenceType valueReferenceType) {
+ ensureNotNull(keyReferenceType, valueReferenceType);
+
+ if (keyReferenceType == ReferenceType.PHANTOM
+ || valueReferenceType == ReferenceType.PHANTOM) {
+ throw new IllegalArgumentException("Phantom references not supported.");
+ }
+
+ this.delegate = new ConcurrentHashMap<Object, Object>();
+ this.keyReferenceType = keyReferenceType;
+ this.valueReferenceType = valueReferenceType;
+ }
+
+ V internalGet(K key) {
+ Object valueReference = delegate.get(makeKeyReferenceAware(key));
+ return valueReference == null
+ ? null
+ : (V) dereferenceValue(valueReference);
+ }
+
+ public V get(final Object key) {
+ ensureNotNull(key);
+ return internalGet((K) key);
+ }
+
+ V execute(Strategy strategy, K key, V value) {
+ ensureNotNull(key, value);
+ Object keyReference = referenceKey(key);
+ Object valueReference = strategy.execute(
+ this,
+ keyReference,
+ referenceValue(keyReference, value)
+ );
+ return valueReference == null ? null
+ : (V) dereferenceValue(valueReference);
+ }
+
+ public V put(K key, V value) {
+ return execute(putStrategy(), key, value);
+ }
+
+ public V remove(Object key) {
+ ensureNotNull(key);
+ Object referenceAwareKey = makeKeyReferenceAware(key);
+ Object valueReference = delegate.remove(referenceAwareKey);
+ return valueReference == null ? null
+ : (V) dereferenceValue(valueReference);
+ }
+
+ public int size() {
+ return delegate.size();
+ }
+
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ public boolean containsKey(Object key) {
+ ensureNotNull(key);
+ Object referenceAwareKey = makeKeyReferenceAware(key);
+ return delegate.containsKey(referenceAwareKey);
+ }
+
+ public boolean containsValue(Object value) {
+ ensureNotNull(value);
+ for (Object valueReference : delegate.values()) {
+ if (value.equals(dereferenceValue(valueReference))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void putAll(Map<? extends K, ? extends V> t) {
+ for (Map.Entry<? extends K, ? extends V> entry : t.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ public void clear() {
+ delegate.clear();
+ }
+
+ /**
+ * Returns an unmodifiable set view of the keys in this map. As this method
+ * creates a defensive copy, the performance is O(n).
+ */
+ public Set<K> keySet() {
+ return Collections.unmodifiableSet(
+ dereferenceKeySet(delegate.keySet()));
+ }
+
+ /**
+ * Returns an unmodifiable set view of the values in this map. As this
+ * method creates a defensive copy, the performance is O(n).
+ */
+ public Collection<V> values() {
+ return Collections.unmodifiableCollection(
+ dereferenceValues(delegate.values()));
+ }
+
+ public V putIfAbsent(K key, V value) {
+ // TODO (crazybob) if the value has been gc'ed but the entry hasn't been
+ // cleaned up yet, this put will fail.
+ return execute(putIfAbsentStrategy(), key, value);
+ }
+
+ public boolean remove(Object key, Object value) {
+ ensureNotNull(key, value);
+ Object referenceAwareKey = makeKeyReferenceAware(key);
+ Object referenceAwareValue = makeValueReferenceAware(value);
+ return delegate.remove(referenceAwareKey, referenceAwareValue);
+ }
+
+ public boolean replace(K key, V oldValue, V newValue) {
+ ensureNotNull(key, oldValue, newValue);
+ Object keyReference = referenceKey(key);
+
+ Object referenceAwareOldValue = makeValueReferenceAware(oldValue);
+ return delegate.replace(
+ keyReference,
+ referenceAwareOldValue,
+ referenceValue(keyReference, newValue)
+ );
+ }
+
+ public V replace(K key, V value) {
+ // TODO (crazybob) if the value has been gc'ed but the entry hasn't been
+ // cleaned up yet, this will succeed when it probably shouldn't.
+ return execute(replaceStrategy(), key, value);
+ }
+
+ /**
+ * Returns an unmodifiable set view of the entries in this map. As this
+ * method creates a defensive copy, the performance is O(n).
+ */
+ public Set<Map.Entry<K, V>> entrySet() {
+ Set<Map.Entry<K, V>> entrySet = new HashSet<Map.Entry<K, V>>();
+ for (Map.Entry<Object, Object> entry : delegate.entrySet()) {
+ Map.Entry<K, V> dereferenced = dereferenceEntry(entry);
+ if (dereferenced != null) {
+ entrySet.add(dereferenced);
+ }
+ }
+ return Collections.unmodifiableSet(entrySet);
+ }
+
+ /**
+ * Dereferences an entry. Returns null if the key or value has been gc'ed.
+ */
+ Entry dereferenceEntry(Map.Entry<Object, Object> entry) {
+ K key = dereferenceKey(entry.getKey());
+ V value = dereferenceValue(entry.getValue());
+ return (key == null || value == null)
+ ? null
+ : new Entry(key, value);
+ }
+
+ /**
+ * Creates a reference for a key.
+ */
+ Object referenceKey(K key) {
+ switch (keyReferenceType) {
+ case STRONG: return key;
+ case SOFT: return new SoftKeyReference(key);
+ case WEAK: return new WeakKeyReference(key);
+ default: throw new AssertionError();
+ }
+ }
+
+ /**
+ * Converts a reference to a key.
+ */
+ K dereferenceKey(Object o) {
+ return (K) dereference(keyReferenceType, o);
+ }
+
+ /**
+ * Converts a reference to a value.
+ */
+ V dereferenceValue(Object o) {
+ return (V) dereference(valueReferenceType, o);
+ }
+
+ /**
+ * Returns the refererent for reference given its reference type.
+ */
+ Object dereference(ReferenceType referenceType, Object reference) {
+ return referenceType == ReferenceType.STRONG ? reference : ((Reference) reference).get();
+ }
+
+ /**
+ * Creates a reference for a value.
+ */
+ Object referenceValue(Object keyReference, Object value) {
+ switch (valueReferenceType) {
+ case STRONG: return value;
+ case SOFT: return new SoftValueReference(keyReference, value);
+ case WEAK: return new WeakValueReference(keyReference, value);
+ default: throw new AssertionError();
+ }
+ }
+
+ /**
+ * Dereferences a set of key references.
+ */
+ Set<K> dereferenceKeySet(Set keyReferences) {
+ return keyReferenceType == ReferenceType.STRONG
+ ? keyReferences
+ : dereferenceCollection(keyReferenceType, keyReferences, new HashSet());
+ }
+
+ /**
+ * Dereferences a collection of value references.
+ */
+ Collection<V> dereferenceValues(Collection valueReferences) {
+ return valueReferenceType == ReferenceType.STRONG
+ ? valueReferences
+ : dereferenceCollection(valueReferenceType, valueReferences,
+ new ArrayList(valueReferences.size()));
+ }
+
+ /**
+ * Wraps key so it can be compared to a referenced key for equality.
+ */
+ Object makeKeyReferenceAware(Object o) {
+ return keyReferenceType == ReferenceType.STRONG ? o : new KeyReferenceAwareWrapper(o);
+ }
+
+ /**
+ * Wraps value so it can be compared to a referenced value for equality.
+ */
+ Object makeValueReferenceAware(Object o) {
+ return valueReferenceType == ReferenceType.STRONG ? o : new ReferenceAwareWrapper(o);
+ }
+
+ /**
+ * Dereferences elements in {@code in} using
+ * {@code referenceType} and puts them in {@code out}. Returns
+ * {@code out}.
+ */
+ <T extends Collection<Object>> T dereferenceCollection(
+ ReferenceType referenceType, T in, T out) {
+ for (Object reference : in) {
+ out.add(dereference(referenceType, reference));
+ }
+ return out;
+ }
+
+ /**
+ * Marker interface to differentiate external and internal references.
+ */
+ interface InternalReference {}
+
+ static int keyHashCode(Object key) {
+ return System.identityHashCode(key);
+ }
+
+ /**
+ * Tests weak and soft references for identity equality. Compares references
+ * to other references and wrappers. If o is a reference, this returns true
+ * if r == o or if r and o reference the same non null object. If o is a
+ * wrapper, this returns true if r's referent is identical to the wrapped
+ * object.
+ */
+ static boolean referenceEquals(Reference r, Object o) {
+ // compare reference to reference.
+ if (o instanceof InternalReference) {
+ // are they the same reference? used in cleanup.
+ if (o == r) {
+ return true;
+ }
+
+ // do they reference identical values? used in conditional puts.
+ Object referent = ((Reference) o).get();
+ return referent != null && referent == r.get();
+ }
+
+ // is the wrapped object identical to the referent? used in lookups.
+ return ((ReferenceAwareWrapper) o).unwrap() == r.get();
+ }
+
+ /**
+ * Big hack. Used to compare keys and values to referenced keys and values
+ * without creating more references.
+ */
+ static class ReferenceAwareWrapper {
+
+ Object wrapped;
+
+ ReferenceAwareWrapper(Object wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ Object unwrap() {
+ return wrapped;
+ }
+
+ @Override
+ public int hashCode() {
+ return wrapped.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ // defer to reference's equals() logic.
+ return obj.equals(this);
+ }
+ }
+
+ /**
+ * Used for keys. Overrides hash code to use identity hash code.
+ */
+ static class KeyReferenceAwareWrapper extends ReferenceAwareWrapper {
+
+ public KeyReferenceAwareWrapper(Object wrapped) {
+ super(wrapped);
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(wrapped);
+ }
+ }
+
+ class SoftKeyReference extends FinalizableSoftReference<Object>
+ implements InternalReference {
+
+ int hashCode;
+
+ public SoftKeyReference(Object key) {
+ super(key);
+ this.hashCode = keyHashCode(key);
+ }
+
+ public void finalizeReferent() {
+ delegate.remove(this);
+ }
+
+ @Override public int hashCode() {
+ return this.hashCode;
+ }
+
+ @Override public boolean equals(Object o) {
+ return referenceEquals(this, o);
+ }
+ }
+
+ class WeakKeyReference extends FinalizableWeakReference<Object>
+ implements InternalReference {
+
+ int hashCode;
+
+ public WeakKeyReference(Object key) {
+ super(key);
+ this.hashCode = keyHashCode(key);
+ }
+
+ public void finalizeReferent() {
+ delegate.remove(this);
+ }
+
+ @Override public int hashCode() {
+ return this.hashCode;
+ }
+
+ @Override public boolean equals(Object o) {
+ return referenceEquals(this, o);
+ }
+ }
+
+ class SoftValueReference extends FinalizableSoftReference<Object>
+ implements InternalReference {
+
+ Object keyReference;
+
+ public SoftValueReference(Object keyReference, Object value) {
+ super(value);
+ this.keyReference = keyReference;
+ }
+
+ public void finalizeReferent() {
+ delegate.remove(keyReference, this);
+ }
+
+ @Override public boolean equals(Object obj) {
+ return referenceEquals(this, obj);
+ }
+ }
+
+ class WeakValueReference extends FinalizableWeakReference<Object>
+ implements InternalReference {
+
+ Object keyReference;
+
+ public WeakValueReference(Object keyReference, Object value) {
+ super(value);
+ this.keyReference = keyReference;
+ }
+
+ public void finalizeReferent() {
+ delegate.remove(keyReference, this);
+ }
+
+ @Override public boolean equals(Object obj) {
+ return referenceEquals(this, obj);
+ }
+ }
+
+ protected interface Strategy {
+ public Object execute(ReferenceMap map, Object keyReference,
+ Object valueReference);
+ }
+
+ protected Strategy putStrategy() {
+ return PutStrategy.PUT;
+ }
+
+ protected Strategy putIfAbsentStrategy() {
+ return PutStrategy.PUT_IF_ABSENT;
+ }
+
+ protected Strategy replaceStrategy() {
+ return PutStrategy.REPLACE;
+ }
+
+ private enum PutStrategy implements Strategy {
+ PUT {
+ public Object execute(ReferenceMap map, Object keyReference,
+ Object valueReference) {
+ return map.delegate.put(keyReference, valueReference);
+ }
+ },
+
+ REPLACE {
+ public Object execute(ReferenceMap map, Object keyReference,
+ Object valueReference) {
+ return map.delegate.replace(keyReference, valueReference);
+ }
+ },
+
+ PUT_IF_ABSENT {
+ public Object execute(ReferenceMap map, Object keyReference,
+ Object valueReference) {
+ return map.delegate.putIfAbsent(keyReference, valueReference);
+ }
+ };
+ };
+
+ private static PutStrategy defaultPutStrategy;
+
+ protected PutStrategy getPutStrategy() {
+ return defaultPutStrategy;
+ }
+
+
+ class Entry implements Map.Entry<K, V> {
+
+ K key;
+ V value;
+
+ public Entry(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public K getKey() {
+ return this.key;
+ }
+
+ public V getValue() {
+ return this.value;
+ }
+
+ public V setValue(V value) {
+ return put(key, value);
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode() * 31 + value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ReferenceMap.Entry)) {
+ return false;
+ }
+
+ Entry entry = (Entry) o;
+ return key.equals(entry.key) && value.equals(entry.value);
+ }
+
+ @Override
+ public String toString() {
+ return key + "=" + value;
+ }
+ }
+
+ static void ensureNotNull(Object o) {
+ if (o == null) {
+ throw new NullPointerException();
+ }
+ }
+
+ static void ensureNotNull(Object... array) {
+ for (int i = 0; i < array.length; i++) {
+ if (array[i] == null) {
+ throw new NullPointerException("Argument #" + i + " is null.");
+ }
+ }
+ }
+
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeInt(size());
+ for (Map.Entry<Object, Object> entry : delegate.entrySet()) {
+ Object key = dereferenceKey(entry.getKey());
+ Object value = dereferenceValue(entry.getValue());
+
+ // don't persist gc'ed entries.
+ if (key != null && value != null) {
+ out.writeObject(key);
+ out.writeObject(value);
+ }
+ }
+ out.writeObject(null);
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException,
+ ClassNotFoundException {
+ in.defaultReadObject();
+ int size = in.readInt();
+ this.delegate = new ConcurrentHashMap<Object, Object>(size);
+ while (true) {
+ K key = (K) in.readObject();
+ if (key == null) {
+ break;
+ }
+ V value = (V) in.readObject();
+ put(key, value);
+ }
+ }
+
+}
Added: struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceType.java
URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceType.java?rev=1209569&view=auto
==============================================================================
--- struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceType.java (added)
+++ struts/struts2/branches/STRUTS_3_X/xwork-core/src/main/java/org/apache/struts2/xwork2/inject/util/ReferenceType.java Fri Dec 2 16:33:03 2011
@@ -0,0 +1,55 @@
+/**
+ * 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 org.apache.struts2.xwork2.inject.util;
+
+/**
+ * Reference type. Used to specify what type of reference to keep to a
+ * referent.
+ *
+ * @see java.lang.ref.Reference
+ * @author crazybob@google.com (Bob Lee)
+ */
+public enum ReferenceType {
+
+ /**
+ * Prevents referent from being reclaimed by the garbage collector.
+ */
+ STRONG,
+
+ /**
+ * Referent reclaimed in an LRU fashion when the VM runs low on memory and
+ * no strong references exist.
+ *
+ * @see java.lang.ref.SoftReference
+ */
+ SOFT,
+
+ /**
+ * Referent reclaimed when no strong or soft references exist.
+ *
+ * @see java.lang.ref.WeakReference
+ */
+ WEAK,
+
+ /**
+ * Similar to weak references except the garbage collector doesn't actually
+ * reclaim the referent. More flexible alternative to finalization.
+ *
+ * @see java.lang.ref.PhantomReference
+ */
+ PHANTOM;
+}