    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}