All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.opensymphony.xwork2.inject.ContainerBuilder Maven / Gradle / Ivy

There is a newer version: 6.3.0.2
Show newest version
/**
 * 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; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; /** * Builds a dependency injection {@link Container}. The combination of * dependency type and name uniquely identifies a dependency mapping; you can * use the same name for two different types. Not safe for concurrent use. * *

Adds the following factories by default:

* *
    *
  • Injects the current {@link Container}. *
  • Injects the {@link Logger} for the injected member's declaring class. *
* * @author [email protected] (Bob Lee) */ public final class ContainerBuilder { final Map, InternalFactory> factories = new HashMap<>(); final List> singletonFactories = new ArrayList<>(); final List> earlyInitializableFactories = new ArrayList<>(); final List> staticInjections = new ArrayList<>(); boolean created; boolean allowDuplicates = false; private static final InternalFactory CONTAINER_FACTORY = new InternalFactory() { public Container create(InternalContext context) { return context.getContainer(); } @Override public Class type() { return Container.class; } }; private static final InternalFactory LOGGER_FACTORY = new InternalFactory() { public Logger create(InternalContext context) { Member member = context.getExternalContext().getMember(); return member == null ? Logger.getAnonymousLogger() : Logger.getLogger(member.getDeclaringClass().getName()); } @Override public Class type() { return Logger.class; } }; /** * Constructs a new builder. */ public ContainerBuilder() { // In the current container as the default Container implementation. factories.put(Key.newInstance(Container.class, Container.DEFAULT_NAME), CONTAINER_FACTORY); // Inject the logger for the injected member's declaring class. factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME), LOGGER_FACTORY); } /** * Maps a dependency. All methods in this class ultimately funnel through * here. */ private ContainerBuilder factory(final Key key, InternalFactory factory, Scope scope) { ensureNotCreated(); checkKey(key); final InternalFactory scopedFactory = scope.scopeFactory(key.getType(), key.getName(), factory); factories.put(key, scopedFactory); InternalFactory callableFactory = createCallableFactory(key, scopedFactory); if (EarlyInitializable.class.isAssignableFrom(factory.type())) { earlyInitializableFactories.add(callableFactory); } else if (scope == Scope.SINGLETON) { singletonFactories.add(callableFactory); } return this; } private InternalFactory createCallableFactory(final Key key, final InternalFactory scopedFactory) { return new InternalFactory() { public T create(InternalContext context) { try { context.setExternalContext(ExternalContext.newInstance(null, key, context.getContainerImpl())); return scopedFactory.create(context); } finally { context.setExternalContext(null); } } @Override public Class type() { return scopedFactory.type(); } }; } /** * Ensures a key isn't already mapped. * * @param key the key to check */ private void checkKey(Key key) { if (factories.containsKey(key) && !allowDuplicates) { throw new DependencyException("Dependency mapping for " + key + " already exists."); } } /** * Maps a factory to a given dependency type and name. * * @param type * @param type of dependency * @param name of dependency * @param factory creates objects to inject * @param scope scope of injected instances * @return this builder */ public ContainerBuilder factory(final Class type, final String name, final Factory factory, Scope scope) { InternalFactory internalFactory = new InternalFactory() { public T create(InternalContext context) { try { Context externalContext = context.getExternalContext(); return factory.create(externalContext); } catch (Exception e) { throw new RuntimeException(e); } } @Override public Class type() { return factory.type(); } @Override public String toString() { return new LinkedHashMap() {{ put("type", type); put("name", name); put("factory", factory); }}.toString(); } }; return factory(Key.newInstance(type, name), internalFactory, scope); } /** * Convenience method. Equivalent to {@code factory(type, * Container.DEFAULT_NAME, factory, scope)}. * * @param type * @param type of dependency * @param factory of dependency * @param scope scope of injected instances * @return a container builder * @see #factory(Class, String, Factory, Scope) */ public ContainerBuilder factory(Class type, Factory factory, Scope scope) { return factory(type, Container.DEFAULT_NAME, factory, scope); } /** * Convenience method. Equivalent to {@code factory(type, name, factory, * Scope.PROTOTYPE)}. * * @param type * @param type of dependency * @param name of dependency * @param factory of dependency * @return a container builder * @see #factory(Class, String, Factory, Scope) */ public ContainerBuilder factory(Class type, String name, Factory factory) { return factory(type, name, factory, Scope.PROTOTYPE); } /** * Convenience method. Equivalent to {@code factory(type, * Container.DEFAULT_NAME, factory, Scope.PROTOTYPE)}. * * @param type * @param type of dependency * @param factory of dependency * @return a container builder * @see #factory(Class, String, Factory, Scope) */ public ContainerBuilder factory(Class type, Factory factory) { return factory(type, Container.DEFAULT_NAME, factory, Scope.PROTOTYPE); } /** * Maps an implementation class to a given dependency type and name. Creates * instances using the container, recursively injecting dependencies. * * @param type * @param type of dependency * @param name of dependency * @param implementation class * @param scope scope of injected instances * @return this builder */ public ContainerBuilder factory(final Class type, final String name, final Class implementation, final Scope scope) { // This factory creates new instances of the given implementation. // We have to lazy load the constructor because the Container // hasn't been created yet. InternalFactory factory = new InternalFactory() { volatile ContainerImpl.ConstructorInjector constructor; @SuppressWarnings("unchecked") public T create(InternalContext context) { if (constructor == null) { this.constructor = context.getContainerImpl().getConstructor(implementation); } return (T) constructor.construct(context, type); } @Override public Class type() { return implementation; } @Override public String toString() { return new LinkedHashMap() {{ put("type", type); put("name", name); put("implementation", implementation); put("scope", scope); }}.toString(); } }; return factory(Key.newInstance(type, name), factory, scope); } /** *

* Maps an implementation class to a given dependency type and name. Creates * instances using the container, recursively injecting dependencies. *

* *

Sets scope to value from {@link Scoped} annotation on the * implementation class. Defaults to {@link Scope#PROTOTYPE} if no annotation * is found. *

* * @param type * @param type of dependency * @param name of dependency * @param implementation class * @return this builder */ public ContainerBuilder factory(final Class type, String name, final Class implementation) { Scoped scoped = implementation.getAnnotation(Scoped.class); Scope scope = scoped == null ? Scope.PROTOTYPE : scoped.value(); return factory(type, name, implementation, scope); } /** * Convenience method. Equivalent to {@code factory(type, * Container.DEFAULT_NAME, implementation)}. * * @param type * @param type of dependency * @param implementation class * @return this builder * @see #factory(Class, String, Class) */ public ContainerBuilder factory(Class type, Class implementation) { return factory(type, Container.DEFAULT_NAME, implementation); } /** * Convenience method. Equivalent to {@code factory(type, * Container.DEFAULT_NAME, type)}. * * @param type * @param type of dependency * @return this builder * @see #factory(Class, String, Class) */ public ContainerBuilder factory(Class type) { return factory(type, Container.DEFAULT_NAME, type); } /** * Convenience method. Equivalent to {@code factory(type, name, type)}. * * @param type * @param type of dependency * @param name of dependency * @return this builder * @see #factory(Class, String, Class) */ public ContainerBuilder factory(Class type, String name) { return factory(type, name, type); } /** * Convenience method. Equivalent to {@code factory(type, * Container.DEFAULT_NAME, implementation, scope)}. * * @param type * @param type of dependency * @param implementation class * @param scope the scope * @return this builder * @see #factory(Class, String, Class, Scope) */ public ContainerBuilder factory(Class type, Class implementation, Scope scope) { return factory(type, Container.DEFAULT_NAME, implementation, scope); } /** * Convenience method. Equivalent to {@code factory(type, * Container.DEFAULT_NAME, type, scope)}. * * @param type * @param type of dependency * @param scope the scope * @return this builder * @see #factory(Class, String, Class, Scope) */ public ContainerBuilder factory(Class type, Scope scope) { return factory(type, Container.DEFAULT_NAME, type, scope); } /** * Convenience method. Equivalent to {@code factory(type, name, type, * scope)}. * * @param type * @param type of dependency * @param name of dependency * @param scope the scope * @return this builder * @see #factory(Class, String, Class, Scope) */ public ContainerBuilder factory(Class type, String name, Scope scope) { return factory(type, name, type, scope); } /** * Convenience method. Equivalent to {@code alias(type, Container.DEFAULT_NAME, * type)}. * * @param type * @param type of dependency * @param alias of dependency * @return this builder * @see #alias(Class, String, String) */ public ContainerBuilder alias(Class type, String alias) { return alias(type, Container.DEFAULT_NAME, alias); } /** * Maps an existing factory to a new name. * * @param type * @param type of dependency * @param name of dependency * @param alias of to the dependency * @return this builder */ public ContainerBuilder alias(Class type, String name, String alias) { return alias(Key.newInstance(type, name), Key.newInstance(type, alias)); } /** * Maps an existing dependency. All methods in this class ultimately funnel through * here. * @param type of key and alias * @param key name of key * @param aliasKey name of alias key * @return this builder */ private ContainerBuilder alias(final Key key, final Key aliasKey) { ensureNotCreated(); checkKey(aliasKey); final InternalFactory scopedFactory = (InternalFactory) factories.get(key); if (scopedFactory == null) { throw new DependencyException("Dependency mapping for " + key + " doesn't exists."); } factories.put(aliasKey, scopedFactory); return this; } /** * Maps a constant value to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, String value) { return constant(String.class, name, value); } /** * Maps a constant value to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, int value) { return constant(int.class, name, value); } /** * Maps a constant value to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, long value) { return constant(long.class, name, value); } /** * Maps a constant value to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, boolean value) { return constant(boolean.class, name, value); } /** * Maps a constant value to the given name. * @param name name * @param value constant value * @return this builder */ public ContainerBuilder constant(String name, double value) { return constant(double.class, name, value); } /** * Maps a constant value to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, float value) { return constant(float.class, name, value); } /** * Maps a constant value to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, short value) { return constant(short.class, name, value); } /** * Maps a constant value to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, char value) { return constant(char.class, name, value); } /** * Maps a class to the given name. * @param name name * @param value value * @return this builder */ public ContainerBuilder constant(String name, Class value) { return constant(Class.class, name, value); } /** * Maps an enum to the given name. * @param name name * @param value type * @param value value * @return this builder */ public > ContainerBuilder constant(String name, E value) { return constant(value.getDeclaringClass(), name, value); } /** * Maps a constant value to the given type and name. * @param type type of class * @param name name * @param value value * @return this builder */ private ContainerBuilder constant(final Class type, final String name, final T value) { InternalFactory factory = new InternalFactory() { public T create(InternalContext ignored) { return value; } @Override public Class type() { return (Class) value.getClass(); } @Override public String toString() { return new LinkedHashMap() { { put("type", type); put("name", name); put("value", value); } }.toString(); } }; return factory(Key.newInstance(type, name), factory, Scope.PROTOTYPE); } /** * Upon creation, the {@link Container} will inject static fields and methods * into the given classes. * * @param types for which static members will be injected * @return this builder */ public ContainerBuilder injectStatics(Class... types) { staticInjections.addAll(Arrays.asList(types)); return this; } /** * @param type type of class * @param name name of class * @return true if this builder contains a mapping for the given type and * name. */ public boolean contains(Class type, String name) { return factories.containsKey(Key.newInstance(type, name)); } /** * Convenience method. Equivalent to {@code contains(type, * Container.DEFAULT_NAME)}. * @param type type of class * @return true if this builder contains a mapping for the given type. */ public boolean contains(Class type) { return contains(type, Container.DEFAULT_NAME); } /** * Creates a {@link Container} instance. Injects static members for classes * which were registered using {@link #injectStatics(Class...)}. * * @param loadSingletons If true, the container will load all singletons * now. If false, the container will lazily load singletons. Eager loading * is appropriate for production use while lazy loading can speed * development. * @return this builder * @throws IllegalStateException if called more than once */ public Container create(boolean loadSingletons) { ensureNotCreated(); created = true; final ContainerImpl container = new ContainerImpl(new HashMap<>(factories)); if (loadSingletons) { container.callInContext(new ContainerImpl.ContextualCallable() { public Void call(InternalContext context) { for (InternalFactory factory : singletonFactories) { factory.create(context); } return null; } }); } container.callInContext(new ContainerImpl.ContextualCallable() { public Void call(InternalContext context) { for (InternalFactory factory : earlyInitializableFactories) { factory.create(context); } return null; } }); container.injectStatics(staticInjections); return container; } /** * Currently we only support creating one Container instance per builder. * If we want to support creating more than one container per builder, * we should move to a "factory factory" model where we create a factory * instance per Container. Right now, one factory instance would be * shared across all the containers, singletons synchronize on the * container when lazy loading, etc. */ private void ensureNotCreated() { if (created) { throw new IllegalStateException("Container already created."); } } public void setAllowDuplicates(boolean val) { allowDuplicates = val; } /** * Implemented by classes which participate in building a container. */ public interface Command { /** * Contributes factories to the given builder. * * @param builder the container builder */ void build(ContainerBuilder builder); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy