1 /* 2 * $Id: ReloadableDefinitionsFactory.java 471754 2006-11-06 14:55:09Z husted $ 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.struts.tiles.definition; 23 24 import java.util.Enumeration; 25 import java.util.HashMap; 26 import java.util.Map; 27 28 import javax.servlet.ServletConfig; 29 import javax.servlet.ServletContext; 30 import javax.servlet.ServletRequest; 31 import javax.servlet.http.HttpServletRequest; 32 33 import org.apache.struts.tiles.ComponentDefinition; 34 import org.apache.struts.tiles.ComponentDefinitionsFactory; 35 import org.apache.struts.tiles.DefinitionsFactoryException; 36 import org.apache.struts.tiles.FactoryNotFoundException; 37 import org.apache.struts.tiles.xmlDefinition.I18nFactorySet; 38 import org.apache.struts.util.RequestUtils; 39 40 /** 41 * A reloadable factory. 42 * This factory is the main entrance to any factory implementation. It takes in 43 * charge real implementation instance, and allows reloading by creating a new 44 * instance. 45 * 46 * @since Struts 1.1 47 * @version $Rev: 471754 $ $Date: 2006-11-06 08:55:09 -0600 (Mon, 06 Nov 2006) $ 48 */ 49 public class ReloadableDefinitionsFactory implements ComponentDefinitionsFactory { 50 51 /** 52 * The real factory instance. 53 */ 54 protected ComponentDefinitionsFactory factory = null; 55 56 /** 57 * Initialization parameters. 58 */ 59 protected Map properties = null; 60 61 /** 62 * Name of init property carrying factory class name. 63 */ 64 public static final String DEFINITIONS_FACTORY_CLASSNAME = 65 "definitions-factory-class"; 66 67 /** 68 * Constructor. 69 * Create a factory according to servlet settings. 70 * @param servletContext Our servlet context. 71 * @param servletConfig Our servlet config. 72 * @throws DefinitionsFactoryException If factory creation fail. 73 */ 74 public ReloadableDefinitionsFactory( 75 ServletContext servletContext, 76 ServletConfig servletConfig) 77 throws DefinitionsFactoryException { 78 79 properties = new ServletPropertiesMap(servletConfig); 80 factory = createFactory(servletContext, properties); 81 } 82 83 /** 84 * Constructor. 85 * Create a factory according to servlet settings. 86 * @param servletContext Our servlet context. 87 * @param properties Map containing all properties. 88 * @throws DefinitionsFactoryException If factory creation fail. 89 */ 90 public ReloadableDefinitionsFactory( 91 ServletContext servletContext, 92 Map properties) 93 throws DefinitionsFactoryException { 94 95 this.properties = properties; 96 factory = createFactory(servletContext, properties); 97 } 98 99 /** 100 * Create Definition factory from provided classname. 101 * If a factory class name is provided, a factory of this class is created. Otherwise, 102 * a default factory is created. 103 * Factory must have a constructor taking ServletContext and Map as parameter. 104 * @param classname Class name of the factory to create. 105 * @param servletContext Servlet Context passed to newly created factory. 106 * @param properties Map of name/property passed to newly created factory. 107 * @return newly created factory. 108 * @throws DefinitionsFactoryException If an error occur while initializing factory 109 */ 110 public ComponentDefinitionsFactory createFactoryFromClassname( 111 ServletContext servletContext, 112 Map properties, 113 String classname) 114 throws DefinitionsFactoryException { 115 116 if (classname == null) { 117 return createFactory(servletContext, properties); 118 } 119 120 // Try to create from classname 121 try { 122 Class factoryClass = RequestUtils.applicationClass(classname); 123 ComponentDefinitionsFactory factory = 124 (ComponentDefinitionsFactory) factoryClass.newInstance(); 125 factory.initFactory(servletContext, properties); 126 return factory; 127 128 } catch (ClassCastException ex) { // Bad classname 129 throw new DefinitionsFactoryException( 130 "Error - createDefinitionsFactory : Factory class '" 131 + classname 132 + " must implements 'ComponentDefinitionsFactory'.", 133 ex); 134 135 } catch (ClassNotFoundException ex) { // Bad classname 136 throw new DefinitionsFactoryException( 137 "Error - createDefinitionsFactory : Bad class name '" 138 + classname 139 + "'.", 140 ex); 141 142 } catch (InstantiationException ex) { // Bad constructor or error 143 throw new DefinitionsFactoryException(ex); 144 145 } catch (IllegalAccessException ex) { 146 throw new DefinitionsFactoryException(ex); 147 } 148 149 } 150 151 /** 152 * Create default Definition factory. 153 * Factory must have a constructor taking ServletContext and Map as parameter. 154 * In this implementation, default factory is of class I18nFactorySet 155 * @param servletContext Servlet Context passed to newly created factory. 156 * @param properties Map of name/property passed to newly created factory. 157 * @return newly created factory. 158 * @throws DefinitionsFactoryException If an error occur while initializing factory 159 */ 160 public ComponentDefinitionsFactory createDefaultFactory( 161 ServletContext servletContext, 162 Map properties) 163 throws DefinitionsFactoryException { 164 165 ComponentDefinitionsFactory factory = 166 new I18nFactorySet(servletContext, properties); 167 168 return factory; 169 } 170 171 /** 172 * Create Definition factory. 173 * Convenience method. ServletConfig is wrapped into a Map allowing retrieval 174 * of init parameters. Factory classname is also retrieved, as well as debug level. 175 * Finally, approriate createDefinitionsFactory() is called. 176 * @param servletContext Servlet Context passed to newly created factory. 177 * @param properties Map containing all properties. 178 */ 179 public ComponentDefinitionsFactory createFactory( 180 ServletContext servletContext, 181 Map properties) 182 throws DefinitionsFactoryException { 183 184 String classname = (String) properties.get(DEFINITIONS_FACTORY_CLASSNAME); 185 186 if (classname != null) { 187 return createFactoryFromClassname(servletContext, properties, classname); 188 } 189 190 return new I18nFactorySet(servletContext, properties); 191 } 192 193 /** 194 * Get a definition by its name. 195 * Call appropriate method on underlying factory instance. 196 * Throw appropriate exception if definition or definition factory is not found. 197 * @param definitionName Name of requested definition. 198 * @param request Current servlet request. 199 * @param servletContext Current servlet context. 200 * @throws FactoryNotFoundException Can't find definition factory. 201 * @throws DefinitionsFactoryException General error in factory while getting definition. 202 */ 203 public ComponentDefinition getDefinition( 204 String definitionName, 205 ServletRequest request, 206 ServletContext servletContext) 207 throws FactoryNotFoundException, DefinitionsFactoryException { 208 209 return factory.getDefinition( 210 definitionName, 211 (HttpServletRequest) request, 212 servletContext); 213 } 214 215 /** 216 * Reload underlying factory. 217 * Reload is done by creating a new factory instance, and replacing the old instance 218 * with the new one. 219 * @param servletContext Current servlet context. 220 * @throws DefinitionsFactoryException If factory creation fails. 221 */ 222 public void reload(ServletContext servletContext) 223 throws DefinitionsFactoryException { 224 225 ComponentDefinitionsFactory newInstance = 226 createFactory(servletContext, properties); 227 228 factory = newInstance; 229 } 230 231 /** 232 * Get underlying factory instance. 233 * @return ComponentDefinitionsFactory 234 */ 235 public ComponentDefinitionsFactory getFactory() { 236 return factory; 237 } 238 239 /** 240 * Init factory. 241 * This method is required by interface ComponentDefinitionsFactory. It is 242 * not used in this implementation, as it manages itself the underlying creation 243 * and initialization. 244 * @param servletContext Servlet Context passed to newly created factory. 245 * @param properties Map of name/property passed to newly created factory. 246 * Map can contain more properties than requested. 247 * @throws DefinitionsFactoryException An error occur during initialization. 248 */ 249 public void initFactory(ServletContext servletContext, Map properties) 250 throws DefinitionsFactoryException { 251 // do nothing 252 } 253 254 /** 255 * Return String representation. 256 * @return String representation. 257 */ 258 public String toString() { 259 return factory.toString(); 260 } 261 262 /** 263 * Inner class. 264 * Wrapper for ServletContext init parameters. 265 * Object of this class is an HashMap containing parameters and values 266 * defined in the servlet config file (web.xml). 267 */ 268 class ServletPropertiesMap extends HashMap { 269 /** 270 * Constructor. 271 */ 272 ServletPropertiesMap(ServletConfig config) { 273 // This implementation is very simple. 274 // It is possible to avoid creation of a new structure, but this would 275 // imply writing all of the Map interface. 276 Enumeration e = config.getInitParameterNames(); 277 while (e.hasMoreElements()) { 278 String key = (String) e.nextElement(); 279 put(key, config.getInitParameter(key)); 280 } 281 } 282 } // end inner class 283 }