001    /*
002     * Copyright 2000-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.scaffold.http;
018    
019    import java.io.BufferedInputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.util.Map;
023    import java.util.Properties;
024    
025    import javax.servlet.ServletException;
026    import javax.servlet.UnavailableException;
027    import javax.servlet.http.HttpServlet;
028    
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    
032    /**
033     * Base servlet for loading application resources.
034     * 
035     * @author Ted Husted
036     * @author Steve Raeburn
037     */
038    public class ResourceServlet extends HttpServlet {
039    
040        // TODO: Externalize messages using commons resources. 
041        //       Previous suggestion was to use MessageResources, but this would 
042        //       create a dependency on Struts. A better option would be to use 
043        //       Commons Resources, which Struts will eventually migrate to.
044        
045        // ------------------------------------------------------- Class variables
046    
047        /**
048         * Commons logging instance
049         */
050        private static Log log = LogFactory.getLog(ResourceServlet.class);
051    
052        // ------------------------------------------------------ Logging Messages
053    
054        private static final String INIT_FAILED_EVENT =
055            "ResourceServlet: init() failed.";
056    
057        private static final String RELOAD_EVENT = 
058            "Reloading ResourceServlet";
059    
060        private static final String DESTROY_EVENT = 
061            "Destroying ResourceServlet";
062    
063        private static final String RESOURCE_LOADING =
064            "Loading resources from path: ";
065    
066        private static final String RESOURCE_NOT_FOUND = 
067            "Resources not found";
068    
069        // --------------------------------------------------- Parameter Utilities
070    
071        /**
072         * Check for a parameter and returns a default value if not found.
073         *
074         * @param parameter The attribute name to look for
075         * @param defaultValue The default to return if the parameter is not found
076         * @return The customized value or the default value
077         */
078        public String getInitString(String parameter, String defaultValue) {
079    
080            String stringValue = getServletConfig().getInitParameter(parameter);
081            if (null == stringValue)
082                return defaultValue;
083            return stringValue;
084    
085        }
086    
087        /**
088         * Check for a parameter and returns a default value if not found,
089         * or if the value does not convert to an <code>int</code>.
090         *
091         * @param parameter The attribute name to look for
092         * @defaultValue The default to return if the parameter is not found
093         * @return The customized value or the default value
094         */
095        public int getInitInt(String parameter, int defaultValue) {
096    
097            String stringValue = null;
098            int intValue = defaultValue;
099    
100            try {
101    
102                stringValue = getServletConfig().getInitParameter(parameter);
103                intValue = Integer.parseInt(stringValue);
104    
105            } catch (Throwable t) {
106                intValue = defaultValue;
107            }
108    
109            return intValue;
110        }
111    
112        /**
113         * Return a map of this servlet's initialization parameters.
114         *
115         * @return A map of this servlet's initialization parameters.
116         * @fixme Not tested yet
117         */
118        public Map getInitParameters() {
119    
120            // :FIXME: Not tested yet.
121    
122            java.util.Enumeration names =
123                getServletConfig().getInitParameterNames();
124            java.util.HashMap map = new java.util.HashMap();
125            while (names.hasMoreElements()) {
126                String name = (String) names.nextElement();
127                String value = getServletConfig().getInitParameter(name);
128                map.put(name, value);
129            }
130            return map;
131        }
132    
133        // --------------------------------------------------- Internal Properties
134    
135        /**
136         * The parameter to check for a new path to the default properties
137         * ["default"].
138         */
139        private static final String DEFAULT_PARAMETER = "default";
140    
141        /**
142         * The default path to use if parameter is not set
143         *["resources/default.properties"].
144         */
145        private static final String DEFAULT_PATH = "resources/default.properties";
146    
147        /**
148         * The default attribute for the application properties.
149         * The properties are exposed in the servlet context
150         * under this attribute name
151         * [lang.Tokens.PROPERTIES_KEY].
152         */
153        private static final String DEFAULT_ATTRIBUTE =
154            org.apache.commons.scaffold.lang.Tokens.PROPERTIES_KEY;
155    
156        /**
157         * Our default properties object.
158         * <p>
159         * The default properties can be used to store applications
160         * settings that do not need to be localized and may
161         * not even be displayed to the user.
162         */
163        Properties properties = null;
164    
165        /**
166         * Set the default properties object.
167         */
168        public void setProperties(Properties properties) {
169            this.properties = properties;
170        }
171    
172        /**
173         * Return the default properties object.
174         *
175         * @return The default properties object
176         */
177        public Properties getProperties() {
178            return this.properties;
179        }
180    
181        /**
182         * A utility method for loading a Properties file
183         * specified by an initialization parameter.
184         *
185         * The initialization parameter should specify the
186         * package and folder for the Properties in system
187         * path format (resources/custom.properties).
188         *
189         * @param parameter The name of the initialization
190         * parameter
191         * @param defaultPath The path to use if the
192         * parameter is not found
193         * @param attribute If not null, store in
194         * application scope under this attribute name
195         */
196        public Properties loadProperties(
197            String parameter,
198            String defaultPath,
199            String attribute)
200            throws ServletException {
201    
202            String path = getInitString(parameter, defaultPath);
203    
204            if (log.isDebugEnabled()) {
205                log.debug(RESOURCE_LOADING);
206                log.debug(path);
207            }
208    
209            InputStream is = null;
210            is = this.getClass().getClassLoader().getResourceAsStream(path);
211            if (null == is)
212                throw new UnavailableException(RESOURCE_NOT_FOUND);
213    
214            Properties p = null;
215            BufferedInputStream bis = new BufferedInputStream(is);
216    
217            try {
218    
219                p = new Properties();
220                p.load(bis);
221                bis.close();
222                is.close();
223            } catch (IOException e) {
224    
225                p = null;
226    
227            } finally {
228                is = null;
229                bis = null;
230            }
231    
232            if ((null != p) && (null != attribute)) {
233    
234                this.getServletContext().setAttribute(attribute, p);
235    
236            }
237    
238            if (log.isDebugEnabled()) {
239                log.debug(p.toString());
240            }
241    
242            return p;
243        }
244    
245        /**
246         * Initialize the default properties for this application.
247         * <p>
248         * Use the <code>default</code> initialization parameter to specify
249         * another path. Otherwise ["resources/default.properties"] is used.
250         * <p>
251         * If the default properties will not be used or specified,
252         * override this method with one that does not try to load the
253         * default properties.
254         *
255         * @exception IOException if an input/output error is encountered
256         * @exception ServletException if we cannot initialize these resources
257         * @todo The PROPERTIES_KEY could be made configurable,
258         * but the BaseAction would need to be notified.
259         */
260        protected void initDefault() throws IOException, ServletException {
261    
262            setProperties(
263                loadProperties(DEFAULT_PARAMETER, DEFAULT_PATH, DEFAULT_ATTRIBUTE));
264        }
265    
266        /**
267         * Release any default resources created at initialization
268         */
269        protected void destroyDefault() {
270            setProperties(null);
271        }
272    
273        // --------------------------------------------------- HttpServlet Methods
274    
275        /**
276         * Initialize this servlet by caling three extension points:
277         * <ul>
278         * <li><code>initLogging</code></li>
279         * <li><code>initDefault</code></li>
280         * <li><code>initCustom</code></li>
281         * </ul>
282         * The main extension point is <code><b>initCustom</b></code>
283         * The default implementation does nothing.
284         * The other two methods have reasonable default behaviors that most
285         * subclasses could use as-is.
286         * This may be called again from <code>reload</code> and should be
287         * "re-enterant".
288         *
289         * @exception ServletException if we cannot configure ourselves
290         * correctly
291         */
292        public void init() throws ServletException {
293    
294            try {
295    
296                initDefault();
297                // initMessages();
298                initCustom();
299    
300            } catch (IOException e) {
301    
302                throw new UnavailableException(INIT_FAILED_EVENT);
303    
304            }
305        }
306    
307        /**
308         * Gracefully shut down this controller servlet, releasing any resources
309         * that were allocated at initialization.
310         */
311        public void destroy() {
312    
313            if (log.isDebugEnabled()) {
314                log.debug(DESTROY_EVENT);
315            }
316    
317            destroyCustom();
318            // destroyMessages();
319            destroyDefault();
320    
321        }
322    
323        // ------------------------------------------------------ Extension Points
324    
325        /**
326         * Initialize the custom properties or objects for this application.
327         *
328         * @exception IOException if an input/output error is encountered
329         * @exception ServletException if we cannot initialize these resources
330         */
331        protected void initCustom() throws IOException, ServletException {
332    
333            // Override with custom initializations
334    
335        }
336    
337        /**
338         * Release any custom resources created at initialization
339         */
340        protected void destroyCustom() {
341            // override to provide functionality if needed
342        }
343    
344        // -------------------------------------------------------- Public Methods
345    
346        /**
347         * Reload the configuration of this controller servlet from our
348         * underlying configuration files.
349         *
350         * @exception IOException if an input/output error occurs
351         * @exception ServletException if a servlet exception occurs
352         */
353        public void reload() throws IOException, ServletException {
354    
355            if (log.isDebugEnabled()) {
356                log.debug(RELOAD_EVENT);
357            }
358    
359            // Re-initialize
360            init();
361        }
362    
363    } // end ResourceServlet