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 1773378 2016-12-09 13:19:59Z 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       *
    79       * @param <T> the type of the assembler
    80       *
    81       * @return list of AssemblerFactories
    82       */
    83      @SuppressWarnings("unchecked")
    84      private <T extends Assembler> List<AssemblerFactory<T>> getFactoryGroup(Class<T> type)
    85      {
    86          if (!factories.containsKey(type))
    87          {
    88              factories.put(type, new ArrayList<AssemblerFactory<T>>());
    89          }
    90          return (List<AssemblerFactory<T>>) factories.get(type);
    91      }
    92
    93      /**
    94       * Utility method to register all factories for a given type.
    95       *
    96       * @param type type of Assembler
    97       * @throws TurbineException if the factory for the given type could not be registered
    98       */
    99      private void registerFactories(String type)
    100         throws TurbineException
    101     {
    102         List<Object> names = getConfiguration().getList(type);
    103
    104         log.info("Registering " + names.size() + " " + type + " factories.");
    105
    106         for (Iterator<Object> it = names.iterator(); it.hasNext(); )
    107         {
    108             String factory = (String) it.next();
    109             try
    110             {
    111                 @SuppressWarnings("unchecked")
    112                 AssemblerFactory<? extends Assembler> af =
    113                     (AssemblerFactory<? extends Assembler>) Class.forName(factory).newInstance();
    114                 registerFactory(af);
    115             }
    116             // these must be passed to the VM
    117             catch (ThreadDeath e)
    118             {
    119                 throw e;
    120             }
    121             catch (OutOfMemoryError e)
    122             {
    123                 throw e;
    124             }
    125             // when using Class.forName(), NoClassDefFoundErrors are likely
    126             // to happen (missing jar files)
    127             catch (Throwable t)
    128             {
    129                 throw new TurbineException("Failed registering " + type
    130                         + " factory: " + factory, t);
    131             }
    132         }
    133     }
    134
    135     /**
    136      * Initializes the AssemblerBroker and loads the AssemblerFactory
    137      * classes registered in TurbineResources.Properties.
    138      *
    139      * @throws InitializationException if problems occur while registering the factories
    140      */
    141     @SuppressWarnings("unchecked") // as long as commons-collections does not use generics
    142     @Override
    143     public void init()
    144         throws InitializationException
    145     {
    146         factories = new HashMap<Class<?>, List<?>>();
    147
    148         try
    149         {
    150             Configuration conf = getConfiguration();
    151
    152             for (Iterator<String> i = conf.getKeys(); i.hasNext();)
    153             {
    154                 String type = i.next();
    155
    156                 if (!"classname".equalsIgnoreCase(type))
    157                 {
    158                     registerFactories(type);
    159                 }
    160             }
    161         }
    162         catch (TurbineException e)
    163         {
    164             throw new InitializationException(
    165                     "AssemblerBrokerService failed to initialize", e);
    166         }
    167
    168         isCaching = Turbine.getConfiguration()
    169             .getBoolean(TurbineConstants.MODULE_CACHE_KEY,
    170                         TurbineConstants.MODULE_CACHE_DEFAULT);
    171
    172         if (isCaching)
    173         {
    174             int cacheSize = Turbine.getConfiguration()
    175                 .getInt(TurbineConstants.MODULE_CACHE_SIZE_KEY,
    176                         TurbineConstants.MODULE_CACHE_SIZE_DEFAULT);
    177
    178             assemblerCache = new LRUMap(cacheSize);
    179             loaderCache = new LRUMap(cacheSize);
    180         }
    181
    182         setInit(true);
    183     }
    184
    185     /**
    186      * Register a new AssemblerFactory
    187      *
    188      * @param factory factory to register
    189      *
    190      * @param <T> the type of the assembler
    191      *
    192      */
    193     @Override
    194     public <T extends Assembler> void registerFactory(AssemblerFactory<T> factory)
    195     {
    196         getFactoryGroup(factory.getManagedClass()).add(factory);
    197     }
    198
    199     /**
    200      * Attempt to retrieve an Assembler of a given type with
    201      * a name.  Cycle through all the registered AssemblerFactory
    202      * classes of type and return the first non-null assembly
    203      * found.  If an assembly was not found return null.
    204      *
    205      * @param type type of Assembler
    206      * @param name name of the requested Assembler
    207      *
    208      * @param <T> the type of the assembler
    209      *
    210      * @return an Assembler or null
    211      * @throws TurbineException if the assembler could not be loaded
    212      */
    213     @Override
    214     @SuppressWarnings("unchecked")
    215     public <T extends Assembler> T getAssembler(Class<T> type, String name)
    216         throws TurbineException
    217     {
    218         String key = type + ":" + name;
    219         T assembler = null;
    220
    221         if (isCaching && assemblerCache.containsKey(key))
    222         {
    223             assembler = (T) assemblerCache.get(key);
    224             log.debug("Found " + key + " in the cache!");
    225         }
    226         else
    227         {
    228             log.debug("Loading " + key);
    229             List<AssemblerFactory<T>> facs = getFactoryGroup(type);
    230
    231             for (Iterator<AssemblerFactory<T>> it = facs.iterator(); (assembler == null) && it.hasNext();)
    232             {
    233                 AssemblerFactory<T> fac = it.next();
    234
    235                 try
    236                 {
    237                     assembler = fac.getAssembler(name);
    238                 }
    239                 catch (Exception e)
    240                 {
    241                     throw new TurbineException("Failed to load an assembler for "
    242                                                + name + " from the "
    243                                                + type + " factory "
    244                                                + fac.getClass().getName(), e);
    245                 }
    246
    247                 if (assembler != null)
    248                 {
    249                     AnnotationProcessor.process(assembler);
    250
    251                     if (isCaching)
    252                     {
    253                         assemblerCache.put(key, assembler);
    254                     }
    255                 }
    256             }
    257         }
    258
    259         return assembler;
    260     }
    261
    262     /**
    263      * Get a Loader for the given assembler type
    264      *
    265      * @param type The Type of the Assembler
    266      *
    267      * @param <T> the type of the assembler
    268      *
    269      * @return A Loader instance for the requested type
    270      */
    271     @Override
    272     @SuppressWarnings("unchecked")
    273     public <T extends Assembler> Loader<T> getLoader(Class<T> type)
    274     {
    275         Loader<T> loader = null;
    276
    277         if (isCaching && loaderCache.containsKey(type))
    278         {
    279             loader = (Loader<T>) loaderCache.get(type);
    280             log.debug("Found " + type + " loader in the cache!");
    281         }
    282         else
    283         {
    284             log.debug("Getting Loader for " + type);
    285             List<AssemblerFactory<T>> facs = getFactoryGroup(type);
    286
    287             for (Iterator<AssemblerFactory<T>> it = facs.iterator(); (loader == null) && it.hasNext();)
    288             {
    289                 AssemblerFactory<T> fac = it.next();
    290                 loader = fac.getLoader();
    291             }
    292
    293             if (isCaching && loader != null)
    294             {
    295                 loaderCache.put(type, loader);
    296             }
    297         }
    298
    299         if (loader == null)
    300         {
    301             log.warn("Loader for " + type + " is null.");
    302         }
    303
    304         return loader;
    305     }
    306 }