View Javadoc

    1   package org.apache.turbine.services.assemblerbroker;
    2
    3
    4   /*
    5    * Licensed to the Apache Software Foundation (ASF) under one
    6    * or more contributor license agreements.  See the NOTICE file
    7    * distributed with this work for additional information
    8    * regarding copyright ownership.  The ASF licenses this file
    9    * to you under the Apache License, Version 2.0 (the
    10   * "License"); you may not use this file except in compliance
    11   * with the License.  You may obtain a copy of the License at
    12   *
    13   *   http://www.apache.org/licenses/LICENSE-2.0
    14   *
    15   * Unless required by applicable law or agreed to in writing,
    16   * software distributed under the License is distributed on an
    17   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    18   * KIND, either express or implied.  See the License for the
    19   * specific language governing permissions and limitations
    20   * under the License.
    21   */
    22
    23
    24  import java.util.ArrayList;
    25  import java.util.HashMap;
    26  import java.util.Iterator;
    27  import java.util.List;
    28  import java.util.Map;
    29
    30  import org.apache.commons.collections.map.LRUMap;
    31  import org.apache.commons.configuration.Configuration;
    32  import org.apache.commons.logging.Log;
    33  import org.apache.commons.logging.LogFactory;
    34  import org.apache.turbine.Turbine;
    35  import org.apache.turbine.TurbineConstants;
    36  import org.apache.turbine.annotation.AnnotationProcessor;
    37  import org.apache.turbine.modules.Assembler;
    38  import org.apache.turbine.modules.Loader;
    39  import org.apache.turbine.services.InitializationException;
    40  import org.apache.turbine.services.TurbineBaseService;
    41  import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
    42  import org.apache.turbine.util.TurbineException;
    43
    44  /**
    45   * TurbineAssemblerBrokerService allows assemblers (like screens,
    46   * actions and layouts) to be loaded from one or more AssemblerFactory
    47   * classes.  AssemblerFactory classes are registered with this broker
    48   * by adding them to the TurbineResources.properties file.
    49   *
    50   * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
    51   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    52   * @version $Id: TurbineAssemblerBrokerService.java 1521999 2013-09-11 19:26:21Z tv $
    53   */
    54  public class TurbineAssemblerBrokerService
    55          extends TurbineBaseService
    56          implements AssemblerBrokerService
    57  {
    58      /** Logging */
    59      private static Log log
    60              = LogFactory.getLog(TurbineAssemblerBrokerService.class);
    61
    62      /** A structure that holds the registered AssemblerFactories */
    63      private Map<Class<?>, List<?>> factories = null;
    64
    65      /** A cache that holds the generated Assemblers */
    66      private Map<String, Assembler> assemblerCache = null;
    67
    68      /** A cache that holds the Loaders */
    69      private Map<Class<?>, Loader<? extends Assembler>> loaderCache = null;
    70
    71      /** Caching on/off */
    72      private boolean isCaching;
    73
    74      /**
    75       * Get a list of AssemblerFactories of a certain type
    76       *
    77       * @param type type of Assembler
    78       * @return list of AssemblerFactories
    79       */
    80      @SuppressWarnings("unchecked")
    81      private <T extends Assembler> List<AssemblerFactory<T>> getFactoryGroup(Class<T> type)
    82      {
    83          if (!factories.containsKey(type))
    84          {
    85              factories.put(type, new ArrayList<AssemblerFactory<T>>());
    86          }
    87          return (List<AssemblerFactory<T>>) factories.get(type);
    88      }
    89
    90      /**
    91       * Utility method to register all factories for a given type.
    92       *
    93       * @param type type of Assembler
    94       * @throws TurbineException
    95       */
    96      private void registerFactories(String type)
    97          throws TurbineException
    98      {
    99          List<Object> names = getConfiguration().getList(type);
    100
    101         log.info("Registering " + names.size() + " " + type + " factories.");
    102
    103         for (Iterator<Object> it = names.iterator(); it.hasNext(); )
    104         {
    105             String factory = (String) it.next();
    106             try
    107             {
    108                 @SuppressWarnings("unchecked")
    109                 AssemblerFactory<? extends Assembler> af =
    110                     (AssemblerFactory<? extends Assembler>) Class.forName(factory).newInstance();
    111                 registerFactory(af);
    112             }
    113             // these must be passed to the VM
    114             catch (ThreadDeath e)
    115             {
    116                 throw e;
    117             }
    118             catch (OutOfMemoryError e)
    119             {
    120                 throw e;
    121             }
    122             // when using Class.forName(), NoClassDefFoundErrors are likely
    123             // to happen (missing jar files)
    124             catch (Throwable t)
    125             {
    126                 throw new TurbineException("Failed registering " + type
    127                         + " factory: " + factory, t);
    128             }
    129         }
    130     }
    131
    132     /**
    133      * Initializes the AssemblerBroker and loads the AssemblerFactory
    134      * classes registered in TurbineResources.Properties.
    135      *
    136      * @throws InitializationException
    137      */
    138     @SuppressWarnings("unchecked") // as long as commons-collections does not use generics
    139     @Override
    140     public void init()
    141         throws InitializationException
    142     {
    143         factories = new HashMap<Class<?>, List<?>>();
    144
    145         try
    146         {
    147             Configuration conf = getConfiguration();
    148
    149             for (Iterator<String> i = conf.getKeys(); i.hasNext();)
    150             {
    151                 String type = i.next();
    152
    153                 if (!"classname".equalsIgnoreCase(type))
    154                 {
    155                     registerFactories(type);
    156                 }
    157             }
    158         }
    159         catch (TurbineException e)
    160         {
    161             throw new InitializationException(
    162                     "AssemblerBrokerService failed to initialize", e);
    163         }
    164
    165         isCaching = Turbine.getConfiguration()
    166             .getBoolean(TurbineConstants.MODULE_CACHE_KEY,
    167                         TurbineConstants.MODULE_CACHE_DEFAULT);
    168
    169         if (isCaching)
    170         {
    171             int cacheSize = Turbine.getConfiguration()
    172                 .getInt(TurbineConstants.MODULE_CACHE_SIZE_KEY,
    173                         TurbineConstants.MODULE_CACHE_SIZE_DEFAULT);
    174
    175             assemblerCache = new LRUMap(cacheSize);
    176             loaderCache = new LRUMap(cacheSize);
    177         }
    178
    179         setInit(true);
    180     }
    181
    182     /**
    183      * Register a new AssemblerFactory
    184      *
    185      * @param factory factory to register
    186      */
    187     public <T extends Assembler> void registerFactory(AssemblerFactory<T> factory)
    188     {
    189         getFactoryGroup(factory.getManagedClass()).add(factory);
    190     }
    191
    192     /**
    193      * Attempt to retrieve an Assembler of a given type with
    194      * a name.  Cycle through all the registered AssemblerFactory
    195      * classes of type and return the first non-null assembly
    196      * found.  If an assembly was not found return null.
    197      *
    198      * @param type type of Assembler
    199      * @param name name of the requested Assembler
    200      * @return an Assembler or null
    201      * @throws TurbineException
    202      */
    203     @SuppressWarnings("unchecked")
    204     public <T extends Assembler> T getAssembler(Class<T> type, String name)
    205         throws TurbineException
    206     {
    207         String key = type + ":" + name;
    208         T assembler = null;
    209
    210         if (isCaching && assemblerCache.containsKey(key))
    211         {
    212             assembler = (T) assemblerCache.get(key);
    213             log.debug("Found " + key + " in the cache!");
    214         }
    215         else
    216         {
    217             log.debug("Loading " + key);
    218             List<AssemblerFactory<T>> facs = getFactoryGroup(type);
    219
    220             for (Iterator<AssemblerFactory<T>> it = facs.iterator(); (assembler == null) && it.hasNext();)
    221             {
    222                 AssemblerFactory<T> fac = it.next();
    223
    224                 try
    225                 {
    226                     assembler = fac.getAssembler(name);
    227                 }
    228                 catch (Exception e)
    229                 {
    230                     throw new TurbineException("Failed to load an assembler for "
    231                                                + name + " from the "
    232                                                + type + " factory "
    233                                                + fac.getClass().getName(), e);
    234                 }
    235
    236                 if (assembler != null)
    237                 {
    238                     AnnotationProcessor.process(assembler);
    239
    240                     if (isCaching)
    241                     {
    242                         assemblerCache.put(key, assembler);
    243                     }
    244                 }
    245             }
    246         }
    247
    248         return assembler;
    249     }
    250
    251     /**
    252      * Get a Loader for the given assembler type
    253      *
    254      * @param type The Type of the Assembler
    255      * @return A Loader instance for the requested type
    256      */
    257     @SuppressWarnings("unchecked")
    258     public <T extends Assembler> Loader<T> getLoader(Class<T> type)
    259     {
    260         Loader<T> loader = null;
    261
    262         if (isCaching && loaderCache.containsKey(type))
    263         {
    264             loader = (Loader<T>) loaderCache.get(type);
    265             log.debug("Found " + type + " loader in the cache!");
    266         }
    267         else
    268         {
    269             log.debug("Getting Loader for " + type);
    270             List<AssemblerFactory<T>> facs = getFactoryGroup(type);
    271
    272             for (Iterator<AssemblerFactory<T>> it = facs.iterator(); (loader == null) && it.hasNext();)
    273             {
    274                 AssemblerFactory<T> fac = it.next();
    275                 loader = fac.getLoader();
    276             }
    277
    278             if (isCaching && loader != null)
    279             {
    280                 loaderCache.put(type, loader);
    281             }
    282         }
    283
    284         if (loader == null)
    285         {
    286             log.warn("Loader for " + type + " is null.");
    287         }
    288
    289         return loader;
    290     }
    291 }