1 /* 2 * $Id: BaseLocaleUrlDefinitionDAO.java 797540 2009-07-24 15:42:00Z 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.io.FileNotFoundException; 25 import java.io.IOException; 26 import java.net.URL; 27 import java.net.URLConnection; 28 import java.util.ArrayList; 29 import java.util.HashMap; 30 import java.util.List; 31 import java.util.Locale; 32 import java.util.Map; 33 import java.util.Set; 34 35 import org.apache.tiles.Definition; 36 import org.apache.tiles.Initializable; 37 import org.apache.tiles.TilesApplicationContext; 38 import org.apache.tiles.awareness.TilesApplicationContextAware; 39 import org.apache.tiles.definition.DefinitionsFactory; 40 import org.apache.tiles.definition.DefinitionsFactoryException; 41 import org.apache.tiles.definition.DefinitionsReader; 42 import org.apache.tiles.definition.RefreshMonitor; 43 import org.apache.tiles.definition.digester.DigesterDefinitionsReader; 44 import org.apache.tiles.impl.BasicTilesContainer; 45 import org.apache.tiles.reflect.ClassUtil; 46 import org.slf4j.Logger; 47 import org.slf4j.LoggerFactory; 48 49 /** 50 * Base abstract class for a DAO that is based on URLs and locale as a 51 * customization key. 52 * 53 * @version $Rev: 797540 $ $Date: 2009-07-24 17:42:00 +0200 (ven, 24 lug 2009) $ 54 * @since 2.1.0 55 */ 56 @SuppressWarnings("deprecation") 57 public abstract class BaseLocaleUrlDefinitionDAO implements 58 DefinitionDAO<Locale>, Initializable, TilesApplicationContextAware, 59 RefreshMonitor, URLReader { 60 61 /** 62 * The logging object. 63 */ 64 private final Logger log = LoggerFactory 65 .getLogger(BaseLocaleUrlDefinitionDAO.class); 66 67 /** 68 * Compatibility constant. 69 * 70 * @deprecated use {@link DefinitionsFactory#DEFINITIONS_CONFIG} to avoid 71 * namespace collisions. 72 */ 73 private static final String LEGACY_DEFINITIONS_CONFIG = "definitions-config"; 74 75 /** 76 * Contains the URL objects identifying where configuration data is found. 77 * 78 * @since 2.1.0 79 */ 80 protected List<URL> sourceURLs; 81 82 /** 83 * Contains the dates that the URL sources were last modified. 84 * 85 * @since 2.1.0 86 */ 87 protected Map<String, Long> lastModifiedDates; 88 89 /** 90 * The application context. 91 * 92 * @since 2.1.0 93 */ 94 protected TilesApplicationContext applicationContext; 95 96 /** 97 * Reader used to get definitions from the sources. 98 * 99 * @since 2.1.0 100 */ 101 protected DefinitionsReader reader; 102 103 /** 104 * Constructor. 105 */ 106 public BaseLocaleUrlDefinitionDAO() { 107 sourceURLs = new ArrayList<URL>(); 108 lastModifiedDates = new HashMap<String, Long>(); 109 } 110 111 /** {@inheritDoc}*/ 112 public void setSourceURLs(List<URL> sourceURLs) { 113 this.sourceURLs = sourceURLs; 114 } 115 116 /** {@inheritDoc}*/ 117 public void setReader(DefinitionsReader reader) { 118 this.reader = reader; 119 } 120 121 /** {@inheritDoc}*/ 122 public void addSourceURL(URL sourceURL) { 123 if (sourceURLs == null) { 124 sourceURLs = new ArrayList<URL>(); 125 } 126 sourceURLs.add(sourceURL); 127 } 128 129 /** {@inheritDoc} */ 130 public void setApplicationContext(TilesApplicationContext applicationContext) { 131 this.applicationContext = applicationContext; 132 } 133 134 /** {@inheritDoc} */ 135 public void init(Map<String, String> params) { 136 identifySources(params); 137 String readerClassName = params 138 .get(DefinitionsFactory.READER_IMPL_PROPERTY); 139 140 if (readerClassName != null) { 141 reader = (DefinitionsReader) ClassUtil.instantiate(readerClassName); 142 } else { 143 reader = new DigesterDefinitionsReader(); 144 } 145 reader.init(params); 146 } 147 148 /** {@inheritDoc} */ 149 public boolean refreshRequired() { 150 boolean status = false; 151 152 Set<String> urls = lastModifiedDates.keySet(); 153 154 try { 155 for (String urlPath : urls) { 156 Long lastModifiedDate = lastModifiedDates.get(urlPath); 157 URL url = new URL(urlPath); 158 URLConnection connection = url.openConnection(); 159 connection.connect(); 160 long newModDate = connection.getLastModified(); 161 if (newModDate != lastModifiedDate) { 162 status = true; 163 break; 164 } 165 } 166 } catch (Exception e) { 167 log.warn("Exception while monitoring update times.", e); 168 return true; 169 } 170 return status; 171 } 172 173 /** 174 * Detects the sources to load. 175 * 176 * @param initParameters The initialization parameters. 177 * @since 2.1.0 178 */ 179 protected void identifySources(Map<String, String> initParameters) { 180 if (applicationContext == null) { 181 throw new IllegalStateException( 182 "The TilesApplicationContext cannot be null"); 183 } 184 185 String resourceString = getResourceString(initParameters); 186 String[] resources = getResourceNames(resourceString); 187 188 try { 189 for (int i = 0; i < resources.length; i++) { 190 Set<URL> urls = applicationContext.getResources(resources[i]); 191 if (urls != null && !urls.isEmpty()) { 192 for (URL resourceUrl : urls) { 193 if (resourceUrl != null) { 194 if (log.isDebugEnabled()) { 195 log.debug("Adding resource '" + resourceUrl 196 + "' to definitions factory."); 197 } 198 String externalForm = resourceUrl.toExternalForm(); 199 if (externalForm.indexOf('_', externalForm 200 .lastIndexOf("/")) < 0) { 201 sourceURLs.add(resourceUrl); 202 } else if (log.isDebugEnabled()) { 203 log.debug("Not adding resource '" + resourceUrl 204 + "' to definitions factory because it is " 205 + "supposed to be an internationalization."); 206 } 207 208 } else { 209 log.warn("Unable to find configured definition '" 210 + resources[i] + "'"); 211 } 212 } 213 } else { 214 log.warn("Unable to find resources under the name '" 215 + resources[i] + "'"); 216 } 217 } 218 } catch (IOException e) { 219 throw new DefinitionsFactoryException( 220 "Unable to parse definitions from " + resourceString, e); 221 } 222 } 223 224 /** 225 * Derive the resource string from the initialization parameters. If no 226 * parameter {@link DefinitionsFactory#DEFINITIONS_CONFIG} is available, 227 * attempts to retrieve the deprecated 228 * <code>org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG</code> 229 * parameter and {@link #LEGACY_DEFINITIONS_CONFIG}. If neither are 230 * available, returns "/WEB-INF/tiles.xml". 231 * 232 * @param parms The initialization parameters. 233 * @return resource string to be parsed. 234 */ 235 protected String getResourceString(Map<String, String> parms) { 236 String resourceStr = parms.get(DefinitionsFactory.DEFINITIONS_CONFIG); 237 if (resourceStr == null) { 238 resourceStr = parms.get(BasicTilesContainer.DEFINITIONS_CONFIG); 239 } 240 if (resourceStr == null) { 241 resourceStr = parms.get(LEGACY_DEFINITIONS_CONFIG); 242 } 243 if (resourceStr == null) { 244 resourceStr = "/WEB-INF/tiles.xml"; 245 } 246 return resourceStr; 247 } 248 249 /** 250 * Parse the resourceString into a list of resource paths which can be 251 * loaded by the application context. 252 * 253 * @param resourceString comma separated resources 254 * @return parsed resources 255 */ 256 protected String[] getResourceNames(String resourceString) { 257 return resourceString.split(","); 258 } 259 260 /** 261 * Loads definitions from an URL without loading from "parent" URLs. 262 * 263 * @param url The URL to read. 264 * @return The definition map that has been read. 265 */ 266 protected Map<String, Definition> loadDefinitionsFromURL(URL url) { 267 Map<String, Definition> defsMap = null; 268 try { 269 URLConnection connection = url.openConnection(); 270 connection.connect(); 271 lastModifiedDates.put(url.toExternalForm(), connection 272 .getLastModified()); 273 274 // Definition must be collected, starting from the base 275 // source up to the last localized file. 276 defsMap = reader.read(connection.getInputStream()); 277 } catch (FileNotFoundException e) { 278 // File not found. continue. 279 if (log.isDebugEnabled()) { 280 log.debug("File " + null + " not found, continue"); 281 } 282 } catch (IOException e) { 283 throw new DefinitionsFactoryException( 284 "I/O error processing configuration.", e); 285 } 286 287 return defsMap; 288 } 289 }