    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}