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    }