Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.opensymphony.xwork2.inject.ContainerImpl Maven / Gradle / Ivy
/*
* 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 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 [email protected] (Bob Lee)
* @see ContainerBuilder
*/
class ContainerImpl implements Container {
final Map, InternalFactory> factories;
final Map, Set> factoryNamesByType;
ContainerImpl(Map, InternalFactory> factories) {
this.factories = factories;
Map, Set> map = new HashMap<>();
for (Key key : factories.keySet()) {
Set names = map.get(key.getType());
if (names == null) {
names = new HashSet<>();
map.put(key.getType(), names);
}
names.add(key.getName());
}
for (Entry, Set> entry : map.entrySet()) {
entry.setValue(Collections.unmodifiableSet(entry.getValue()));
}
this.factoryNamesByType = Collections.unmodifiableMap(map);
}
@SuppressWarnings("unchecked")
InternalFactory getFactory(Key key) {
return (InternalFactory) factories.get(key);
}
/**
* Field and method injectors.
*/
final Map, List> injectors =
new ReferenceCache, List>() {
@Override
protected List create(Class key) {
List 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 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> staticInjections) {
final List injectors = new ArrayList<>();
for (Class clazz : staticInjections) {
addInjectorsForFields(clazz.getDeclaredFields(), true, injectors);
addInjectorsForMethods(clazz.getDeclaredMethods(), true, injectors);
}
callInContext(new ContextualCallable() {
public Void call(InternalContext context) {
for (Injector injector : injectors) {
injector.inject(context, null);
}
return null;
}
});
}
void addInjectorsForMethods(Method[] methods, boolean statics, List injectors) {
addInjectorsForMembers(Arrays.asList(methods), statics, injectors,
new InjectorFactory() {
public Injector create(ContainerImpl container, Method method,
String name) throws MissingDependencyException {
return new MethodInjector(container, method, name);
}
});
}
void addInjectorsForFields(Field[] fields, boolean statics, List injectors) {
addInjectorsForMembers(Arrays.asList(fields), statics, injectors,
new InjectorFactory() {
public Injector create(ContainerImpl container, Field field,
String name) throws MissingDependencyException {
return new FieldInjector(container, field, name);
}
});
}
void addInjectorsForMembers(
List members, boolean statics, List injectors, InjectorFactory 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 {
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 ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()))
&& !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
*/
ParameterInjector[]
getParametersInjectors(M member, Annotation[][] annotations, Class[] parameterTypes, String defaultName) throws MissingDependencyException {
List> parameterInjectors = new ArrayList<>();
Iterator 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);
}
ParameterInjector createParameterInjector(Key key, Member member) throws MissingDependencyException {
InternalFactory factory = getFactory(key);
if (factory == null) {
throw new MissingDependencyException("No mapping found for dependency " + key + " in " + member + ".");
}
ExternalContext externalContext = ExternalContext.newInstance(member, key, this);
return new ParameterInjector(externalContext, factory);
}
@SuppressWarnings("unchecked")
private ParameterInjector[] toArray(List> 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 ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
&& !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, ConstructorInjector> constructors =
new ReferenceCache, ConstructorInjector>() {
@Override
@SuppressWarnings("unchecked")
protected ConstructorInjector create(Class implementation) {
return new ConstructorInjector(ContainerImpl.this, implementation);
}
};
static class ConstructorInjector {
final Class implementation;
final List injectors;
final Constructor constructor;
final ParameterInjector[] parameterInjectors;
ConstructorInjector(ContainerImpl container, Class implementation) {
this.implementation = implementation;
constructor = findConstructorIn(implementation);
if ((!Modifier.isPublic(constructor.getModifiers()) || !Modifier.isPublic(constructor.getDeclaringClass().getModifiers()))
&& !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 constructor) throws MissingDependencyException {
return constructor.getParameterTypes().length == 0
? null // default constructor.
: container.getParametersInjectors(
constructor,
constructor.getParameterAnnotations(),
constructor.getParameterTypes(),
inject.value()
);
}
@SuppressWarnings("unchecked")
private Constructor findConstructorIn(Class implementation) {
Constructor found = null;
Constructor[] declaredConstructors = (Constructor[]) implementation.getDeclaredConstructors();
for (Constructor 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 expectedType) {
ConstructionContext 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 {
final ExternalContext externalContext;
final InternalFactory factory;
public ParameterInjector(ExternalContext externalContext, InternalFactory 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 injectors = this.injectors.get(o.getClass());
for (Injector injector : injectors) {
injector.inject(context, o);
}
}
T inject(Class implementation, InternalContext context) {
try {
ConstructorInjector constructor = getConstructor(implementation);
return implementation.cast(constructor.construct(context, implementation));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
T getInstance(Class type, String name, InternalContext context) {
ExternalContext previous = context.getExternalContext();
Key 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 getInstance(Class type, InternalContext context) {
return getInstance(type, DEFAULT_NAME, context);
}
public void inject(final Object o) {
callInContext(new ContextualCallable() {
public Void call(InternalContext context) {
inject(o, context);
return null;
}
});
}
public T inject(final Class implementation) {
return callInContext(new ContextualCallable() {
public T call(InternalContext context) {
return inject(implementation, context);
}
});
}
public T getInstance(final Class type, final String name) {
return callInContext(new ContextualCallable() {
public T call(InternalContext context) {
return getInstance(type, name, context);
}
});
}
public T getInstance(final Class type) {
return callInContext(new ContextualCallable() {
public T call(InternalContext context) {
return getInstance(type, context);
}
});
}
public Set getInstanceNames(final Class type) {
Set names = factoryNamesByType.get(type);
if (names == null) {
names = Collections.emptySet();
}
return names;
}
ThreadLocal localContext = new ThreadLocal() {
@Override
protected Object[] initialValue() {
return new Object[1];
}
};
/**
* Looks up thread local context. Creates (and removes) a new context if necessary.
*/
T callInContext(ContextualCallable 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 call(InternalContext context);
}
/**
* Gets a constructor function for a given implementation class.
*/
@SuppressWarnings("unchecked")
ConstructorInjector getConstructor(Class implementation) {
return constructors.get(implementation);
}
final ThreadLocal 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);
}
}
}