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

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