001 /* 002 * $Id: WebappPropertyResources.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.util.Locale; 030 import java.util.Map; 031 import java.util.Properties; 032 033 import javax.servlet.ServletContext; 034 035 import org.apache.commons.logging.Log; 036 import org.apache.commons.logging.LogFactory; 037 038 /** 039 * <p>Concrete implementation of {@link org.apache.commons.resources.Resources} that wraps a family 040 * (one per <code>Locale</code> of properties files that share a base 041 * context-relative path for servlet context resources, and have 042 * name suffixes reflecting the <code>Locale</code> for which 043 * the document's messages apply. Resources are looked up in a hierarchy 044 * of properties files in a manner identical to that performed by 045 * <code>java.util.ResourceBundle.getBundle().</code>.</p> 046 * 047 * <p>The base resource path passed to our constructor must contain the 048 * context-relative base name of the properties file family. 049 * For example, if the base path is passed as 050 * <code>http://localhost/foo/Bar</code>, the resources for the 051 * <code>en_US</code> Locale would be stored under URL 052 * <code>http://localhost/foo/Bar_en_US.properties</code>, and the default 053 * resources would be stored in 054 * <code>http://localhost/foo/Bar.properties</code>.</p> 055 */ 056 public class WebappPropertyResources extends CollectionResourcesBase { 057 058 /** 059 * <p>The <code>Log</code> instance for this class.</p> 060 */ 061 private transient Log log = 062 LogFactory.getLog(WebappPropertyResources.class); 063 064 // ----------------------------------------------------------- Constructors 065 066 067 /** 068 * <p>Create a new {@link org.apache.commons.resources.Resources} instance with the specified 069 * logical name and base resource URL.</p> 070 * 071 * @param name Logical name of the new instance 072 * @param base Base URL of the family of properties files that contain 073 * the resource keys and values 074 * @param servletContext the <code>ServletContext</code> instance 075 * to use for resolving resource references 076 */ 077 public WebappPropertyResources(String name, String base, 078 ServletContext servletContext) { 079 080 super(name, base); 081 this.servletContext = servletContext; 082 083 } 084 085 086 // ----------------------------------------------------- Instance Variables 087 088 089 /** 090 * <p>The <code>ServletContext</code> instance for resolving 091 * our resources references.</p> 092 */ 093 private ServletContext servletContext = null; 094 095 096 // ------------------------------------------------------ Protected Methods 097 098 099 /** 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 }