001package 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 024import java.util.ArrayList; 025import java.util.HashMap; 026import java.util.Iterator; 027import java.util.List; 028import java.util.Map; 029 030import org.apache.commons.collections.map.LRUMap; 031import org.apache.commons.configuration.Configuration; 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.apache.turbine.Turbine; 035import org.apache.turbine.TurbineConstants; 036import org.apache.turbine.annotation.AnnotationProcessor; 037import org.apache.turbine.modules.Assembler; 038import org.apache.turbine.modules.Loader; 039import org.apache.turbine.services.InitializationException; 040import org.apache.turbine.services.TurbineBaseService; 041import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory; 042import org.apache.turbine.util.TurbineException; 043 044/** 045 * TurbineAssemblerBrokerService allows assemblers (like screens, 046 * actions and layouts) to be loaded from one or more AssemblerFactory 047 * classes. AssemblerFactory classes are registered with this broker 048 * by adding them to the TurbineResources.properties file. 049 * 050 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a> 051 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> 052 * @version $Id: TurbineAssemblerBrokerService.java 1773378 2016-12-09 13:19:59Z tv $ 053 */ 054public class TurbineAssemblerBrokerService 055 extends TurbineBaseService 056 implements AssemblerBrokerService 057{ 058 /** Logging */ 059 private static Log log 060 = LogFactory.getLog(TurbineAssemblerBrokerService.class); 061 062 /** A structure that holds the registered AssemblerFactories */ 063 private Map<Class<?>, List<?>> factories = null; 064 065 /** A cache that holds the generated Assemblers */ 066 private Map<String, Assembler> assemblerCache = null; 067 068 /** A cache that holds the Loaders */ 069 private Map<Class<?>, Loader<? extends Assembler>> loaderCache = null; 070 071 /** Caching on/off */ 072 private boolean isCaching; 073 074 /** 075 * Get a list of AssemblerFactories of a certain type 076 * 077 * @param type type of Assembler 078 * 079 * @param <T> the type of the assembler 080 * 081 * @return list of AssemblerFactories 082 */ 083 @SuppressWarnings("unchecked") 084 private <T extends Assembler> List<AssemblerFactory<T>> getFactoryGroup(Class<T> type) 085 { 086 if (!factories.containsKey(type)) 087 { 088 factories.put(type, new ArrayList<AssemblerFactory<T>>()); 089 } 090 return (List<AssemblerFactory<T>>) factories.get(type); 091 } 092 093 /** 094 * Utility method to register all factories for a given type. 095 * 096 * @param type type of Assembler 097 * @throws TurbineException if the factory for the given type could not be registered 098 */ 099 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}