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