Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.inject » [javadoc | source]
    1   /**
    2    * Copyright (C) 2006 Google Inc.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    * http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package com.opensymphony.xwork2.inject;
   18   
   19   import java.lang.reflect.Member;
   20   import java.util.ArrayList;
   21   import java.util.Arrays;
   22   import java.util.HashMap;
   23   import java.util.LinkedHashMap;
   24   import java.util.List;
   25   import java.util.Map;
   26   import java.util.logging.Logger;
   27   
   28   /**
   29    * Builds a dependency injection {@link Container}. The combination of
   30    * dependency type and name uniquely identifies a dependency mapping; you can
   31    * use the same name for two different types. Not safe for concurrent use.
   32    *
   33    * <p>Adds the following factories by default:
   34    *
   35    * <ul>
   36    *   <li>Injects the current {@link Container}.
   37    *   <li>Injects the {@link Logger} for the injected member's declaring class.
   38    * </ul>
   39    *
   40    * @author crazybob@google.com (Bob Lee)
   41    */
   42   public final class ContainerBuilder {
   43   
   44     final Map<Key<?>, InternalFactory<?>> factories =
   45         new HashMap<Key<?>, InternalFactory<?>>();
   46     final List<InternalFactory<?>> singletonFactories =
   47         new ArrayList<InternalFactory<?>>();
   48     final List<Class<?>> staticInjections = new ArrayList<Class<?>>();
   49     boolean created;
   50     boolean allowDuplicates = false;
   51   
   52     private static final InternalFactory<Container> CONTAINER_FACTORY =
   53         new InternalFactory<Container>() {
   54           public Container create(InternalContext context) {
   55             return context.getContainer();
   56           }
   57         };
   58   
   59     private static final InternalFactory<Logger> LOGGER_FACTORY =
   60         new InternalFactory<Logger>() {
   61           public Logger create(InternalContext context) {
   62             Member member = context.getExternalContext().getMember();
   63             return member == null ? Logger.getAnonymousLogger()
   64                 : Logger.getLogger(member.getDeclaringClass().getName());
   65           }
   66         };
   67   
   68     /**
   69      * Constructs a new builder.
   70      */
   71     public ContainerBuilder() {
   72       // In the current container as the default Container implementation.
   73       factories.put(Key.newInstance(Container.class, Container.DEFAULT_NAME),
   74           CONTAINER_FACTORY);
   75   
   76       // Inject the logger for the injected member's declaring class.
   77       factories.put(Key.newInstance(Logger.class, Container.DEFAULT_NAME),
   78           LOGGER_FACTORY);
   79     }
   80   
   81     /**
   82      * Maps a dependency. All methods in this class ultimately funnel through
   83      * here.
   84      */
   85     private <T> ContainerBuilder factory(final Key<T> key,
   86         InternalFactory<? extends T> factory, Scope scope) {
   87       ensureNotCreated();
   88       checkKey(key);
   89       final InternalFactory<? extends T> scopedFactory =
   90           scope.scopeFactory(key.getType(), key.getName(), factory);
   91       factories.put(key, scopedFactory);
   92       if (scope == Scope.SINGLETON) {
   93         singletonFactories.add(new InternalFactory<T>() {
   94           public T create(InternalContext context) {
   95             try {
   96               context.setExternalContext(ExternalContext.newInstance(
   97                   null, key, context.getContainerImpl()));
   98               return scopedFactory.create(context);
   99             } finally {
  100               context.setExternalContext(null);
  101             }
  102           }
  103         });
  104       }
  105       return this;
  106     }
  107     
  108     /**
  109      * Ensures a key isn't already mapped.
  110      */
  111     private void checkKey(Key<?> key) {
  112       if (factories.containsKey(key) && !allowDuplicates) {
  113         throw new DependencyException(
  114             "Dependency mapping for " + key + " already exists.");
  115       }
  116     }
  117   
  118     /**
  119      * Maps a factory to a given dependency type and name.
  120      *
  121      * @param type of dependency
  122      * @param name of dependency
  123      * @param factory creates objects to inject
  124      * @param scope scope of injected instances
  125      * @return this builder
  126      */
  127     public <T> ContainerBuilder factory(final Class<T> type, final String name,
  128         final Factory<? extends T> factory, Scope scope) {
  129       InternalFactory<T> internalFactory =
  130           new InternalFactory<T>() {
  131   
  132         public T create(InternalContext context) {
  133           try {
  134             Context externalContext = context.getExternalContext();
  135             return factory.create(externalContext);
  136           } catch (Exception e) {
  137             throw new RuntimeException(e);
  138           }
  139         }
  140   
  141         public String toString() {
  142           return new LinkedHashMap<String, Object>() {{
  143             put("type", type);
  144             put("name", name);
  145             put("factory", factory);
  146           }}.toString();
  147         }
  148       };
  149   
  150       return factory(Key.newInstance(type, name), internalFactory, scope);
  151     }
  152   
  153     /**
  154      * Convenience method.&nbsp;Equivalent to {@code factory(type,
  155      * Container.DEFAULT_NAME, factory, scope)}.
  156      *
  157      * @see #factory(Class, String, Factory, Scope)
  158      */
  159     public <T> ContainerBuilder factory(Class<T> type,
  160         Factory<? extends T> factory, Scope scope) {
  161       return factory(type, Container.DEFAULT_NAME, factory, scope);
  162     }
  163   
  164     /**
  165      * Convenience method.&nbsp;Equivalent to {@code factory(type, name, factory,
  166      * Scope.DEFAULT)}.
  167      *
  168      * @see #factory(Class, String, Factory, Scope)
  169      */
  170     public <T> ContainerBuilder factory(Class<T> type, String name,
  171         Factory<? extends T> factory) {
  172       return factory(type, name, factory, Scope.DEFAULT);
  173     }
  174   
  175     /**
  176      * Convenience method.&nbsp;Equivalent to {@code factory(type,
  177      * Container.DEFAULT_NAME, factory, Scope.DEFAULT)}.
  178      *
  179      * @see #factory(Class, String, Factory, Scope)
  180      */
  181     public <T> ContainerBuilder factory(Class<T> type,
  182         Factory<? extends T> factory) {
  183       return factory(type, Container.DEFAULT_NAME, factory, Scope.DEFAULT);
  184     }
  185   
  186     /**
  187      * Maps an implementation class to a given dependency type and name. Creates
  188      * instances using the container, recursively injecting dependencies.
  189      *
  190      * @param type of dependency
  191      * @param name of dependency
  192      * @param implementation class
  193      * @param scope scope of injected instances
  194      * @return this builder
  195      */
  196     public <T> ContainerBuilder factory(final Class<T> type, final String name,
  197         final Class<? extends T> implementation, final Scope scope) {
  198       // This factory creates new instances of the given implementation.
  199       // We have to lazy load the constructor because the Container
  200       // hasn't been created yet.
  201       InternalFactory<? extends T> factory = new InternalFactory<T>() {
  202   
  203         volatile ContainerImpl.ConstructorInjector<? extends T> constructor;
  204   
  205         @SuppressWarnings("unchecked")
  206         public T create(InternalContext context) {
  207           if (constructor == null) {
  208             this.constructor =
  209                 context.getContainerImpl().getConstructor(implementation);
  210           }
  211           return (T) constructor.construct(context, type);
  212         }
  213   
  214         public String toString() {
  215           return new LinkedHashMap<String, Object>() {{
  216             put("type", type);
  217             put("name", name);
  218             put("implementation", implementation);
  219             put("scope", scope);
  220           }}.toString();
  221         }
  222       };
  223   
  224       return factory(Key.newInstance(type, name), factory, scope);
  225     }
  226   
  227     /**
  228      * Maps an implementation class to a given dependency type and name. Creates
  229      * instances using the container, recursively injecting dependencies.
  230      *
  231      * <p>Sets scope to value from {@link Scoped} annotation on the
  232      * implementation class. Defaults to {@link Scope#DEFAULT} if no annotation
  233      * is found.
  234      *
  235      * @param type of dependency
  236      * @param name of dependency
  237      * @param implementation class
  238      * @return this builder
  239      */
  240     public <T> ContainerBuilder factory(final Class<T> type, String name,
  241         final Class<? extends T> implementation) {
  242       Scoped scoped = implementation.getAnnotation(Scoped.class);
  243       Scope scope = scoped == null ? Scope.DEFAULT : scoped.value();
  244       return factory(type, name, implementation, scope);
  245     }
  246   
  247     /**
  248      * Convenience method.&nbsp;Equivalent to {@code factory(type,
  249      * Container.DEFAULT_NAME, implementation)}.
  250      *
  251      * @see #factory(Class, String, Class)
  252      */
  253     public <T> ContainerBuilder factory(Class<T> type,
  254         Class<? extends T> implementation) {
  255       return factory(type, Container.DEFAULT_NAME, implementation);
  256     }
  257   
  258     /**
  259      * Convenience method.&nbsp;Equivalent to {@code factory(type,
  260      * Container.DEFAULT_NAME, type)}.
  261      *
  262      * @see #factory(Class, String, Class)
  263      */
  264     public <T> ContainerBuilder factory(Class<T> type) {
  265       return factory(type, Container.DEFAULT_NAME, type);
  266     }
  267   
  268     /**
  269      * Convenience method.&nbsp;Equivalent to {@code factory(type, name, type)}.
  270      *
  271      * @see #factory(Class, String, Class)
  272      */
  273     public <T> ContainerBuilder factory(Class<T> type, String name) {
  274       return factory(type, name, type);
  275     }
  276   
  277     /**
  278      * Convenience method.&nbsp;Equivalent to {@code factory(type,
  279      * Container.DEFAULT_NAME, implementation, scope)}.
  280      *
  281      * @see #factory(Class, String, Class, Scope)
  282      */
  283     public <T> ContainerBuilder factory(Class<T> type,
  284         Class<? extends T> implementation, Scope scope) {
  285       return factory(type, Container.DEFAULT_NAME, implementation, scope);
  286     }
  287   
  288     /**
  289      * Convenience method.&nbsp;Equivalent to {@code factory(type,
  290      * Container.DEFAULT_NAME, type, scope)}.
  291      *
  292      * @see #factory(Class, String, Class, Scope)
  293      */
  294     public <T> ContainerBuilder factory(Class<T> type, Scope scope) {
  295       return factory(type, Container.DEFAULT_NAME, type, scope);
  296     }
  297   
  298     /**
  299      * Convenience method.&nbsp;Equivalent to {@code factory(type, name, type,
  300      * scope)}.
  301      *
  302      * @see #factory(Class, String, Class, Scope)
  303      */
  304     public <T> ContainerBuilder factory(Class<T> type, String name, Scope scope) {
  305       return factory(type, name, type, scope);
  306     }
  307     
  308     /**
  309      * Convenience method.&nbsp;Equivalent to {@code alias(type, Container.DEFAULT_NAME,
  310      * type)}.
  311      *
  312      * @see #alias(Class, String, String)
  313      */
  314     public <T> ContainerBuilder alias(Class<T> type, String alias) {
  315       return alias(type, Container.DEFAULT_NAME, alias);
  316     }
  317     
  318     /**
  319      * Maps an existing factory to a new name. 
  320      * 
  321      * @param type of dependency
  322      * @param name of dependency
  323      * @param alias of to the dependency
  324      * @return this builder
  325      */
  326     public <T> ContainerBuilder alias(Class<T> type, String name, String alias) {
  327       return alias(Key.newInstance(type, name), Key.newInstance(type, alias));
  328     }
  329     
  330     /**
  331      * Maps an existing dependency. All methods in this class ultimately funnel through
  332      * here.
  333      */
  334     private <T> ContainerBuilder alias(final Key<T> key,
  335         final Key<T> aliasKey) {
  336       ensureNotCreated();
  337       checkKey(aliasKey);
  338       
  339       final InternalFactory<? extends T> scopedFactory = 
  340         (InternalFactory<? extends T>)factories.get(key);
  341       if (scopedFactory == null) {
  342           throw new DependencyException(
  343                   "Dependency mapping for " + key + " doesn't exists.");
  344       }
  345       factories.put(aliasKey, scopedFactory);
  346       return this;
  347     }
  348   
  349     /**
  350      * Maps a constant value to the given name.
  351      */
  352     public ContainerBuilder constant(String name, String value) {
  353       return constant(String.class, name, value);
  354     }
  355   
  356     /**
  357      * Maps a constant value to the given name.
  358      */
  359     public ContainerBuilder constant(String name, int value) {
  360       return constant(int.class, name, value);
  361     }
  362   
  363     /**
  364      * Maps a constant value to the given name.
  365      */
  366     public ContainerBuilder constant(String name, long value) {
  367       return constant(long.class, name, value);
  368     }
  369   
  370     /**
  371      * Maps a constant value to the given name.
  372      */
  373     public ContainerBuilder constant(String name, boolean value) {
  374       return constant(boolean.class, name, value);
  375     }
  376   
  377     /**
  378      * Maps a constant value to the given name.
  379      */
  380     public ContainerBuilder constant(String name, double value) {
  381       return constant(double.class, name, value);
  382     }
  383   
  384     /**
  385      * Maps a constant value to the given name.
  386      */
  387     public ContainerBuilder constant(String name, float value) {
  388       return constant(float.class, name, value);
  389     }
  390   
  391     /**
  392      * Maps a constant value to the given name.
  393      */
  394     public ContainerBuilder constant(String name, short value) {
  395       return constant(short.class, name, value);
  396     }
  397   
  398     /**
  399      * Maps a constant value to the given name.
  400      */
  401     public ContainerBuilder constant(String name, char value) {
  402       return constant(char.class, name, value);
  403     }
  404   
  405     /**
  406      * Maps a class to the given name.
  407      */
  408     public ContainerBuilder constant(String name, Class value) {
  409       return constant(Class.class, name, value);
  410     }
  411   
  412     /**
  413      * Maps an enum to the given name.
  414      */
  415     public <E extends Enum<E>> ContainerBuilder constant(String name, E value) {
  416       return constant(value.getDeclaringClass(), name, value);
  417     }
  418   
  419     /**
  420      * Maps a constant value to the given type and name.
  421      */
  422     private <T> ContainerBuilder constant(final Class<T> type, final String name,
  423         final T value) {
  424       InternalFactory<T> factory = new InternalFactory<T>() {
  425         public T create(InternalContext ignored) {
  426           return value;
  427         }
  428   
  429         public String toString() {
  430           return new LinkedHashMap<String, Object>() {
  431             {
  432               put("type", type);
  433               put("name", name);
  434               put("value", value);
  435             }
  436           }.toString();
  437         }
  438       };
  439   
  440       return factory(Key.newInstance(type, name), factory, Scope.DEFAULT);
  441     }
  442   
  443     /**
  444      * Upon creation, the {@link Container} will inject static fields and methods
  445      * into the given classes.
  446      *
  447      * @param types for which static members will be injected
  448      */
  449     public ContainerBuilder injectStatics(Class<?>... types) {
  450       staticInjections.addAll(Arrays.asList(types));
  451       return this;
  452     }
  453   
  454     /**
  455      * Returns true if this builder contains a mapping for the given type and
  456      * name.
  457      */
  458     public boolean contains(Class<?> type, String name) {
  459       return factories.containsKey(Key.newInstance(type, name));
  460     }
  461   
  462     /**
  463      * Convenience method.&nbsp;Equivalent to {@code contains(type,
  464      * Container.DEFAULT_NAME)}.
  465      */
  466     public boolean contains(Class<?> type) {
  467       return contains(type, Container.DEFAULT_NAME);
  468     }
  469   
  470     /**
  471      * Creates a {@link Container} instance. Injects static members for classes
  472      * which were registered using {@link #injectStatics(Class...)}.
  473      *
  474      * @param loadSingletons If true, the container will load all singletons
  475      *  now. If false, the container will lazily load singletons. Eager loading
  476      *  is appropriate for production use while lazy loading can speed
  477      *  development.
  478      * @throws IllegalStateException if called more than once
  479      */
  480     public Container create(boolean loadSingletons) {
  481       ensureNotCreated();
  482       created = true;
  483       final ContainerImpl container = new ContainerImpl(
  484           new HashMap<Key<?>, InternalFactory<?>>(factories));
  485       if (loadSingletons) {
  486         container.callInContext(new ContainerImpl.ContextualCallable<Void>() {
  487           public Void call(InternalContext context) {
  488             for (InternalFactory<?> factory : singletonFactories) {
  489               factory.create(context);
  490             }
  491             return null;
  492           }
  493         });
  494       }
  495       container.injectStatics(staticInjections);
  496       return container;
  497     }
  498   
  499     /**
  500      * Currently we only support creating one Container instance per builder.
  501      * If we want to support creating more than one container per builder,
  502      * we should move to a "factory factory" model where we create a factory
  503      * instance per Container. Right now, one factory instance would be
  504      * shared across all the containers, singletons synchronize on the
  505      * container when lazy loading, etc.
  506      */
  507     private void ensureNotCreated() {
  508       if (created) {
  509         throw new IllegalStateException("Container already created.");
  510       }
  511     }
  512     
  513     public void setAllowDuplicates(boolean val) {
  514         allowDuplicates = val;
  515     }
  516   
  517     /**
  518      * Implemented by classes which participate in building a container.
  519      */
  520     public interface Command {
  521   
  522       /**
  523        * Contributes factories to the given builder.
  524        *
  525        * @param builder
  526        */
  527       void build(ContainerBuilder builder);
  528     }
  529   }

Save This Page
Home » xwork-2.1.1-src » com.opensymphony.xwork2.inject » [javadoc | source]