001    /*
002     * Copyright 1999-2002,2004 The Apache Software Foundation.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.apache.commons.latka;
018    
019    import java.io.InputStream;
020    import java.io.IOException;
021    import java.util.Properties;
022    
023    import org.apache.log4j.Category;
024    
025    /**
026     * Stores properties for Latka.  This class is backed by a 
027     * ThreadLocal variable, so every Thread is guaranteed to
028     * get a unique Properties object.  Note, however, that
029     * inside an environment utilizing a Thread pool, such
030     * as many Servlet engines, it is possible for 
031     * the Properties object to travel quite widely.  Use the
032     * {@link #resetProperties()} method to reset the Properties
033     * object for a Thread to its default values.
034     * 
035     * @author Morgan Delagrange
036     * @author dIon Gillard (javadoc changes)
037     */
038    public class LatkaProperties {
039    
040        /** log4j category to append output to */
041        protected static final Category _log =
042            Category.getInstance(LatkaProperties.class);
043    
044        /** default Properties file for Latka */
045        protected static Properties _initProps = loadDefaultProps();
046    
047        static {
048            _initProps.putAll(loadUserProps());
049        }
050    
051        /**
052         * This ThreadLocal is automatically instantiated per thread
053         * with a Properties object containing the default properties.
054         */
055        protected static ThreadLocal _propsThreadLocal =
056            new LatkaThreadLocal(_initProps);
057    
058        /**
059         * Returns the unique Properties object for the current
060         * Thread.  The Properties object is initialized with
061         * the default Latka Properties.
062         * 
063         * @return Latka Properties object 
064         */
065        public static Properties getProperties() {
066            return (Properties) _propsThreadLocal.get();
067        }
068    
069        /**
070         * Resets the Latka properties to their initial value
071         * (getProperties() will still return the same Object).
072         * One use for this method is to reset state inside
073         * a Thread-pooling environment.
074         */
075        public static void resetProperties() {
076            Properties props = (Properties) _propsThreadLocal.get();
077            props.clear();
078            props.putAll(_initProps);
079        }
080    
081        /**
082         * Loads the default Properties from the 
083         * first "latka.properties" file located encountered 
084         * in the classpath.
085         * 
086         * @return A Properties object generated from the contents of the
087         *         default property file
088         */
089        protected static Properties loadDefaultProps() {
090    
091            Properties properties  = new Properties();
092        
093            try {
094                properties.putAll(
095                           loadPropsFromClasspath("latka.properties.internal"));
096            } catch (IOException e) {
097                _log.error(
098                    "Couldn't find latka.properties.internal file in the classpath",
099                    e);
100            }
101    
102            return properties;
103    
104        }
105    
106        /**
107         * Load <em>latka.properties</em> from classpath
108         * @return loaded properties
109         */
110        protected static Properties loadUserProps() {
111    
112            Properties properties  = new Properties();
113    
114            try {
115                properties.putAll(loadPropsFromClasspath("latka.properties"));
116            } catch (IOException e) {
117                _log.debug(e);
118                _log.warn(
119                 "No user-defined latka.properties file in the classpath (optional)"
120                         );
121            }
122    
123            return properties;
124       
125        }
126    
127        /**
128         * Load properties specified from context class path
129         * @param classpathLocation Resource name to load
130         * @throws IOException from loading resource
131         * @return initialized properties object
132         */
133        protected static Properties
134                         loadPropsFromClasspath(String classpathLocation)
135                         throws IOException {
136            Properties properties  = new Properties();
137        
138            ClassLoader loader = Thread.currentThread().getContextClassLoader();
139            if (loader == null) {
140                    // there may not be a context class loader
141                    loader = LatkaProperties.class.getClassLoader();
142            }
143    
144            InputStream stream = loader.getResourceAsStream(classpathLocation);
145    
146            if (stream == null) {
147                throw new IOException("Could not find this file in classpath: "
148                    + classpathLocation);
149            }
150    
151            properties.load(stream);
152    
153            return properties;
154        }
155    
156        /**
157         * Custom ThreadLocal class that automatically initialized
158         * the default Properties for the Thread.
159         * 
160         * @author Morgan Delagrange
161         */
162        private static class LatkaThreadLocal extends ThreadLocal {
163            /** default properties for the thread */
164            protected Properties _initProps = null;
165    
166            /**
167             * Constructor specifying the default Properties object
168             * for Latka
169             * 
170             * @param initProps default Properties object
171             */
172            public LatkaThreadLocal(Properties initProps) {
173                _initProps = initProps;
174            }
175    
176            /**
177             * Returns a clone of the default Properties file
178             * for Latka
179             * 
180             * @return Latka Properties file for the current Thread
181             */
182            protected Object initialValue() {
183                return _initProps.clone();
184            }
185    
186        }
187    
188    }