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 }