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 1521999 2013-09-11 19:26:21Z 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 * @return list of AssemblerFactories 079 */ 080 @SuppressWarnings("unchecked") 081 private <T extends Assembler> List<AssemblerFactory<T>> getFactoryGroup(Class<T> type) 082 { 083 if (!factories.containsKey(type)) 084 { 085 factories.put(type, new ArrayList<AssemblerFactory<T>>()); 086 } 087 return (List<AssemblerFactory<T>>) factories.get(type); 088 } 089 090 /** 091 * Utility method to register all factories for a given type. 092 * 093 * @param type type of Assembler 094 * @throws TurbineException 095 */ 096 private void registerFactories(String type) 097 throws TurbineException 098 { 099 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}