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 }