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