1 /* 2 * $Id: CachingLocaleUrlDefinitionDAO.java 823662 2009-10-09 18:48:03Z apetrelli $ 3 * 4 * Licensed to the Apache Software Foundation (ASF) under one 5 * or more contributor license agreements. See the NOTICE file 6 * distributed with this work for additional information 7 * regarding copyright ownership. The ASF licenses this file 8 * to you under the Apache License, Version 2.0 (the 9 * "License"); you may not use this file except in compliance 10 * with the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, 15 * software distributed under the License is distributed on an 16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17 * KIND, either express or implied. See the License for the 18 * specific language governing permissions and limitations 19 * under the License. 20 */ 21 22 package org.apache.tiles.definition.dao; 23 24 import java.net.MalformedURLException; 25 import java.net.URL; 26 import java.util.HashMap; 27 import java.util.LinkedHashMap; 28 import java.util.Locale; 29 import java.util.Map; 30 31 import org.apache.tiles.Definition; 32 import org.apache.tiles.definition.DefinitionsFactoryException; 33 import org.apache.tiles.definition.NoSuchDefinitionException; 34 import org.apache.tiles.definition.Refreshable; 35 import org.apache.tiles.definition.pattern.BasicPatternDefinitionResolver; 36 import org.apache.tiles.definition.pattern.PatternDefinitionResolver; 37 import org.apache.tiles.definition.pattern.PatternDefinitionResolverAware; 38 import org.apache.tiles.definition.pattern.wildcard.WildcardDefinitionPatternMatcherFactory; 39 import org.apache.tiles.util.LocaleUtil; 40 41 /** 42 * <p> 43 * A definitions DAO (loading URLs and using Locale as a customization key) that 44 * caches definitions that have been loaded in a raw way (i.e. with inheritance 45 * that is not resolved). 46 * </p> 47 * <p> 48 * It can check if the URLs change, but by default this feature is turned off. 49 * </p> 50 * 51 * @version $Rev: 823662 $ $Date: 2009-10-09 20:48:03 +0200 (ven, 09 ott 2009) $ 52 * @since 2.1.0 53 */ 54 public class CachingLocaleUrlDefinitionDAO extends BaseLocaleUrlDefinitionDAO 55 implements Refreshable, PatternDefinitionResolverAware<Locale> { 56 57 /** 58 * Initialization parameter to set whether we want to refresh URLs when they 59 * change. 60 * 61 * @since 2.1.0 62 */ 63 public static final String CHECK_REFRESH_INIT_PARAMETER = 64 "org.apache.tiles.definition.dao.LocaleUrlDefinitionDAO.CHECK_REFRESH"; 65 66 /** 67 * The locale-specific set of definitions objects. 68 * 69 * @since 2.1.0 70 */ 71 protected Map<Locale, Map<String, Definition>> locale2definitionMap; 72 73 /** 74 * Flag that, when <code>true</code>, enables automatic checking of URLs 75 * changing. 76 * 77 * @since 2.1.0 78 */ 79 protected boolean checkRefresh = false; 80 81 /** 82 * Resolves definitions using patterns. 83 * 84 * @since 2.2.0 85 */ 86 protected PatternDefinitionResolver<Locale> definitionResolver; 87 88 /** 89 * Constructor. 90 * 91 * @since 2.1.0 92 */ 93 public CachingLocaleUrlDefinitionDAO() { 94 locale2definitionMap = new HashMap<Locale, Map<String, Definition>>(); 95 } 96 97 /** {@inheritDoc} */ 98 @Override 99 public void init(Map<String, String> params) { 100 super.init(params); 101 102 String param = params.get(CHECK_REFRESH_INIT_PARAMETER); 103 checkRefresh = "true".equals(param); 104 WildcardDefinitionPatternMatcherFactory definitionPatternMatcherFactory = 105 new WildcardDefinitionPatternMatcherFactory(); 106 definitionResolver = new BasicPatternDefinitionResolver<Locale>( 107 definitionPatternMatcherFactory, 108 definitionPatternMatcherFactory); 109 } 110 111 /** {@inheritDoc} */ 112 public void setPatternDefinitionResolver( 113 PatternDefinitionResolver<Locale> definitionResolver) { 114 this.definitionResolver = definitionResolver; 115 } 116 117 /** {@inheritDoc} */ 118 public Definition getDefinition(String name, Locale customizationKey) { 119 Definition retValue = null; 120 if (customizationKey == null) { 121 customizationKey = LocaleUtil.NULL_LOCALE; 122 } 123 Map<String, Definition> definitions = getDefinitions(customizationKey); 124 if (definitions != null) { 125 retValue = definitions.get(name); 126 127 if (retValue == null) { 128 retValue = getDefinitionFromResolver(name, customizationKey); 129 130 if (retValue != null) { 131 try { 132 synchronized (definitions) { 133 definitions.put(name, retValue); 134 } 135 } catch (NoSuchDefinitionException ex) { 136 throw new IllegalStateException( 137 "Unable to resolve wildcard mapping", ex); 138 } 139 } 140 } 141 } 142 143 return retValue; 144 } 145 146 /** {@inheritDoc} */ 147 public Map<String, Definition> getDefinitions(Locale customizationKey) { 148 if (customizationKey == null) { 149 customizationKey = LocaleUtil.NULL_LOCALE; 150 } 151 Map<String, Definition> retValue = locale2definitionMap 152 .get(customizationKey); 153 if (retValue == null || (checkRefresh && refreshRequired())) { 154 retValue = checkAndloadDefinitions(customizationKey); 155 } 156 return retValue; 157 } 158 159 /** {@inheritDoc} */ 160 public synchronized void refresh() { 161 if (refreshRequired()) { 162 locale2definitionMap.clear(); 163 } 164 } 165 166 /** 167 * Sets the flag to check URL refresh. If not called, the default is 168 * <code>false</code>. 169 * 170 * @param checkRefresh When <code>true</code>, enables automatic checking 171 * of URLs changing. 172 * @since 2.1.0 173 */ 174 public void setCheckRefresh(boolean checkRefresh) { 175 this.checkRefresh = checkRefresh; 176 } 177 178 /** 179 * Returns a definition from the definition resolver. 180 * 181 * @param name The name of the definition. 182 * @param customizationKey The customization key to use. 183 * @return The resolved definition. 184 */ 185 protected Definition getDefinitionFromResolver(String name, 186 Locale customizationKey) { 187 return definitionResolver.resolveDefinition(name, 188 customizationKey); 189 } 190 191 /** 192 * Checks if URLs have changed. If yes, it clears the cache. Then continues 193 * loading definitions. 194 * 195 * @param customizationKey The locale to use when loading URLs. 196 * @return The loaded definitions. 197 * @since 2.1.0 198 */ 199 protected synchronized Map<String, Definition> checkAndloadDefinitions( 200 Locale customizationKey) { 201 if (checkRefresh && refreshRequired()) { 202 locale2definitionMap.clear(); 203 } 204 loadDefinitions(customizationKey); 205 return locale2definitionMap.get(customizationKey); 206 } 207 208 /** 209 * Tries to load definitions if necessary. 210 * 211 * @param customizationKey The locale to use when loading URLs. 212 * @return The loaded definitions. 213 * @since 2.1.0 214 */ 215 protected Map<String, Definition> loadDefinitions(Locale customizationKey) { 216 Map<String, Definition> localeDefsMap = locale2definitionMap 217 .get(customizationKey); 218 if (localeDefsMap != null) { 219 return localeDefsMap; 220 } 221 222 return loadDefinitionsFromURLs(customizationKey); 223 } 224 225 /** 226 * Loads definitions from the URLs. 227 * 228 * @param customizationKey The locale to use when loading URLs. 229 * @return The loaded definitions. 230 * @since 2.1.0 231 */ 232 protected Map<String, Definition> loadDefinitionsFromURLs(Locale customizationKey) { 233 Map<String, Definition> localeDefsMap; 234 235 String postfix = LocaleUtil.calculatePostfix(customizationKey); 236 Locale parentLocale = LocaleUtil.getParentLocale(customizationKey); 237 localeDefsMap = new LinkedHashMap<String, Definition>(); 238 if (parentLocale != null) { 239 Map<String, Definition> parentDefs = loadParentDefinitions(parentLocale); 240 if (parentDefs != null) { 241 localeDefsMap.putAll(parentDefs); 242 } 243 } 244 // For each source, the URL must be loaded. 245 for (URL url : sourceURLs) { 246 String path = url.toExternalForm(); 247 248 String newPath = LocaleUtil.concatPostfix(path, postfix); 249 try { 250 URL newUrl = new URL(newPath); 251 Map<String, Definition> defsMap = loadDefinitionsFromURL(newUrl); 252 if (defsMap != null) { 253 localeDefsMap.putAll(defsMap); 254 } 255 } catch (MalformedURLException e) { 256 throw new DefinitionsFactoryException("Error parsing URL " 257 + newPath, e); 258 } 259 } 260 Map<String, Definition> defsMap = definitionResolver 261 .storeDefinitionPatterns(copyDefinitionMap(localeDefsMap), 262 customizationKey); 263 locale2definitionMap.put(customizationKey, defsMap); 264 return localeDefsMap; 265 } 266 267 /** 268 * Loads the raw definitions from the URLs associated with a locale. 269 * 270 * @param customizationKey The locale to use when loading URLs. 271 * @return The loaded definitions. 272 * @since 2.1.3 273 * @deprecated Use {@link #loadDefinitionsFromURLs(Locale)}. 274 */ 275 protected Map<String, Definition> loadRawDefinitionsFromURLs( 276 Locale customizationKey) { 277 return loadDefinitionsFromURLs(customizationKey); 278 } 279 280 /** 281 * Loads parent definitions, i.e. definitions mapped to a parent locale. 282 * 283 * @param parentLocale The locale to use when loading URLs. 284 * @return The loaded parent definitions. 285 * @since 2.1.0 286 */ 287 protected Map<String, Definition> loadParentDefinitions(Locale parentLocale) { 288 return loadDefinitions(parentLocale); 289 } 290 291 /** 292 * Operations to be done after definitions are loaded. 293 * 294 * @param localeDefsMap The loaded definitions. 295 * @param customizationKey The locale to use when loading URLs. 296 * @since 2.1.0 297 * @deprecated Never used. 298 */ 299 protected void postDefinitionLoadOperations( 300 Map<String, Definition> localeDefsMap, Locale customizationKey) { 301 302 definitionResolver.storeDefinitionPatterns(localeDefsMap, customizationKey); 303 } 304 305 /** 306 * Copies the definition map to be passed to a higher level of customization 307 * key. 308 * 309 * @param localeDefsMap The map of definition to be copied. 310 * @return The copy of the definition map. This particular implementation 311 * return the <code>localeDefsMap</code> itself. 312 * @since 2.1.4 313 */ 314 protected Map<String, Definition> copyDefinitionMap( 315 Map<String, Definition> localeDefsMap) { 316 return localeDefsMap; 317 } 318 }