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