struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lukaszlen...@apache.org
Subject [42/57] [partial] struts git commit: Merges xwork packages into struts
Date Wed, 17 Jun 2015 21:09:42 GMT
http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
new file mode 100644
index 0000000..1879933
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/ContainerImpl.java
@@ -0,0 +1,603 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ * <p/>
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.xwork2.inject;
+
+import com.opensymphony.xwork2.inject.util.ReferenceCache;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.*;
+import java.security.AccessControlException;
+import java.util.*;
+import java.util.Map.Entry;
+
+/**
+ * Default {@link Container} implementation.
+ *
+ * @author crazybob@google.com (Bob Lee)
+ * @see ContainerBuilder
+ */
+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<>();
+        for (Key<?> key : factories.keySet()) {
+            Set<String> names = map.get(key.getType());
+            if (names == null) {
+                names = new HashSet<>();
+                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<>();
+                    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<>();
+
+        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<>();
+
+        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 | IllegalAccessException | 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) {
+        Set<String> names = factoryNamesByType.get(type);
+        if (names == null) {
+            names = Collections.emptySet();
+        }
+        return names;
+    }
+
+    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;
+                // WW-3768: ThreadLocal was not removed
+                localContext.remove();
+            }
+        } 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<>();
+
+    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);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/Context.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/Context.java b/core/src/main/java/com/opensymphony/xwork2/inject/Context.java
new file mode 100644
index 0000000..233b5e4
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/Context.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.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();
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/DependencyException.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/DependencyException.java b/core/src/main/java/com/opensymphony/xwork2/inject/DependencyException.java
new file mode 100644
index 0000000..f92896d
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/DependencyException.java
@@ -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 com.opensymphony.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);
+  }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/ExternalContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/ExternalContext.java b/core/src/main/java/com/opensymphony/xwork2/inject/ExternalContext.java
new file mode 100644
index 0000000..0c054e8
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/ExternalContext.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ * <p/>
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/Factory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/Factory.java b/core/src/main/java/com/opensymphony/xwork2/inject/Factory.java
new file mode 100644
index 0000000..1117506
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/Factory.java
@@ -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 com.opensymphony.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;
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/Inject.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/Inject.java b/core/src/main/java/com/opensymphony/xwork2/inject/Inject.java
new file mode 100644
index 0000000..610d2bb
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/Inject.java
@@ -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 com.opensymphony.xwork2.inject;
+
+import static com.opensymphony.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;
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/InternalContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/InternalContext.java b/core/src/main/java/com/opensymphony/xwork2/inject/InternalContext.java
new file mode 100644
index 0000000..7014480
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/InternalContext.java
@@ -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 com.opensymphony.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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/InternalFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/InternalFactory.java b/core/src/main/java/com/opensymphony/xwork2/inject/InternalFactory.java
new file mode 100644
index 0000000..49fcf27
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/InternalFactory.java
@@ -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 com.opensymphony.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);
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/Key.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/Key.java b/core/src/main/java/com/opensymphony/xwork2/inject/Key.java
new file mode 100644
index 0000000..ad9e0ba
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/Key.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ * <p/>
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.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);
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/Scope.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/Scope.java b/core/src/main/java/com/opensymphony/xwork2/inject/Scope.java
new file mode 100644
index 0000000..62443f7
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/Scope.java
@@ -0,0 +1,214 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ * <p/>
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.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/>
+     * <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(com.opensymphony.xwork2.inject.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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/Scoped.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/Scoped.java b/core/src/main/java/com/opensymphony/xwork2/inject/Scoped.java
new file mode 100644
index 0000000..31a9447
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/Scoped.java
@@ -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 com.opensymphony.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();
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/package-info.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/package-info.java b/core/src/main/java/com/opensymphony/xwork2/inject/package-info.java
new file mode 100644
index 0000000..6e26e24
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/package-info.java
@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+/**
+ * <i>Guice</i> (pronounced "juice"). A lightweight dependency injection
+ * container. Features include:
+ *
+ * <ul>
+ *   <li>constructor, method, and field injection</li>
+ *   <li>static method and field injection</li>
+ *   <li>circular reference support (including constructors if you depend upon
+ *    interfaces)</li>
+ *   <li>high performance</li>
+ *   <li>externalize what needs to be and no more</li>
+ * </ul>
+ */
+package com.opensymphony.xwork2.inject;

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizablePhantomReference.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizablePhantomReference.java b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizablePhantomReference.java
new file mode 100644
index 0000000..869b96b
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizablePhantomReference.java
@@ -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 com.opensymphony.xwork2.inject.util;
+
+import java.lang.ref.PhantomReference;
+
+/**
+ * Phantom reference with a {@link com.opensymphony.xwork2.inject.util.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());
+  }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReference.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReference.java b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReference.java
new file mode 100644
index 0000000..16653e2
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReference.java
@@ -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 com.opensymphony.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();
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReferenceQueue.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReferenceQueue.java b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReferenceQueue.java
new file mode 100644
index 0000000..72121ef
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableReferenceQueue.java
@@ -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 com.opensymphony.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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableSoftReference.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableSoftReference.java b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableSoftReference.java
new file mode 100644
index 0000000..eeb1c20
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableSoftReference.java
@@ -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 com.opensymphony.xwork2.inject.util;
+
+import java.lang.ref.SoftReference;
+
+/**
+ * Soft reference with a {@link com.opensymphony.xwork2.inject.util.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());
+  }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableWeakReference.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableWeakReference.java b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableWeakReference.java
new file mode 100644
index 0000000..7c3a67f
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/util/FinalizableWeakReference.java
@@ -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 com.opensymphony.xwork2.inject.util;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Weak reference with a {@link com.opensymphony.xwork2.inject.util.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());
+  }
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/util/Function.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/util/Function.java b/core/src/main/java/com/opensymphony/xwork2/inject/util/Function.java
new file mode 100644
index 0000000..fd21237
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/util/Function.java
@@ -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 com.opensymphony.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);
+}

http://git-wip-us.apache.org/repos/asf/struts/blob/31af5842/core/src/main/java/com/opensymphony/xwork2/inject/util/ReferenceCache.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/com/opensymphony/xwork2/inject/util/ReferenceCache.java b/core/src/main/java/com/opensymphony/xwork2/inject/util/ReferenceCache.java
new file mode 100644
index 0000000..9ebf545
--- /dev/null
+++ b/core/src/main/java/com/opensymphony/xwork2/inject/util/ReferenceCache.java
@@ -0,0 +1,177 @@
+/**
+ * Copyright (C) 2006 Google Inc.
+ * <p/>
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.opensymphony.xwork2.inject.util;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.util.concurrent.*;
+
+import static com.opensymphony.xwork2.inject.util.ReferenceType.STRONG;
+
+/**
+ * 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<>();
+    transient ThreadLocal<Future<V>> localFuture = new ThreadLocal<>();
+
+    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<>(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}
+     * <p/>
+     * 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<>();
+        this.localFuture = new ThreadLocal<>();
+    }
+
+}


Mime
View raw message