    001    package 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
    024    import java.util.HashMap;
    025    import java.util.Iterator;
    026    import java.util.List;
    027    import java.util.Map;
    028    import java.util.Vector;
    029
    030    import org.apache.commons.collections.map.LRUMap;
    031    import org.apache.commons.configuration.Configuration;
    032    import org.apache.commons.logging.Log;
    033    import org.apache.commons.logging.LogFactory;
    034    import org.apache.turbine.Turbine;
    035    import org.apache.turbine.TurbineConstants;
    036    import org.apache.turbine.modules.Assembler;
    037    import org.apache.turbine.modules.Loader;
    038    import org.apache.turbine.services.InitializationException;
    039    import org.apache.turbine.services.TurbineBaseService;
    040    import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
    041    import org.apache.turbine.util.TurbineException;
    042
    043    /**
    044     * TurbineAssemblerBrokerService allows assemblers (like screens,
    045     * actions and layouts) to be loaded from one or more AssemblerFactory
    046     * classes.  AssemblerFactory classes are registered with this broker
    047     * by adding them to the TurbineResources.properties file.
    048     *
    049     * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
    050     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    051     * @version $Id: TurbineAssemblerBrokerService.java 1078552 2011-03-06 19:58:46Z tv $
    052     */
    053    public class TurbineAssemblerBrokerService
    054            extends TurbineBaseService
    055            implements AssemblerBrokerService
    056    {
    057        /** Logging */
    058        private static Log log
    059                = LogFactory.getLog(TurbineAssemblerBrokerService.class);
    060
    061        /** A structure that holds the registered AssemblerFactories */
    062        private Map<String, List<AssemblerFactory>> factories = null;
    063
    064        /** A cache that holds the generated Assemblers */
    065        private Map<String, Assembler> assemblerCache = null;
    066
    067        /** A cache that holds the Loaders */
    068        private Map<String, Loader> loaderCache = null;
    069
    070        /** Caching on/off */
    071        private boolean isCaching;
    072
    073        /**
    074         * Get a list of AssemblerFactories of a certain type
    075         *
    076         * @param type type of Assembler
    077         * @return list of AssemblerFactories
    078         */
    079        private List<AssemblerFactory> getFactoryGroup(String type)
    080        {
    081            if (!factories.containsKey(type))
    082            {
    083                factories.put(type, new Vector<AssemblerFactory>());
    084            }
    085            return factories.get(type);
    086        }
    087
    088        /**
    089         * Utiltiy method to register all factories for a given type.
    090         *
    091         * @param type type of Assembler
    092         * @throws TurbineException
    093         */
    094        private void registerFactories(String type)
    095            throws TurbineException
    096        {
    097            List names = getConfiguration().getList(type);
    098
    099            log.info("Registering " + names.size() + " " + type + " factories.");
    100
    101            for (Iterator it = names.iterator(); it.hasNext(); )
    102            {
    103                String factory = (String) it.next();
    104                try
    105                {
    106                    Object o = Class.forName(factory).newInstance();
    107                    registerFactory(type, (AssemblerFactory) o);
    108                }
    109                // these must be passed to the VM
    110                catch (ThreadDeath e)
    111                {
    112                    throw e;
    113                }
    114                catch (OutOfMemoryError e)
    115                {
    116                    throw e;
    117                }
    118                // when using Class.forName(), NoClassDefFoundErrors are likely
    119                // to happen (missing jar files)
    120                catch (Throwable t)
    121                {
    122                    throw new TurbineException("Failed registering " + type
    123                            + " factory: " + factory, t);
    124                }
    125            }
    126        }
    127
    128        /**
    129         * Initializes the AssemblerBroker and loads the AssemblerFactory
    130         * classes registered in TurbineResources.Properties.
    131         *
    132         * @throws InitializationException
    133         */
    134        @SuppressWarnings("unchecked") // as long as commons-collections does not use generics
    135        @Override
    136        public void init()
    137            throws InitializationException
    138        {
    139            factories = new HashMap<String, List<AssemblerFactory>>();
    140
    141            try
    142            {
    143                Configuration conf = getConfiguration();
    144
    145                for (Iterator i = conf.getKeys(); i.hasNext();)
    146                {
    147                    String type = (String)i.next();
    148
    149                    if (!"classname".equalsIgnoreCase(type))
    150                    {
    151                        registerFactories(type);
    152                    }
    153                }
    154            }
    155            catch (TurbineException e)
    156            {
    157                throw new InitializationException(
    158                        "AssemblerBrokerService failed to initialize", e);
    159            }
    160
    161            isCaching = Turbine.getConfiguration()
    162                .getBoolean(TurbineConstants.MODULE_CACHE_KEY,
    163                            TurbineConstants.MODULE_CACHE_DEFAULT);
    164
    165            if (isCaching)
    166            {
    167                int cacheSize = Turbine.getConfiguration()
    168                    .getInt(TurbineConstants.MODULE_CACHE_SIZE_KEY,
    169                            TurbineConstants.MODULE_CACHE_SIZE_DEFAULT);
    170
    171                assemblerCache = new LRUMap(cacheSize);
    172                loaderCache = new LRUMap(cacheSize);
    173            }
    174
    175            setInit(true);
    176        }
    177
    178        /**
    179         * Register a new AssemblerFactory under a certain type
    180         *
    181         * @param type type of Assembler
    182         * @param factory factory to register
    183         */
    184        public void registerFactory(String type, AssemblerFactory factory)
    185        {
    186            getFactoryGroup(type).add(factory);
    187        }
    188
    189        /**
    190         * Attempt to retrieve an Assembler of a given type with
    191         * a name.  Cycle through all the registered AssemblerFactory
    192         * classes of type and return the first non-null assembly
    193         * found.  If an assembly was not found return null.
    194         *
    195         * @param type type of Assembler
    196         * @param name name of the requested Assembler
    197         * @return an Assembler or null
    198         * @throws TurbineException
    199         */
    200        public Assembler getAssembler(String type, String name)
    201            throws TurbineException
    202        {
    203            String key = type + ":" + name;
    204            Assembler assembler = null;
    205
    206            if (isCaching && assemblerCache.containsKey(key))
    207            {
    208                assembler = assemblerCache.get(key);
    209                log.debug("Found " + key + " in the cache!");
    210            }
    211            else
    212            {
    213                log.debug("Loading " + key);
    214                List<AssemblerFactory> facs = getFactoryGroup(type);
    215
    216                for (Iterator<AssemblerFactory> it = facs.iterator(); (assembler == null) && it.hasNext();)
    217                {
    218                    AssemblerFactory fac = it.next();
    219
    220                    try
    221                    {
    222                        assembler = fac.getAssembler(name);
    223                    }
    224                    catch (Exception e)
    225                    {
    226                        throw new TurbineException("Failed to load an assembler for "
    227                                                   + name + " from the "
    228                                                   + type + " factory "
    229                                                   + fac.getClass().getName(), e);
    230                    }
    231
    232                    if (isCaching && assembler != null)
    233                    {
    234                        assemblerCache.put(key, assembler);
    235                    }
    236                }
    237            }
    238
    239            return assembler;
    240        }
    241
    242        /**
    243         * Get a Loader for the given assembler type
    244         *
    245         * @param type The Type of the Assembler
    246         * @return A Loader instance for the requested type
    247         */
    248        public Loader getLoader(String type)
    249        {
    250            Loader loader = null;
    251
    252            if (isCaching && loaderCache.containsKey(type))
    253            {
    254                loader = loaderCache.get(type);
    255                log.debug("Found " + type + " loader in the cache!");
    256            }
    257            else
    258            {
    259                log.debug("Getting Loader for " + type);
    260                List facs = getFactoryGroup(type);
    261
    262                for (Iterator it = facs.iterator(); (loader == null) && it.hasNext();)
    263                {
    264                    AssemblerFactory fac = (AssemblerFactory) it.next();
    265
    266                    loader = fac.getLoader();
    267                }
    268
    269                if (isCaching && loader != null)
    270                {
    271                    loaderCache.put(type, loader);
    272                }
    273            }
    274
    275            if (loader == null)
    276            {
    277                log.warn("Loader for " + type + " is null.");
    278            }
    279
    280            return loader;
    281        }
    282    }