001package org.apache.turbine.services.assemblerbroker.util.java;
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
023import java.util.List;
024import java.util.concurrent.ConcurrentHashMap;
025
026import org.apache.commons.lang3.StringUtils;
027import org.apache.logging.log4j.LogManager;
028import org.apache.logging.log4j.Logger;
029import org.apache.turbine.modules.Assembler;
030import org.apache.turbine.modules.GenericLoader;
031import org.apache.turbine.modules.Loader;
032import org.apache.turbine.services.assemblerbroker.util.AssemblerFactory;
033
034/**
035 * A screen factory that attempts to load a java class from
036 * the module packages defined in the TurbineResource.properties.
037 *
038 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
039 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
040 * @param <T> the specialized assembler type
041 */
042public abstract class JavaBaseFactory<T extends Assembler>
043    implements AssemblerFactory<T>
044{
045    /** A vector of packages. */
046    private static List<String> packages = GenericLoader.getPackages();
047
048    /** Logging */
049    protected Logger log = LogManager.getLogger(this.getClass());
050
051    /**
052     * A cache for previously obtained Class instances, which we keep in order
053     * to reduce the Class.forName() overhead (which can be sizable).
054     */
055    private final ConcurrentHashMap<String, Class<T>> classCache = new ConcurrentHashMap<>();
056
057    /**
058     * Get an Assembler.
059     *
060     * @param packageName java package name
061     * @param name name of the requested Assembler
062     * @return an Assembler
063     */
064    @SuppressWarnings("unchecked")
065    public T getAssembler(String packageName, String name)
066    {
067        T assembler = null;
068
069        log.debug("Class Fragment is {}", name);
070
071        if (StringUtils.isNotEmpty(name))
072        {
073            for (String p : packages)
074            {
075                StringBuilder sb = new StringBuilder();
076
077                sb.append(p).append('.').append(packageName).append('.').append(name);
078                String className = sb.toString();
079
080                log.debug("Trying {}", className);
081
082                try
083                {
084                    Class<T> servClass = classCache.get(className);
085                    if (servClass == null)
086                    {
087                        servClass = (Class<T>) Class.forName(className);
088                        Class<T> _servClass = classCache.putIfAbsent(className, servClass);
089                        if (_servClass != null)
090                        {
091                            servClass = _servClass;
092                        }
093                    }
094                    assembler = servClass.newInstance();
095                    break; // for()
096                }
097                catch (ClassNotFoundException cnfe)
098                {
099                    // Do this so we loop through all the packages.
100                    log.debug("{}: Not found", className);
101                }
102                catch (NoClassDefFoundError ncdfe)
103                {
104                    // Do this so we loop through all the packages.
105                    log.debug("{}: No Class Definition found", className);
106                }
107                // With ClassCastException, InstantiationException we hit big problems
108                catch (ClassCastException | InstantiationException | IllegalAccessException e)
109                {
110                    // This means trouble!
111                    // Alternatively we can throw this exception so
112                    // that it will appear on the client browser
113                    log.error("Could not load {}", className, e);
114                    break; // for()
115                }
116            }
117        }
118
119        log.debug("Returning: {}", assembler);
120
121        return assembler;
122    }
123
124    /**
125     * Get the loader for this type of assembler
126     *
127     * @return a Loader
128     */
129    @Override
130    public abstract Loader<T> getLoader();
131
132    /**
133     * Get the size of a possibly configured cache
134     *
135     * @return the size of the cache in bytes
136     */
137    @Override
138    public int getCacheSize()
139    {
140        return getLoader().getCacheSize();
141    }
142}