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&lt;String,Integer&gt;</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&lt;Float,Float&gt;</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;
+}