1 /*
2 * $Id: WebappPropertyResources.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.Locale;
30 import java.util.Map;
31 import java.util.Properties;
32
33 import javax.servlet.ServletContext;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 /**
39 * <p>Concrete implementation of {@link org.apache.commons.resources.Resources} that wraps a family
40 * (one per <code>Locale</code> of properties files that share a base
41 * context-relative path for servlet context resources, and have
42 * name suffixes reflecting the <code>Locale</code> for which
43 * the document's messages apply. Resources are looked up in a hierarchy
44 * of properties files in a manner identical to that performed by
45 * <code>java.util.ResourceBundle.getBundle().</code>.</p>
46 *
47 * <p>The base resource path passed to our constructor must contain the
48 * context-relative base name of the properties file family.
49 * For example, if the base path is passed as
50 * <code>http://localhost/foo/Bar</code>, the resources for the
51 * <code>en_US</code> Locale would be stored under URL
52 * <code>http://localhost/foo/Bar_en_US.properties</code>, and the default
53 * resources would be stored in
54 * <code>http://localhost/foo/Bar.properties</code>.</p>
55 */
56 public class WebappPropertyResources extends CollectionResourcesBase {
57
58 /**
59 * <p>The <code>Log</code> instance for this class.</p>
60 */
61 private transient Log log =
62 LogFactory.getLog(WebappPropertyResources.class);
63
64 // ----------------------------------------------------------- Constructors
65
66
67 /**
68 * <p>Create a new {@link org.apache.commons.resources.Resources} instance with the specified
69 * logical name and base resource URL.</p>
70 *
71 * @param name Logical name of the new instance
72 * @param base Base URL of the family of properties files that contain
73 * the resource keys and values
74 * @param servletContext the <code>ServletContext</code> instance
75 * to use for resolving resource references
76 */
77 public WebappPropertyResources(String name, String base,
78 ServletContext servletContext) {
79
80 super(name, base);
81 this.servletContext = servletContext;
82
83 }
84
85
86 // ----------------------------------------------------- Instance Variables
87
88
89 /**
90 * <p>The <code>ServletContext</code> instance for resolving
91 * our resources references.</p>
92 */
93 private ServletContext servletContext = null;
94
95
96 // ------------------------------------------------------ Protected Methods
97
98
99 /**
100 * <p>Return a <code>Map</code> containing the name-value mappings for
101 * the specified base URL and requested <code>Locale</code>, if there
102 * are any. If there are no defined mappings for the specified
103 * <code>Locale</code>, return an empty <code>Map</code> instead.</p>
104 *
105 * <p>Concrete subclasses must override this method to perform the
106 * appropriate lookup. A typical implementation will construct an
107 * absolute URL based on the specified base URL and <code>Locale</code>,
108 * retrieve the specified resource file (if any), and parse it into
109 * a <code>Map</code> structure.</p>
110 *
111 * <p>Caching of previously retrieved <code>Map</code>s (if any) should
112 * be performed by callers of this method. Therefore, this method should
113 * always attempt to retrieve the specified resource and load it
114 * appropriately.</p>
115 *
116 * @param baseUrl Base URL of the resource files for this {@link org.apache.commons.resources.Resources}
117 * instance
118 * @param locale <code>Locale</code> for which name-value mappings
119 * are requested
120 * @return A name-value Map for the specified URL and locale.
121 */
122 protected Map getLocaleMap(String baseUrl, Locale locale) {
123
124 if (getLog().isDebugEnabled()) {
125 getLog().debug("Loading locale '" + locale + "' resources from base '" +
126 baseUrl + "'");
127 }
128
129 Properties props = new Properties();
130 String name = baseUrl.replace('.', '/');
131 name += getLocaleSuffix(locale) + ".properties";
132
133 InputStream stream = null;
134
135 try {
136
137 // Open an input stream to the URL for this locale (if any)
138 if (getLog().isTraceEnabled()) {
139 getLog().trace("Complete path is '" + name + "'");
140 }
141 try{
142 stream = servletContext.getResourceAsStream(name);
143 }
144 catch(Exception e){
145 // not found
146 // try ContextClassLoader
147 }
148 if (stream == null){
149
150 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
151 if (classLoader == null) {
152 classLoader = this.getClass().getClassLoader();
153 }
154 stream = classLoader.getResourceAsStream(name);
155 }
156
157 // Parse the input stream and populate the name-value mappings map
158 if (stream != null) {
159 if (getLog().isTraceEnabled()) {
160 getLog().trace("Parsing input resource");
161 }
162 props.load(stream);
163 }
164
165 } catch (FileNotFoundException e) {
166
167 // Log and swallow this exception
168 if (getLog().isDebugEnabled()) {
169 getLog().debug("No resources for locale '" + locale +
170 "' from base '" + baseUrl + "'");
171 }
172 props.clear();
173
174 } catch (IOException e) {
175
176 getLog().warn("IOException loading locale '" + locale +
177 "' from base '" + baseUrl + "'", e);
178 props.clear();
179
180 } finally {
181
182 // Close the input stream that was opened earlier
183 if (stream != null) {
184 try {
185 stream.close();
186 } catch (IOException e) {
187 getLog().error("Error closing stream.", e);
188 }
189 stream = null;
190 }
191
192 }
193
194 // Return the populated (or empty) properties
195 return (props);
196
197 }
198
199 /**
200 * Accessor method for Log instance.
201 *
202 * The Log instance variable is transient and
203 * accessing it through this method ensures it
204 * is re-initialized when this instance is
205 * de-serialized.
206 *
207 * @return The Log instance.
208 */
209 private Log getLog() {
210 if (log == null) {
211 log = LogFactory.getLog(WebappPropertyResources.class);
212 }
213 return log;
214 }
215
216 }