View Javadoc

1   package org.apache.turbine.services.rundata;
2   
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  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.Locale;
25  import java.util.Map;
26  
27  import javax.servlet.ServletConfig;
28  import javax.servlet.ServletContext;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  
32  import org.apache.commons.configuration.Configuration;
33  import org.apache.fulcrum.parser.CookieParser;
34  import org.apache.fulcrum.parser.DefaultCookieParser;
35  import org.apache.fulcrum.parser.DefaultParameterParser;
36  import org.apache.fulcrum.parser.ParameterParser;
37  import org.apache.fulcrum.parser.ParserService;
38  import org.apache.fulcrum.pool.PoolException;
39  import org.apache.fulcrum.pool.PoolService;
40  import org.apache.turbine.Turbine;
41  import org.apache.turbine.services.InitializationException;
42  import org.apache.turbine.services.TurbineBaseService;
43  import org.apache.turbine.services.TurbineServices;
44  import org.apache.turbine.util.RunData;
45  import org.apache.turbine.util.ServerData;
46  import org.apache.turbine.util.TurbineException;
47  
48  /**
49   * The RunData Service provides the implementations for RunData and
50   * related interfaces required by request processing. It supports
51   * different configurations of implementations, which can be selected
52   * by specifying a configuration key. It may use pooling, in which case
53   * the implementations should implement the Recyclable interface.
54   *
55   * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
56   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
57   * @version $Id: TurbineRunDataService.java 1706239 2015-10-01 13:18:35Z tv $
58   */
59  public class TurbineRunDataService
60      extends TurbineBaseService
61      implements RunDataService
62  {
63  
64      /** The default implementation of the RunData object*/
65      private static final String DEFAULT_RUN_DATA =
66          DefaultTurbineRunData.class.getName();
67  
68      /** The default implementation of the Parameter Parser object */
69      private static final String DEFAULT_PARAMETER_PARSER =
70          DefaultParameterParser.class.getName();
71  
72      /** The default implementation of the Cookie parser object */
73      private static final String DEFAULT_COOKIE_PARSER =
74          DefaultCookieParser.class.getName();
75  
76      /** The map of configurations. */
77      private final Map<String, Object> configurations = new HashMap<String, Object>();
78  
79      /** A class cache. */
80      private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>();
81  
82      /** Private reference to the pool service for object recycling */
83      private PoolService pool = null;
84  
85      /** Private reference to the parser service for parser recycling */
86      private ParserService parserService = null;
87  
88      /**
89       * Constructs a RunData Service.
90       */
91      public TurbineRunDataService()
92      {
93          super();
94      }
95  
96      /**
97       * Initializes the service by setting the pool capacity.
98       *
99       * @throws InitializationException if initialization fails.
100      */
101     @Override
102     public void init()
103             throws InitializationException
104     {
105         // Create a default configuration.
106         String[] def = new String[]
107         {
108             DEFAULT_RUN_DATA,
109             DEFAULT_PARAMETER_PARSER,
110             DEFAULT_COOKIE_PARSER
111         };
112         configurations.put(DEFAULT_CONFIG, def.clone());
113 
114         // Check other configurations.
115         Configuration conf = getConfiguration();
116         if (conf != null)
117         {
118             String key,value;
119             String[] config;
120             String[] plist = new String[]
121             {
122                 RUN_DATA_KEY,
123                 PARAMETER_PARSER_KEY,
124                 COOKIE_PARSER_KEY
125             };
126             for (Iterator<String> i = conf.getKeys(); i.hasNext();)
127             {
128                 key = i.next();
129                 value = conf.getString(key);
130                 for (int j = 0; j < plist.length; j++)
131                 {
132                     if (key.endsWith(plist[j]) &&
133                             (key.length() > (plist[j].length() + 1)))
134                     {
135                         key = key.substring(0, key.length() - plist[j].length() - 1);
136                         config = (String[]) configurations.get(key);
137                         if (config == null)
138                         {
139                             config = def.clone();
140                             configurations.put(key, config);
141                         }
142                         config[j] = value;
143                         break;
144                     }
145                 }
146             }
147         }
148 
149 		pool = (PoolService)TurbineServices.getInstance().getService(PoolService.ROLE);
150 
151         if (pool == null)
152         {
153             throw new InitializationException("RunData Service requires"
154                 + " configured Pool Service!");
155         }
156 
157         parserService = (ParserService)TurbineServices.getInstance().getService(ParserService.ROLE);
158 
159         if (parserService == null)
160         {
161             throw new InitializationException("RunData Service requires"
162                 + " configured Parser Service!");
163         }
164 
165         setInit(true);
166     }
167 
168     /**
169      * Shutdown the service
170      *
171      * @see org.apache.turbine.services.TurbineBaseService#shutdown()
172      */
173     @Override
174     public void shutdown()
175     {
176         classCache.clear();
177         super.shutdown();
178     }
179 
180     /**
181      * Gets a default RunData object.
182      *
183      * @param req a servlet request.
184      * @param res a servlet response.
185      * @param config a servlet config.
186      * @return a new or recycled RunData object.
187      * @throws TurbineException if the operation fails.
188      */
189     @Override
190     public RunData getRunData(HttpServletRequest req,
191                               HttpServletResponse res,
192                               ServletConfig config)
193             throws TurbineException
194     {
195         return getRunData(DEFAULT_CONFIG, req, res, config);
196     }
197 
198     /**
199      * Gets a RunData instance from a specific configuration.
200      *
201      * @param key a configuration key.
202      * @param req a servlet request.
203      * @param res a servlet response.
204      * @param config a servlet config.
205      * @return a new or recycled RunData object.
206      * @throws TurbineException if the operation fails.
207      * @throws IllegalArgumentException if any of the parameters are null.
208      * TODO The "key" parameter should be removed in favor of just looking up what class via the roleConfig avalon file.
209      */
210     @Override
211     public RunData getRunData(String key,
212                               HttpServletRequest req,
213                               HttpServletResponse res,
214                               ServletConfig config)
215             throws TurbineException,
216             IllegalArgumentException
217     {
218         // The RunData object caches all the information that is needed for
219         // the execution lifetime of a single request. A RunData object
220         // is created/recycled for each and every request and is passed
221         // to each and every module. Since each thread has its own RunData
222         // object, it is not necessary to perform synchronization for
223         // the data within this object.
224         if ((req == null)
225             || (res == null)
226             || (config == null))
227         {
228             throw new IllegalArgumentException("HttpServletRequest, "
229                 + "HttpServletResponse or ServletConfig was null.");
230         }
231 
232         // Get the specified configuration.
233         String[] cfg = (String[]) configurations.get(key);
234         if (cfg == null)
235         {
236             throw new TurbineException("RunTime configuration '" + key + "' is undefined");
237         }
238 
239         TurbineRunData data;
240         try
241         {
242     		Class<?> runDataClazz = classCache.get(cfg[0]);
243     		if (runDataClazz == null)
244     		{
245     		    runDataClazz = Class.forName(cfg[0]);
246     		    classCache.put(cfg[0], runDataClazz);
247     		}
248 
249             Class<?> parameterParserClazz = classCache.get(cfg[1]);
250             if (parameterParserClazz == null)
251             {
252                 parameterParserClazz = Class.forName(cfg[1]);
253                 classCache.put(cfg[1], parameterParserClazz);
254             }
255 
256             Class<?> cookieParserClazz = classCache.get(cfg[2]);
257             if (cookieParserClazz == null)
258             {
259                 cookieParserClazz = Class.forName(cfg[2]);
260                 classCache.put(cfg[2], cookieParserClazz);
261             }
262 
263             data = (TurbineRunData) pool.getInstance(runDataClazz);
264             @SuppressWarnings("unchecked") // ok
265             ParameterParser pp = parserService.getParser((Class<ParameterParser>)parameterParserClazz);
266             data.get(Turbine.class).put(ParameterParser.class, pp);
267 
268             @SuppressWarnings("unchecked") // ok
269             CookieParser cp = parserService.getParser((Class<CookieParser>)cookieParserClazz);
270             data.get(Turbine.class).put(CookieParser.class, cp);
271 
272             Locale locale = req.getLocale();
273 
274             if (locale == null)
275             {
276                 // get the default from the Turbine configuration
277                 locale = data.getLocale();
278             }
279 
280             // set the locale detected and propagate it to the parsers
281             data.setLocale(locale);
282         }
283         catch (PoolException pe)
284         {
285             throw new TurbineException("RunData configuration '" + key + "' is illegal caused a pool exception", pe);
286         }
287         catch (ClassNotFoundException x)
288         {
289             throw new TurbineException("RunData configuration '" + key + "' is illegal", x);
290         }
291         catch (ClassCastException x)
292         {
293             throw new TurbineException("RunData configuration '" + key + "' is illegal", x);
294         }
295         catch (InstantiationException e)
296         {
297             throw new TurbineException("RunData configuration '" + key + "' is illegal", e);
298         }
299 
300         // Set the request and response.
301         data.get(Turbine.class).put(HttpServletRequest.class, req);
302         data.get(Turbine.class).put(HttpServletResponse.class, res);
303 
304         // Set the servlet configuration.
305         data.get(Turbine.class).put(ServletConfig.class, config);
306         data.get(Turbine.class).put(ServletContext.class, config.getServletContext());
307 
308         // Set the ServerData.
309         data.get(Turbine.class).put(ServerData.class, new ServerData(req));
310 
311         return data;
312     }
313 
314     /**
315      * Puts the used RunData object back to the factory for recycling.
316      *
317      * @param data the used RunData object.
318      * @return true, if pooling is supported and the object was accepted.
319      */
320     @Override
321     public boolean putRunData(RunData data)
322     {
323         if (data instanceof TurbineRunData)
324         {
325             parserService.putParser(((TurbineRunData) data).getParameterParser());
326             parserService.putParser(((TurbineRunData) data).getCookieParser());
327 
328             return pool.putInstance(data);
329         }
330         else
331         {
332             return false;
333         }
334     }
335 }