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 }