View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.resources.impl;
19  
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URL;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.Properties;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  /**
32   * <p>Concrete implementation of
33   * {@link org.apache.commons.resources.Resources} that wraps a family
34   * (one per <code>Locale</code>) of properties files that share a base URL
35   * and have name suffixes reflecting the <code>Locale</code> for which
36   * the document's messages apply.  Resources are looked up in a hierarchy
37   * of properties files in a manner identical to that performed by
38   * <code>java.util.ResourceBundle.getBundle().</code>.</p>
39   *
40   * <p>The base URL passed to our constructor must contain the base name
41   * of the properties file family.
42   * For example, if the configuration URL is passed as
43   * <code>http://localhost/foo/Bar</code>, the resources for the
44   * <code>en_US</code> Locale would be stored under URL
45   * <code>http://localhost/foo/Bar_en_US.properties</code>, and the default
46   * resources would be stored in
47   * <code>http://localhost/foo/Bar.properties</code>.</p>
48   */
49  public class PropertyResources extends CollectionResourcesBase {
50  
51      /**
52       * <p>The <code>Log</code> instance for this class.</p>
53       */
54      private transient Log log = LogFactory.getLog(PropertyResources.class);
55  
56      // ----------------------------------------------------------- Constructors
57  
58      /**
59       * <p>Create a new
60       * {@link org.apache.commons.resources.Resources} instance with the specified
61       * logical name and base resource URL.</p>
62       *
63       * @param name Logical name of the new instance
64       * @param base Base URL of the family of properties files that contain
65       *  the resource keys and values
66       */
67      public PropertyResources(String name, String base) {
68          super(name, base);
69      }
70  
71  
72      // ------------------------------------------------------ Protected Methods
73  
74  
75      /**
76       * <p>Return a <code>Map</code> containing the name-value mappings for
77       * the specified base URL and requested <code>Locale</code>, if there
78       * are any.  If there are no defined mappings for the specified
79       * <code>Locale</code>, return an empty <code>Map</code> instead.</p>
80       *
81       * <p>Concrete subclasses must override this method to perform the
82       * appropriate lookup.  A typical implementation will construct an
83       * absolute URL based on the specified base URL and <code>Locale</code>,
84       * retrieve the specified resource file (if any), and parse it into
85       * a <code>Map</code> structure.</p>
86       *
87       * <p>Caching of previously retrieved <code>Map</code>s (if any) should
88       * be performed by callers of this method.  Therefore, this method should
89       * always attempt to retrieve the specified resource and load it
90       * appropriately.</p>
91       *
92       * @param baseUrl Base URL of the resource files for this
93       * {@link org.apache.commons.resources.Resources} instance
94       * @param locale <code>Locale</code> for which name-value mappings
95       *  are requested
96       * @return A name-value Map for the specified URL and locale.
97       */
98      protected Map getLocaleMap(String baseUrl, Locale locale) {
99  
100         if (getLog().isDebugEnabled()) {
101             getLog().debug("Loading locale '" + locale + "' resources from base '" +
102                     baseUrl + "'");
103         }
104 
105         Properties props = new Properties();
106         String name = baseUrl + getLocaleSuffix(locale) + ".properties";
107         InputStream stream = null;
108 
109         try {
110 
111             // Open an input stream to the URL for this locale (if any)
112             if (getLog().isTraceEnabled()) {
113                 getLog().trace("Absolute URL is '" + name + "'");
114             }
115             URL url = new URL(name);
116             stream = url.openStream();
117 
118             // Parse the input stream and populate the name-value mappings map
119             if (getLog().isTraceEnabled()) {
120                 getLog().trace("Parsing input resource");
121             }
122             props.load(stream);
123 
124         } catch (FileNotFoundException e) {
125 
126             // Log and swallow this exception
127             if (getLog().isDebugEnabled()) {
128                 getLog().debug("No resources for locale '" + locale +
129                           "' from base '" + baseUrl + "'");
130             }
131             props.clear();
132 
133         } catch (IOException e) {
134 
135             getLog().warn("IOException loading locale '" + locale +
136                      "' from base '" + baseUrl + "'", e);
137             props.clear();
138 
139         } finally {
140 
141             // Close the input stream that was opened earlier
142             if (stream != null) {
143                 try {
144                     stream.close();
145                 } catch (IOException e) {
146                     getLog().error("Error closing stream.", e);
147                 }
148                 stream = null;
149             }
150 
151         }
152 
153         // Return the populated (or empty) properties
154         return (props);
155 
156     }
157 
158 
159     /**
160      * Accessor method for Log instance.
161      *
162      * The Log instance variable is transient and
163      * accessing it through this method ensures it
164      * is re-initialized when this instance is
165      * de-serialized.
166      *
167      * @return The Log instance.
168      */
169     private Log getLog() {
170         if (log == null) {
171             log =  LogFactory.getLog(PropertyResources.class);
172         }
173         return log;
174     }
175 
176 }