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}