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