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  import java.util.concurrent.ConcurrentHashMap;
30  import java.util.concurrent.ConcurrentMap;
31  
32  import org.apache.commons.configuration2.Configuration;
33  import org.apache.logging.log4j.LogManager;
34  import org.apache.logging.log4j.Logger;
35  import org.apache.turbine.Turbine;
36  import org.apache.turbine.TurbineConstants;
37  import org.apache.turbine.annotation.AnnotationProcessor;
38  import org.apache.turbine.modules.Assembler;
39  import org.apache.turbine.modules.Loader;
40  import org.apache.turbine.services.InitializationException;
41  import org.apache.turbine.services.TurbineBaseService;
42  import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
43  import org.apache.turbine.util.TurbineException;
44  
45  /**
46   * TurbineAssemblerBrokerService allows assemblers (like screens,
47   * actions and layouts) to be loaded from one or more AssemblerFactory
48   * classes.  AssemblerFactory classes are registered with this broker
49   * by adding them to the TurbineResources.properties file.
50   *
51   * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
52   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
53   * @version $Id: TurbineAssemblerBrokerService.java 1854688 2019-03-03 10:36:42Z tv $
54   */
55  public class TurbineAssemblerBrokerService
56          extends TurbineBaseService
57          implements AssemblerBrokerService
58  {
59      /** Logging */
60      private static Logger log
61              = LogManager.getLogger(TurbineAssemblerBrokerService.class);
62  
63      /** A structure that holds the registered AssemblerFactories */
64      private Map<Class<?>, List<?>> factories = null;
65  
66      /** A cache that holds the generated Assemblers */
67      private ConcurrentMap<String, Assembler> assemblerCache = null;
68  
69      /** A cache that holds the Loaders */
70      private ConcurrentMap<Class<?>, Loader<? extends Assembler>> loaderCache = null;
71  
72      /** Caching on/off */
73      private boolean isCaching;
74  
75      /**
76       * Get a list of AssemblerFactories of a certain type
77       *
78       * @param type type of Assembler
79       *
80       * @param <T> the type of the assembler
81       *
82       * @return list of AssemblerFactories
83       */
84      @SuppressWarnings("unchecked")
85      private <T extends Assembler> List<AssemblerFactory<T>> getFactoryGroup(Class<T> type)
86      {
87          if (!factories.containsKey(type))
88          {
89              factories.put(type, new ArrayList<AssemblerFactory<T>>());
90          }
91          return (List<AssemblerFactory<T>>) factories.get(type);
92      }
93  
94      /**
95       * Utility method to register all factories for a given type.
96       *
97       * @param type type of Assembler
98       * @throws TurbineException if the factory for the given type could not be registered
99       */
100     private void registerFactories(String type)
101         throws TurbineException
102     {
103         List<Object> names = getConfiguration().getList(type);
104 
105         log.info("Registering {} {} factories.", Integer.valueOf(names.size()), type);
106 
107         for (Iterator<Object> it = names.iterator(); it.hasNext(); )
108         {
109             String factory = (String) it.next();
110             try
111             {
112                 @SuppressWarnings("unchecked")
113                 AssemblerFactory<? extends Assembler> af =
114                     (AssemblerFactory<? extends Assembler>) Class.forName(factory).newInstance();
115                 registerFactory(af);
116             }
117             // these must be passed to the VM
118             catch (ThreadDeath | 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 if problems occur while registering the factories
137      */
138     @Override
139     public void init()
140         throws InitializationException
141     {
142         factories = new HashMap<Class<?>, List<?>>();
143 
144         try
145         {
146             Configuration conf = getConfiguration();
147 
148             for (Iterator<String> i = conf.getKeys(); i.hasNext();)
149             {
150                 String type = i.next();
151 
152                 if (!"classname".equalsIgnoreCase(type))
153                 {
154                     registerFactories(type);
155                 }
156             }
157         }
158         catch (TurbineException e)
159         {
160             throw new InitializationException(
161                     "AssemblerBrokerService failed to initialize", e);
162         }
163 
164         isCaching = Turbine.getConfiguration()
165             .getBoolean(TurbineConstants.MODULE_CACHE_KEY,
166                         TurbineConstants.MODULE_CACHE_DEFAULT);
167 
168         if (isCaching)
169         {
170             int cacheSize = Turbine.getConfiguration()
171                 .getInt(TurbineConstants.MODULE_CACHE_SIZE_KEY,
172                         TurbineConstants.MODULE_CACHE_SIZE_DEFAULT);
173 
174             assemblerCache = new ConcurrentHashMap<String, Assembler>(cacheSize);
175             loaderCache = new ConcurrentHashMap<Class<?>, Loader<? extends Assembler>>(cacheSize);
176         }
177 
178         setInit(true);
179     }
180 
181     /**
182      * Register a new AssemblerFactory
183      *
184      * @param factory factory to register
185      *
186      * @param <T> the type of the assembler
187      *
188      */
189     @Override
190     public <T extends Assembler> void registerFactory(AssemblerFactory<T> factory)
191     {
192         getFactoryGroup(factory.getManagedClass()).add(factory);
193     }
194 
195     /**
196      * Attempt to retrieve an Assembler of a given type with
197      * a name.  Cycle through all the registered AssemblerFactory
198      * classes of type and return the first non-null assembly
199      * found.  If an assembly was not found return null.
200      *
201      * @param type type of Assembler
202      * @param name name of the requested Assembler
203      *
204      * @param <T> the type of the assembler
205      *
206      * @return an Assembler or null
207      * @throws TurbineException if the assembler could not be loaded
208      */
209     @Override
210     @SuppressWarnings("unchecked")
211     public <T extends Assembler> T getAssembler(Class<T> type, String name)
212         throws TurbineException
213     {
214         String key = type + ":" + name;
215         T assembler = null;
216 
217         if (isCaching && assemblerCache.containsKey(key))
218         {
219             assembler = (T) assemblerCache.get(key);
220             log.debug("Found {} in the cache!", key);
221         }
222         else
223         {
224             log.debug("Loading {}", key);
225             List<AssemblerFactory<T>> facs = getFactoryGroup(type);
226 
227             for (Iterator<AssemblerFactory<T>> it = facs.iterator(); (assembler == null) && it.hasNext();)
228             {
229                 AssemblerFactory<T> fac = it.next();
230 
231                 try
232                 {
233                     assembler = fac.getAssembler(name);
234                 }
235                 catch (Exception e)
236                 {
237                     throw new TurbineException("Failed to load an assembler for "
238                                                + name + " from the "
239                                                + type + " factory "
240                                                + fac.getClass().getName(), e);
241                 }
242 
243                 if (assembler != null)
244                 {
245                     AnnotationProcessor.process(assembler);
246 
247                     if (isCaching)
248                     {
249                         T oldAssembler = (T) assemblerCache.putIfAbsent(key, assembler);
250                         if (oldAssembler != null)
251                         {
252                             assembler = oldAssembler;
253                         }
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 {} loader in the cache!", type);
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 {} is null.", type);
302         }
303 
304         return loader;
305     }
306 }