View Javadoc

1   /*
2    * $Id: WebappXMLResources.java 348371 2005-11-23 04:56:51Z niallp $
3    * $Revision: 348371 $
4    * $Date: 2005-11-23 04:56:51 +0000 (Wed, 23 Nov 2005) $
5    *
6    * ====================================================================
7    *
8    *  Copyright 2003-2005 The Apache Software Foundation
9    *
10   *  Licensed under the Apache License, Version 2.0 (the "License");
11   *  you may not use this file except in compliance with the License.
12   *  You may obtain a copy of the License at
13   *
14   *      http://www.apache.org/licenses/LICENSE-2.0
15   *
16   *  Unless required by applicable law or agreed to in writing, software
17   *  distributed under the License is distributed on an "AS IS" BASIS,
18   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   *  See the License for the specific language governing permissions and
20   *  limitations under the License.
21   *
22   */
23  
24  package org.apache.commons.resources.impl;
25  
26  import java.io.FileNotFoundException;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.util.HashMap;
30  import java.util.Locale;
31  import java.util.Map;
32  
33  import javax.servlet.ServletContext;
34  
35  import org.apache.commons.digester.Digester;
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  import org.xml.sax.SAXException;
39  
40  /**
41   * <p>Concrete implementation of 
42   * {@link org.apache.commons.resources.Resources} that wraps a family
43   * (one per <code>Locale</code> of XML documents that share a base
44   * context-relative path for servlet context resources, and have
45   * name suffixes reflecting the <code>Locale</code> for which
46   * the document's messages apply.  Resources are looked up in a hierarchy
47   * of properties files in a manner identical to that performed by
48   * <code>java.util.ResourceBundle.getBundle().</code>.</p>
49   *
50   * <p>The base resource path passed to our constructor must contain the
51   * context-relative base name of the properties file family.
52   * For example, if the base path is passed as
53   * <code>http://localhost/foo/Bar</code>, the resources for the
54   * <code>en_US</code> Locale would be stored under URL
55   * <code>http://localhost/foo/Bar_en_US.xml</code>, and the default
56   * resources would be stored in
57   * <code>http://localhost/foo/Bar.xml</code>.</p>
58   */
59  public class WebappXMLResources extends CollectionResourcesBase {
60  
61      /**
62       * <p>The <code>Log</code> instance for this class.</p>
63       */
64      private transient Log log = LogFactory.getLog(WebappXMLResources.class);
65  
66      /**
67       * <p>Create a new {@link org.apache.commons.resources.Resources} instance with the specified
68       * logical name and base resource URL.</p>
69       *
70       * @param name Logical name of the new instance
71       * @param base Base URL of the family of properties files that contain
72       *  the resource keys and values
73       * @param servletContext the <code>ServletContext</code> instance
74       *  to use for resolving resource references
75       */
76      public WebappXMLResources(String name, String base,
77                                     ServletContext servletContext) {
78  
79          super(name, base);
80          this.servletContext = servletContext;
81  
82      }
83  
84  
85      // ----------------------------------------------------- Instance Variables
86  
87      /**
88       * <p>The <code>ServletContext</code> instance for resolving
89       * our resources references.</p>
90       */
91      private ServletContext servletContext = null;
92  
93  
94      // ------------------------------------------------------ Protected Methods
95  
96  
97      /**
98       * <p>Return a <code>Map</code> containing the name-value mappings for
99       * the specified base URL and requested <code>Locale</code>, if there
100      * are any.  If there are no defined mappings for the specified
101      * <code>Locale</code>, return an empty <code>Map</code> instead.</p>
102      *
103      * <p>Concrete subclasses must override this method to perform the
104      * appropriate lookup.  A typical implementation will construct an
105      * absolute URL based on the specified base URL and <code>Locale</code>,
106      * retrieve the specified resource file (if any), and parse it into
107      * a <code>Map</code> structure.</p>
108      *
109      * <p>Caching of previously retrieved <code>Map</code>s (if any) should
110      * be performed by callers of this method.  Therefore, this method should
111      * always attempt to retrieve the specified resource and load it
112      * appropriately.</p>
113      *
114      * @param baseUrl Base URL of the resource files for this {@link org.apache.commons.resources.Resources}
115      *  instance
116      * @param locale <code>Locale</code> for which name-value mappings
117      *  are requested
118      * @return A name-value Map for the specified URL and locale.
119      */
120     protected Map getLocaleMap(String baseUrl, Locale locale) {
121 
122         if (getLog().isDebugEnabled()) {
123             getLog().debug("Loading locale '" + locale + "' resources from base '" +
124                     baseUrl + "'");
125         }
126 
127         Map map = new HashMap();
128         String name = baseUrl + getLocaleSuffix(locale) + ".xml";
129         InputStream stream = null;
130 
131         try {
132 
133             // Open an input stream to the URL for this locale (if any)
134             if (getLog().isTraceEnabled()) {
135                 getLog().trace("Complete path is '" + name + "'");
136             }
137             stream = servletContext.getResourceAsStream(name);
138 
139             // Create and configure a new Digester instance
140             if (stream != null) {
141 
142                 if (getLog().isTraceEnabled()) {
143                     getLog().trace("Creating Digester instance");
144                 }
145                 Digester digester = new Digester();
146                 digester.setNamespaceAware(false);
147                 digester.setValidating(false);
148                 digester.push(map);
149                 digester.addCallMethod("resources/resource", "put", 2,
150                                        new String[] { "java.lang.Object",
151                                                       "java.lang.Object" });
152                 digester.addCallParam("resources/resource", 0, "id");
153                 digester.addCallParam("resources/resource", 1);
154 
155                 // Parse the input stream and populate the name-value mappings
156                 if (getLog().isTraceEnabled()) {
157                     getLog().trace("Parsing input resource");
158                 }
159                 digester.parse(stream);
160 
161             }
162 
163         } catch (FileNotFoundException e) {
164 
165             // Log and swallow this exception
166             if (getLog().isDebugEnabled()) {
167                 getLog().debug("No resources for locale '" + locale +
168                           "' from base '" + baseUrl + "'");
169             }
170             map.clear();
171 
172         } catch (IOException e) {
173 
174             // Log and swallow this exception
175             getLog().warn("IOException loading locale '" + locale +
176                      "' from base '" + baseUrl + "'", e);
177             map.clear();
178 
179         } catch (SAXException e) {
180 
181             // Log and swallow this exception
182             getLog().warn("SAXException loading locale '" + locale +
183                      "' from base '" + baseUrl + "'", e);
184             map.clear();
185 
186         } finally {
187 
188             // Close the input stream that was opened earlier
189             if (stream != null) {
190                 try {
191                     stream.close();
192                 } catch (Exception e) {
193                     // standard io
194                 }
195                 stream = null;
196             }
197 
198         }
199 
200         // Return the populated (or empty) map
201         return (map);
202 
203 
204     }
205 
206     /**
207      * Accessor method for Log instance.
208      *
209      * The Log instance variable is transient and
210      * accessing it through this method ensures it
211      * is re-initialized when this instance is
212      * de-serialized.
213      *
214      * @return The Log instance.
215      */
216     private Log getLog() {
217         if (log == null) {
218             log =  LogFactory.getLog(WebappXMLResources.class);
219         }
220         return log;
221     }
222 
223 }