001package org.apache.turbine.services.assemblerbroker.util.java;
002
003
004import java.lang.reflect.InvocationTargetException;
005
006/*
007 * Licensed to the Apache Software Foundation (ASF) under one
008 * or more contributor license agreements.  See the NOTICE file
009 * distributed with this work for additional information
010 * regarding copyright ownership.  The ASF licenses this file
011 * to you under the Apache License, Version 2.0 (the
012 * "License"); you may not use this file except in compliance
013 * with the License.  You may obtain a copy of the License at
014 *
015 *   http://www.apache.org/licenses/LICENSE-2.0
016 *
017 * Unless required by applicable law or agreed to in writing,
018 * software distributed under the License is distributed on an
019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
020 * KIND, either express or implied.  See the License for the
021 * specific language governing permissions and limitations
022 * under the License.
023 */
024
025import java.util.List;
026import java.util.concurrent.ConcurrentHashMap;
027
028import org.apache.commons.lang3.StringUtils;
029import org.apache.logging.log4j.LogManager;
030import org.apache.logging.log4j.Logger;
031import org.apache.turbine.modules.Assembler;
032import org.apache.turbine.modules.GenericLoader;
033import org.apache.turbine.modules.Loader;
034import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
035
036/**
037 * A screen factory that attempts to load a java class from
038 * the module packages defined in the TurbineResource.properties.
039 *
040 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
041 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
042 * @param <T> the specialized assembler type
043 */
044public abstract class JavaBaseFactory<T extends Assembler>
045    implements AssemblerFactory<T>
046{
047    /** A vector of packages. */
048    private static List<String> packages = GenericLoader.getPackages();
049
050    /** Logging */
051    protected Logger log = LogManager.getLogger(this.getClass());
052
053    /**
054     * A cache for previously obtained Class instances, which we keep in order
055     * to reduce the Class.forName() overhead (which can be sizable).
056     */
057    private final ConcurrentHashMap<String, Class<T>> classCache = new ConcurrentHashMap<>();
058
059    /**
060     * Get an Assembler.
061     *
062     * @param packageName java package name
063     * @param name name of the requested Assembler
064     * @return an Assembler
065     */
066    @SuppressWarnings("unchecked")
067    public T getAssembler(String packageName, String name)
068    {
069        T assembler = null;
070
071        log.debug("Class Fragment is {}", name);
072
073        if (StringUtils.isNotEmpty(name))
074        {
075            for (String p : packages)
076            {
077                StringBuilder sb = new StringBuilder();
078
079                sb.append(p).append('.').append(packageName).append('.').append(name);
080                String className = sb.toString();
081
082                log.debug("Trying {}", className);
083
084                try
085                {
086                    Class<T> servClass = classCache.get(className);
087                    if (servClass == null)
088                    {
089                        servClass = (Class<T>) Class.forName(className);
090                        Class<T> _servClass = classCache.putIfAbsent(className, servClass);
091                        if (_servClass != null)
092                        {
093                            servClass = _servClass;
094                        }
095                    }
096                    assembler = servClass.getDeclaredConstructor().newInstance();
097                    break; // for()
098                }
099                catch (ClassNotFoundException cnfe)
100                {
101                    // Do this so we loop through all the packages.
102                    log.debug("{}: Not found", className);
103                }
104                catch (NoClassDefFoundError ncdfe)
105                {
106                    // Do this so we loop through all the packages.
107                    log.debug("{}: No Class Definition found", className);
108                }
109                // With ClassCastException, InstantiationException we hit big problems
110                catch (ClassCastException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e)
111                {
112                    // This means trouble!
113                    // Alternatively we can throw this exception so
114                    // that it will appear on the client browser
115                    log.error("Could not load {}", className, e);
116                    break; // for()
117                }
118            }
119        }
120
121        log.debug("Returning: {}", assembler);
122
123        return assembler;
124    }
125
126    /**
127     * Get the loader for this type of assembler
128     *
129     * @return a Loader
130     */
131    @Override
132    public abstract Loader<T> getLoader();
133
134    /**
135     * Get the size of a possibly configured cache
136     *
137     * @return the size of the cache in bytes
138     */
139    @Override
140    public int getCacheSize()
141    {
142        return getLoader().getCacheSize();
143    }
144}