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
025/**
026 * 
027 */
028public class FactoryFinder {
029
030    /**
031     * The strategy that the FactoryFinder uses to find load and instantiate Objects
032     * can be changed out by calling the
033     * {@link org.apache.activemq.util.FactoryFinder#setObjectFactory(org.apache.activemq.util.FactoryFinder.ObjectFactory)}
034     * method with a custom implementation of ObjectFactory.
035     *
036     * The default ObjectFactory is typically changed out when running in a specialized container
037     * environment where service discovery needs to be done via the container system.  For example,
038     * in an OSGi scenario.
039     */
040    public interface ObjectFactory {
041        /**
042         * @param path the full service path 
043         * @return
044         */
045        public Object create(String path) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException;
046
047    }
048
049    /**
050     * The default implementation of Object factory which works well in standalone applications.
051     */
052    protected static class StandaloneObjectFactory implements ObjectFactory {
053        final ConcurrentHashMap<String, Class> classMap = new ConcurrentHashMap<String, Class>();
054
055        public Object create(final String path) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
056            Class clazz = classMap.get(path);
057            if (clazz == null) {
058                clazz = loadClass(loadProperties(path));
059                classMap.put(path, clazz);
060            }
061            return clazz.newInstance();
062        }
063
064        static public Class loadClass(Properties properties) throws ClassNotFoundException, IOException {
065
066            String className = properties.getProperty("class");
067            if (className == null) {
068                throw new IOException("Expected property is missing: class");
069            }
070            Class clazz = null;
071            ClassLoader loader = Thread.currentThread().getContextClassLoader();
072            if (loader != null) {
073                try {
074                    clazz = loader.loadClass(className);
075                } catch (ClassNotFoundException e) {
076                    // ignore
077                }
078            }
079            if (clazz == null) {
080                clazz = FactoryFinder.class.getClassLoader().loadClass(className);
081            }
082
083            return clazz;
084        }
085
086        static public Properties loadProperties(String uri) throws IOException {
087            // lets try the thread context class loader first
088            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
089            if (classLoader == null) {
090                classLoader = StandaloneObjectFactory.class.getClassLoader();
091            }
092            InputStream in = classLoader.getResourceAsStream(uri);
093            if (in == null) {
094                in = FactoryFinder.class.getClassLoader().getResourceAsStream(uri);
095                if (in == null) {
096                    throw new IOException("Could not find factory class for resource: " + uri);
097                }
098            }
099
100            // lets load the file
101            BufferedInputStream reader = null;
102            try {
103                reader = new BufferedInputStream(in);
104                Properties properties = new Properties();
105                properties.load(reader);
106                return properties;
107            } finally {
108                try {
109                    reader.close();
110                } catch (Exception e) {
111                }
112            }
113        }
114    }
115
116    // ================================================================
117    // Class methods and properties
118    // ================================================================
119    private static ObjectFactory objectFactory = new StandaloneObjectFactory();
120
121    public static ObjectFactory getObjectFactory() {
122        return objectFactory;
123    }
124
125    public static void setObjectFactory(ObjectFactory objectFactory) {
126        FactoryFinder.objectFactory = objectFactory;
127    }
128
129    // ================================================================
130    // Instance methods and properties
131    // ================================================================
132    private final String path;
133
134    public FactoryFinder(String path) {
135        this.path = path;
136    }
137
138    /**
139     * Creates a new instance of the given key
140     *
141     * @param key is the key to add to the path to find a text file containing
142     *                the factory name
143     * @return a newly created instance
144     */
145    public Object newInstance(String key) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException {
146        return objectFactory.create(path+key);
147    }
148
149    
150}