001 package org.apache.turbine.services.assemblerbroker;
002
003
004 /*
005 * Licensed to the Apache Software Foundation (ASF) under one
006 * or more contributor license agreements. See the NOTICE file
007 * distributed with this work for additional information
008 * regarding copyright ownership. The ASF licenses this file
009 * to you under the Apache License, Version 2.0 (the
010 * "License"); you may not use this file except in compliance
011 * with the License. You may obtain a copy of the License at
012 *
013 * http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing,
016 * software distributed under the License is distributed on an
017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018 * KIND, either express or implied. See the License for the
019 * specific language governing permissions and limitations
020 * under the License.
021 */
022
023
024 import java.util.HashMap;
025 import java.util.Iterator;
026 import java.util.List;
027 import java.util.Map;
028 import java.util.Vector;
029
030 import org.apache.commons.collections.map.LRUMap;
031 import org.apache.commons.configuration.Configuration;
032 import org.apache.commons.logging.Log;
033 import org.apache.commons.logging.LogFactory;
034 import org.apache.turbine.Turbine;
035 import org.apache.turbine.TurbineConstants;
036 import org.apache.turbine.modules.Assembler;
037 import org.apache.turbine.modules.Loader;
038 import org.apache.turbine.services.InitializationException;
039 import org.apache.turbine.services.TurbineBaseService;
040 import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
041 import org.apache.turbine.util.TurbineException;
042
043 /**
044 * TurbineAssemblerBrokerService allows assemblers (like screens,
045 * actions and layouts) to be loaded from one or more AssemblerFactory
046 * classes. AssemblerFactory classes are registered with this broker
047 * by adding them to the TurbineResources.properties file.
048 *
049 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
050 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
051 * @version $Id: TurbineAssemblerBrokerService.java 1078552 2011-03-06 19:58:46Z tv $
052 */
053 public class TurbineAssemblerBrokerService
054 extends TurbineBaseService
055 implements AssemblerBrokerService
056 {
057 /** Logging */
058 private static Log log
059 = LogFactory.getLog(TurbineAssemblerBrokerService.class);
060
061 /** A structure that holds the registered AssemblerFactories */
062 private Map<String, List<AssemblerFactory>> factories = null;
063
064 /** A cache that holds the generated Assemblers */
065 private Map<String, Assembler> assemblerCache = null;
066
067 /** A cache that holds the Loaders */
068 private Map<String, Loader> loaderCache = null;
069
070 /** Caching on/off */
071 private boolean isCaching;
072
073 /**
074 * Get a list of AssemblerFactories of a certain type
075 *
076 * @param type type of Assembler
077 * @return list of AssemblerFactories
078 */
079 private List<AssemblerFactory> getFactoryGroup(String type)
080 {
081 if (!factories.containsKey(type))
082 {
083 factories.put(type, new Vector<AssemblerFactory>());
084 }
085 return factories.get(type);
086 }
087
088 /**
089 * Utiltiy method to register all factories for a given type.
090 *
091 * @param type type of Assembler
092 * @throws TurbineException
093 */
094 private void registerFactories(String type)
095 throws TurbineException
096 {
097 List names = getConfiguration().getList(type);
098
099 log.info("Registering " + names.size() + " " + type + " factories.");
100
101 for (Iterator it = names.iterator(); it.hasNext(); )
102 {
103 String factory = (String) it.next();
104 try
105 {
106 Object o = Class.forName(factory).newInstance();
107 registerFactory(type, (AssemblerFactory) o);
108 }
109 // these must be passed to the VM
110 catch (ThreadDeath e)
111 {
112 throw e;
113 }
114 catch (OutOfMemoryError e)
115 {
116 throw e;
117 }
118 // when using Class.forName(), NoClassDefFoundErrors are likely
119 // to happen (missing jar files)
120 catch (Throwable t)
121 {
122 throw new TurbineException("Failed registering " + type
123 + " factory: " + factory, t);
124 }
125 }
126 }
127
128 /**
129 * Initializes the AssemblerBroker and loads the AssemblerFactory
130 * classes registered in TurbineResources.Properties.
131 *
132 * @throws InitializationException
133 */
134 @SuppressWarnings("unchecked") // as long as commons-collections does not use generics
135 @Override
136 public void init()
137 throws InitializationException
138 {
139 factories = new HashMap<String, List<AssemblerFactory>>();
140
141 try
142 {
143 Configuration conf = getConfiguration();
144
145 for (Iterator i = conf.getKeys(); i.hasNext();)
146 {
147 String type = (String)i.next();
148
149 if (!"classname".equalsIgnoreCase(type))
150 {
151 registerFactories(type);
152 }
153 }
154 }
155 catch (TurbineException e)
156 {
157 throw new InitializationException(
158 "AssemblerBrokerService failed to initialize", e);
159 }
160
161 isCaching = Turbine.getConfiguration()
162 .getBoolean(TurbineConstants.MODULE_CACHE_KEY,
163 TurbineConstants.MODULE_CACHE_DEFAULT);
164
165 if (isCaching)
166 {
167 int cacheSize = Turbine.getConfiguration()
168 .getInt(TurbineConstants.MODULE_CACHE_SIZE_KEY,
169 TurbineConstants.MODULE_CACHE_SIZE_DEFAULT);
170
171 assemblerCache = new LRUMap(cacheSize);
172 loaderCache = new LRUMap(cacheSize);
173 }
174
175 setInit(true);
176 }
177
178 /**
179 * Register a new AssemblerFactory under a certain type
180 *
181 * @param type type of Assembler
182 * @param factory factory to register
183 */
184 public void registerFactory(String type, AssemblerFactory factory)
185 {
186 getFactoryGroup(type).add(factory);
187 }
188
189 /**
190 * Attempt to retrieve an Assembler of a given type with
191 * a name. Cycle through all the registered AssemblerFactory
192 * classes of type and return the first non-null assembly
193 * found. If an assembly was not found return null.
194 *
195 * @param type type of Assembler
196 * @param name name of the requested Assembler
197 * @return an Assembler or null
198 * @throws TurbineException
199 */
200 public Assembler getAssembler(String type, String name)
201 throws TurbineException
202 {
203 String key = type + ":" + name;
204 Assembler assembler = null;
205
206 if (isCaching && assemblerCache.containsKey(key))
207 {
208 assembler = assemblerCache.get(key);
209 log.debug("Found " + key + " in the cache!");
210 }
211 else
212 {
213 log.debug("Loading " + key);
214 List<AssemblerFactory> facs = getFactoryGroup(type);
215
216 for (Iterator<AssemblerFactory> it = facs.iterator(); (assembler == null) && it.hasNext();)
217 {
218 AssemblerFactory fac = it.next();
219
220 try
221 {
222 assembler = fac.getAssembler(name);
223 }
224 catch (Exception e)
225 {
226 throw new TurbineException("Failed to load an assembler for "
227 + name + " from the "
228 + type + " factory "
229 + fac.getClass().getName(), e);
230 }
231
232 if (isCaching && assembler != null)
233 {
234 assemblerCache.put(key, assembler);
235 }
236 }
237 }
238
239 return assembler;
240 }
241
242 /**
243 * Get a Loader for the given assembler type
244 *
245 * @param type The Type of the Assembler
246 * @return A Loader instance for the requested type
247 */
248 public Loader getLoader(String type)
249 {
250 Loader loader = null;
251
252 if (isCaching && loaderCache.containsKey(type))
253 {
254 loader = loaderCache.get(type);
255 log.debug("Found " + type + " loader in the cache!");
256 }
257 else
258 {
259 log.debug("Getting Loader for " + type);
260 List facs = getFactoryGroup(type);
261
262 for (Iterator it = facs.iterator(); (loader == null) && it.hasNext();)
263 {
264 AssemblerFactory fac = (AssemblerFactory) it.next();
265
266 loader = fac.getLoader();
267 }
268
269 if (isCaching && loader != null)
270 {
271 loaderCache.put(type, loader);
272 }
273 }
274
275 if (loader == null)
276 {
277 log.warn("Loader for " + type + " is null.");
278 }
279
280 return loader;
281 }
282 }