001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.util;
018
019import java.io.BufferedInputStream;
020import java.io.IOException;
021import java.io.InputStream;
022import java.util.Properties;
023import java.util.concurrent.ConcurrentHashMap;
024import java.util.concurrent.ConcurrentMap;
025
026import org.apache.activemq.transport.LogWriter;
027import org.apache.activemq.transport.TransportLoggerView;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * Class used to find a LogWriter implementation, and returning
033 * a LogWriter object, taking as argument the name of a log writer.
034 * The mapping between the log writer names and the classes
035 * implementing LogWriter is specified by the files in the
036 * resources/META-INF/services/org/apache/activemq/transport/logwriters
037 * directory.
038 *
039 * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com
040 *
041 */
042public class LogWriterFinder {
043
044    private static final Logger log = LoggerFactory.getLogger(TransportLoggerView.class);
045
046    private final String path;
047    private final ConcurrentMap classMap = new ConcurrentHashMap();
048
049    /**
050     * Builds a LogWriterFinder that will look for the mappings between
051     * LogWriter names and classes in the directory "path".
052     * @param path The directory where the files that map log writer names to
053     * LogWriter classes are.
054     */
055    public LogWriterFinder(String path) {
056        this.path = path;
057    }
058
059    /**
060     * Returns a LogWriter object, given a log writer name (for example "default", or "custom").
061     * Uses a ConcurrentHashMap to cache the Class objects that have already been loaded.
062     * @param logWriterName a log writer name (for example "default", or "custom").
063     * @return a LogWriter object to be used by the TransportLogger class.
064     * @throws IllegalAccessException
065     * @throws InstantiationException
066     * @throws IOException
067     * @throws ClassNotFoundException
068     */
069    public LogWriter newInstance(String logWriterName)
070    throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException
071    {
072        Class clazz = (Class) classMap.get(logWriterName);
073        if (clazz == null) {
074            clazz = newInstance(doFindLogWriterProperties(logWriterName));
075            classMap.put(logWriterName, clazz);
076        }
077        return (LogWriter)clazz.newInstance();
078    }
079
080    /**
081     * Loads and returns a class given a Properties object with a "class" property.
082     * @param properties a Properties object with a "class" property.
083     * @return a Class object.
084     * @throws ClassNotFoundException
085     * @throws IOException
086     */
087    private Class newInstance(Properties properties) throws ClassNotFoundException, IOException {
088
089        String className = properties.getProperty("class");
090        if (className == null) {
091            throw new IOException("Expected property is missing: " + "class");
092        }
093        Class clazz;
094        try {
095            clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
096        } catch (ClassNotFoundException e) {
097            clazz = LogWriterFinder.class.getClassLoader().loadClass(className);
098        }
099
100        return clazz;
101    }
102
103    /**
104     * Given a log writer name, returns a Properties object with a "class" property
105     * whose value is a String with the name of the class to be loaded.
106     * @param logWriterName a log writer name.
107     * @return a Properties object with a "class" property
108     * @throws IOException
109     */
110    protected Properties doFindLogWriterProperties (String logWriterName) throws IOException {
111
112        String uri = path + logWriterName;
113
114        // lets try the thread context class loader first
115        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
116        if (classLoader == null) classLoader = getClass().getClassLoader();
117        InputStream in = classLoader.getResourceAsStream(uri);
118        if (in == null) {
119            in = LogWriterFinder.class.getClassLoader().getResourceAsStream(uri);
120            if (in == null) {
121                log.error("Could not find log writer for resource: " + uri);
122                throw new IOException("Could not find log writer for resource: " + uri);
123            }
124        }
125
126        // lets load the file
127        BufferedInputStream reader = null;
128        Properties properties = new Properties();
129        try {
130            reader = new BufferedInputStream(in);
131            properties.load(reader);
132            return properties;
133        } finally {
134            try {
135                reader.close();
136            } catch (Exception e) {
137            }
138        }
139    }
140
141
142}