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;
024
025import org.apache.activemq.transport.LogWriter;
026import org.apache.activemq.transport.TransportLoggerView;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030/**
031 * Class used to find a LogWriter implementation, and returning
032 * a LogWriter object, taking as argument the name of a log writer.
033 * The mapping between the log writer names and the classes
034 * implementing LogWriter is specified by the files in the
035 * resources/META-INF/services/org/apache/activemq/transport/logwriters
036 * directory.
037 * 
038 * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com
039 * 
040 */
041public class LogWriterFinder {
042    
043    private static final Logger log = LoggerFactory.getLogger(TransportLoggerView.class);
044
045    private final String path;
046    private final ConcurrentHashMap classMap = new ConcurrentHashMap();
047
048    /**
049     * Builds a LogWriterFinder that will look for the mappings between
050     * LogWriter names and classes in the directory "path".
051     * @param path The directory where the files that map log writer names to
052     * LogWriter classes are. 
053     */
054    public LogWriterFinder(String path) {
055        this.path = path;
056    }
057
058    /**
059     * Returns a LogWriter object, given a log writer name (for example "default", or "custom").
060     * Uses a ConcurrentHashMap to cache the Class objects that have already been loaded.
061     * @param logWriterName a log writer name (for example "default", or "custom").
062     * @return a LogWriter object to be used by the TransportLogger class.
063     * @throws IllegalAccessException
064     * @throws InstantiationException
065     * @throws IOException
066     * @throws ClassNotFoundException
067     */
068    public LogWriter newInstance(String logWriterName)
069    throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException
070    {
071        Class clazz = (Class) classMap.get(logWriterName);
072        if (clazz == null) {
073            clazz = newInstance(doFindLogWriterProperties(logWriterName));
074            classMap.put(logWriterName, clazz);
075        }
076        return (LogWriter)clazz.newInstance();
077    }
078    
079    /**
080     * Loads and returns a class given a Properties object with a "class" property.
081     * @param properties a Properties object with a "class" property.
082     * @return a Class object.
083     * @throws ClassNotFoundException
084     * @throws IOException
085     */
086    private Class newInstance(Properties properties) throws ClassNotFoundException, IOException {
087
088        String className = properties.getProperty("class");
089        if (className == null) {
090            throw new IOException("Expected property is missing: " + "class");
091        }
092        Class clazz;
093        try {
094            clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
095        } catch (ClassNotFoundException e) {
096            clazz = LogWriterFinder.class.getClassLoader().loadClass(className);
097        }
098
099        return clazz;
100    }
101
102    /**
103     * Given a log writer name, returns a Properties object with a "class" property
104     * whose value is a String with the name of the class to be loaded.
105     * @param logWriterName a log writer name.
106     * @return a Properties object with a "class" property
107     * @throws IOException
108     */
109    protected Properties doFindLogWriterProperties (String logWriterName) throws IOException {
110
111        String uri = path + logWriterName;
112
113        // lets try the thread context class loader first
114        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
115        if (classLoader == null) classLoader = getClass().getClassLoader();
116        InputStream in = classLoader.getResourceAsStream(uri);
117        if (in == null) {
118            in = LogWriterFinder.class.getClassLoader().getResourceAsStream(uri);
119            if (in == null) {
120                log.error("Could not find log writer for resource: " + uri);
121                throw new IOException("Could not find log writer for resource: " + uri);
122            }
123        }
124
125        // lets load the file
126        BufferedInputStream reader = null;
127        Properties properties = new Properties();
128        try {
129            reader = new BufferedInputStream(in);
130            properties.load(reader);
131            return properties;
132        } finally {
133            try {
134                reader.close();
135            } catch (Exception e) {
136            }
137        }
138    }
139
140
141}