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;
029import java.util.concurrent.ConcurrentHashMap;
030import java.util.concurrent.ConcurrentMap;
031
032import org.apache.commons.configuration2.Configuration;
033import org.apache.logging.log4j.LogManager;
034import org.apache.logging.log4j.Logger;
035import org.apache.turbine.Turbine;
036import org.apache.turbine.TurbineConstants;
037import org.apache.turbine.annotation.AnnotationProcessor;
038import org.apache.turbine.modules.Assembler;
039import org.apache.turbine.modules.Loader;
040import org.apache.turbine.services.InitializationException;
041import org.apache.turbine.services.TurbineBaseService;
042import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
043import org.apache.turbine.util.TurbineException;
044
045/**
046 * TurbineAssemblerBrokerService allows assemblers (like screens,
047 * actions and layouts) to be loaded from one or more AssemblerFactory
048 * classes.  AssemblerFactory classes are registered with this broker
049 * by adding them to the TurbineResources.properties file.
050 *
051 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
052 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
053 * @version $Id$
054 */
055public class TurbineAssemblerBrokerService
056        extends TurbineBaseService
057        implements AssemblerBrokerService
058{
059    /** Logging */
060    private static Logger log
061            = LogManager.getLogger(TurbineAssemblerBrokerService.class);
062
063    /** A structure that holds the registered AssemblerFactories */
064    private Map<Class<?>, List<?>> factories = null;
065
066    /** A cache that holds the generated Assemblers */
067    private ConcurrentMap<String, Assembler> assemblerCache = null;
068
069    /** A cache that holds the Loaders */
070    private ConcurrentMap<Class<?>, Loader<? extends Assembler>> loaderCache = null;
071
072    /** Caching on/off */
073    private boolean isCaching;
074
075    /**
076     * Get a list of AssemblerFactories of a certain type
077     *
078     * @param type type of Assembler
079     *
080     * @param <T> the type of the assembler
081     *
082     * @return list of AssemblerFactories
083     */
084    @SuppressWarnings("unchecked")
085    private <T extends Assembler> List<AssemblerFactory<T>> getFactoryGroup(Class<T> type)
086    {
087        if (!factories.containsKey(type))
088        {
089            factories.put(type, new ArrayList<AssemblerFactory<T>>());
090        }
091        return (List<AssemblerFactory<T>>) factories.get(type);
092    }
093
094    /**
095     * Utility method to register all factories for a given type.
096     *
097     * @param type type of Assembler
098     * @throws TurbineException if the factory for the given type could not be registered
099     */
100    private void registerFactories(String type)
101        throws TurbineException
102    {
103        List<Object> names = getConfiguration().getList(type);
104
105        log.info("Registering {} {} factories.", Integer.valueOf(names.size()), type);
106
107        for (Object name2 : names)
108        {
109            String factory = (String) name2;
110            try
111            {
112                @SuppressWarnings("unchecked")
113                AssemblerFactory<? extends Assembler> af =
114                    (AssemblerFactory<? extends Assembler>) Class.forName(factory).newInstance();
115                registerFactory(af);
116            }
117            // these must be passed to the VM
118            catch (ThreadDeath | 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 if problems occur while registering the factories
137     */
138    @Override
139    public void init()
140        throws InitializationException
141    {
142        factories = new HashMap<>();
143
144        try
145        {
146            Configuration conf = getConfiguration();
147
148            for (Iterator<String> i = conf.getKeys(); i.hasNext();)
149            {
150                String type = i.next();
151
152                if (!"classname".equalsIgnoreCase(type))
153                {
154                    registerFactories(type);
155                }
156            }
157        }
158        catch (TurbineException e)
159        {
160            throw new InitializationException(
161                    "AssemblerBrokerService failed to initialize", e);
162        }
163
164        isCaching = Turbine.getConfiguration()
165            .getBoolean(TurbineConstants.MODULE_CACHE_KEY,
166                        TurbineConstants.MODULE_CACHE_DEFAULT);
167
168        if (isCaching)
169        {
170            int cacheSize = Turbine.getConfiguration()
171                .getInt(TurbineConstants.MODULE_CACHE_SIZE_KEY,
172                        TurbineConstants.MODULE_CACHE_SIZE_DEFAULT);
173
174            assemblerCache = new ConcurrentHashMap<>(cacheSize);
175            loaderCache = new ConcurrentHashMap<>(cacheSize);
176        }
177
178        setInit(true);
179    }
180
181    /**
182     * Register a new AssemblerFactory
183     *
184     * @param factory factory to register
185     *
186     * @param <T> the type of the assembler
187     *
188     */
189    @Override
190    public <T extends Assembler> void registerFactory(AssemblerFactory<T> factory)
191    {
192        getFactoryGroup(factory.getManagedClass()).add(factory);
193    }
194
195    /**
196     * Attempt to retrieve an Assembler of a given type with
197     * a name.  Cycle through all the registered AssemblerFactory
198     * classes of type and return the first non-null assembly
199     * found.  If an assembly was not found return null.
200     *
201     * @param type type of Assembler
202     * @param name name of the requested Assembler
203     *
204     * @param <T> the type of the assembler
205     *
206     * @return an Assembler or null
207     * @throws TurbineException if the assembler could not be loaded
208     */
209    @Override
210    @SuppressWarnings("unchecked")
211    public <T extends Assembler> T getAssembler(Class<T> type, String name)
212        throws TurbineException
213    {
214        String key = type + ":" + name;
215        T assembler = null;
216
217        if (isCaching && assemblerCache.containsKey(key))
218        {
219            assembler = (T) assemblerCache.get(key);
220            log.debug("Found {} in the cache!", key);
221        }
222        else
223        {
224            log.debug("Loading {}", key);
225            List<AssemblerFactory<T>> facs = getFactoryGroup(type);
226
227            for (Iterator<AssemblerFactory<T>> it = facs.iterator(); (assembler == null) && it.hasNext();)
228            {
229                AssemblerFactory<T> fac = it.next();
230
231                try
232                {
233                    assembler = fac.getAssembler(name);
234                }
235                catch (Exception e)
236                {
237                    throw new TurbineException("Failed to load an assembler for "
238                                               + name + " from the "
239                                               + type + " factory "
240                                               + fac.getClass().getName(), e);
241                }
242
243                if (assembler != null)
244                {
245                    AnnotationProcessor.process(assembler);
246
247                    if (isCaching)
248                    {
249                        T oldAssembler = (T) assemblerCache.putIfAbsent(key, assembler);
250                        if (oldAssembler != null)
251                        {
252                            assembler = oldAssembler;
253                        }
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 {} loader in the cache!", type);
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 {} is null.", type);
302        }
303
304        return loader;
305    }
306}