001    /*
002     * $Id: PropertyResources.java 348371 2005-11-23 04:56:51Z niallp $
003     * $Revision: 348371 $
004     * $Date: 2005-11-23 04:56:51 +0000 (Wed, 23 Nov 2005) $
005     *
006     * ====================================================================
007     *
008     *  Copyright 2003-2005 The Apache Software Foundation
009     *
010     *  Licensed under the Apache License, Version 2.0 (the "License");
011     *  you may not use this file except in compliance with the License.
012     *  You may obtain a copy of the License at
013     *
014     *      http://www.apache.org/licenses/LICENSE-2.0
015     *
016     *  Unless required by applicable law or agreed to in writing, software
017     *  distributed under the License is distributed on an "AS IS" BASIS,
018     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
019     *  See the License for the specific language governing permissions and
020     *  limitations under the License.
021     *
022     */
023    
024    package org.apache.commons.resources.impl;
025    
026    import java.io.FileNotFoundException;
027    import java.io.IOException;
028    import java.io.InputStream;
029    import java.net.URL;
030    import java.util.Locale;
031    import java.util.Map;
032    import java.util.Properties;
033    
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    
037    /**
038     * <p>Concrete implementation of 
039     * {@link org.apache.commons.resources.Resources} that wraps a family
040     * (one per <code>Locale</code>) of properties files that share a base URL
041     * and have name suffixes reflecting the <code>Locale</code> for which
042     * the document's messages apply.  Resources are looked up in a hierarchy
043     * of properties files in a manner identical to that performed by
044     * <code>java.util.ResourceBundle.getBundle().</code>.</p>
045     *
046     * <p>The base URL passed to our constructor must contain the base name
047     * of the properties file family.
048     * For example, if the configuration URL is passed as
049     * <code>http://localhost/foo/Bar</code>, the resources for the
050     * <code>en_US</code> Locale would be stored under URL
051     * <code>http://localhost/foo/Bar_en_US.properties</code>, and the default
052     * resources would be stored in
053     * <code>http://localhost/foo/Bar.properties</code>.</p>
054     */
055    public class PropertyResources extends CollectionResourcesBase {
056    
057        /**
058         * <p>The <code>Log</code> instance for this class.</p>
059         */
060        private transient Log log = LogFactory.getLog(PropertyResources.class);
061    
062        // ----------------------------------------------------------- Constructors
063    
064        /**
065         * <p>Create a new 
066         * {@link org.apache.commons.resources.Resources} instance with the specified
067         * logical name and base resource URL.</p>
068         *
069         * @param name Logical name of the new instance
070         * @param base Base URL of the family of properties files that contain
071         *  the resource keys and values
072         */
073        public PropertyResources(String name, String base) {
074            super(name, base);
075        }
076    
077    
078        // ------------------------------------------------------ Protected Methods
079    
080    
081        /**
082         * <p>Return a <code>Map</code> containing the name-value mappings for
083         * the specified base URL and requested <code>Locale</code>, if there
084         * are any.  If there are no defined mappings for the specified
085         * <code>Locale</code>, return an empty <code>Map</code> instead.</p>
086         *
087         * <p>Concrete subclasses must override this method to perform the
088         * appropriate lookup.  A typical implementation will construct an
089         * absolute URL based on the specified base URL and <code>Locale</code>,
090         * retrieve the specified resource file (if any), and parse it into
091         * a <code>Map</code> structure.</p>
092         *
093         * <p>Caching of previously retrieved <code>Map</code>s (if any) should
094         * be performed by callers of this method.  Therefore, this method should
095         * always attempt to retrieve the specified resource and load it
096         * appropriately.</p>
097         *
098         * @param baseUrl Base URL of the resource files for this 
099         * {@link org.apache.commons.resources.Resources} instance
100         * @param locale <code>Locale</code> for which name-value mappings
101         *  are requested
102         * @return A name-value Map for the specified URL and locale.
103         */
104        protected Map getLocaleMap(String baseUrl, Locale locale) {
105    
106            if (getLog().isDebugEnabled()) {
107                getLog().debug("Loading locale '" + locale + "' resources from base '" +
108                        baseUrl + "'");
109            }
110    
111            Properties props = new Properties();
112            String name = baseUrl + getLocaleSuffix(locale) + ".properties";
113            InputStream stream = null;
114    
115            try {
116    
117                // Open an input stream to the URL for this locale (if any)
118                if (getLog().isTraceEnabled()) {
119                    getLog().trace("Absolute URL is '" + name + "'");
120                }
121                URL url = new URL(name);
122                stream = url.openStream();
123    
124                // Parse the input stream and populate the name-value mappings map
125                if (getLog().isTraceEnabled()) {
126                    getLog().trace("Parsing input resource");
127                }
128                props.load(stream);
129    
130            } catch (FileNotFoundException e) {
131    
132                // Log and swallow this exception
133                if (getLog().isDebugEnabled()) {
134                    getLog().debug("No resources for locale '" + locale +
135                              "' from base '" + baseUrl + "'");
136                }
137                props.clear();
138    
139            } catch (IOException e) {
140    
141                getLog().warn("IOException loading locale '" + locale +
142                         "' from base '" + baseUrl + "'", e);
143                props.clear();
144    
145            } finally {
146    
147                // Close the input stream that was opened earlier
148                if (stream != null) {
149                    try {
150                        stream.close();
151                    } catch (IOException e) {
152                        getLog().error("Error closing stream.", e);
153                    }
154                    stream = null;
155                }
156    
157            }
158    
159            // Return the populated (or empty) properties
160            return (props);
161    
162        }
163    
164    
165        /**
166         * Accessor method for Log instance.
167         *
168         * The Log instance variable is transient and
169         * accessing it through this method ensures it
170         * is re-initialized when this instance is
171         * de-serialized.
172         *
173         * @return The Log instance.
174         */
175        private Log getLog() {
176            if (log == null) {
177                log =  LogFactory.getLog(PropertyResources.class);
178            }
179            return log;
180        }
181    
182    }