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     */
017    package org.apache.activemq.util;
018    
019    import java.io.BufferedInputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.util.Properties;
023    import java.util.concurrent.ConcurrentHashMap;
024    
025    /**
026     * 
027     */
028    public 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    }