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.configuration2.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 1854787 2019-03-04 18:30:25Z 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]) && key.length() > plist[j].length() + 1)
133                     {
134                         key = key.substring(0, key.length() - plist[j].length() - 1);
135                         config = (String[]) configurations.get(key);
136                         if (config == null)
137                         {
138                             config = def.clone();
139                             configurations.put(key, config);
140                         }
141                         config[j] = value;
142                         break;
143                     }
144                 }
145             }
146         }
147 
148 		pool = (PoolService)TurbineServices.getInstance().getService(PoolService.ROLE);
149 
150         if (pool == null)
151         {
152             throw new InitializationException("RunData Service requires"
153                 + " configured Pool Service!");
154         }
155 
156         parserService = (ParserService)TurbineServices.getInstance().getService(ParserService.ROLE);
157 
158         if (parserService == null)
159         {
160             throw new InitializationException("RunData Service requires"
161                 + " configured Parser Service!");
162         }
163 
164         setInit(true);
165     }
166 
167     /**
168      * Shutdown the service
169      *
170      * @see org.apache.turbine.services.TurbineBaseService#shutdown()
171      */
172     @Override
173     public void shutdown()
174     {
175         classCache.clear();
176         super.shutdown();
177     }
178 
179     /**
180      * Gets a default RunData object.
181      *
182      * @param req a servlet request.
183      * @param res a servlet response.
184      * @param config a servlet config.
185      * @return a new or recycled RunData object.
186      * @throws TurbineException if the operation fails.
187      */
188     @Override
189     public RunData getRunData(HttpServletRequest req,
190                               HttpServletResponse res,
191                               ServletConfig config)
192             throws TurbineException
193     {
194         return getRunData(DEFAULT_CONFIG, req, res, config);
195     }
196 
197     /**
198      * Gets a RunData instance from a specific configuration.
199      *
200      * @param key a configuration key.
201      * @param req a servlet request.
202      * @param res a servlet response.
203      * @param config a servlet config.
204      * @return a new or recycled RunData object.
205      * @throws TurbineException if the operation fails.
206      * @throws IllegalArgumentException if any of the parameters are null.
207      * TODO The "key" parameter should be removed in favor of just looking up what class via the roleConfig avalon file.
208      */
209     @Override
210     public RunData getRunData(String key,
211                               HttpServletRequest req,
212                               HttpServletResponse res,
213                               ServletConfig config)
214             throws TurbineException,
215             IllegalArgumentException
216     {
217         // The RunData object caches all the information that is needed for
218         // the execution lifetime of a single request. A RunData object
219         // is created/recycled for each and every request and is passed
220         // to each and every module. Since each thread has its own RunData
221         // object, it is not necessary to perform synchronization for
222         // the data within this object.
223         if (req == null || res == null || config == null)
224         {
225             throw new IllegalArgumentException("HttpServletRequest, "
226                 + "HttpServletResponse or ServletConfig was null.");
227         }
228 
229         // Get the specified configuration.
230         String[] cfg = (String[]) configurations.get(key);
231         if (cfg == null)
232         {
233             throw new TurbineException("RunTime configuration '" + key + "' is undefined");
234         }
235 
236         TurbineRunData data;
237         try
238         {
239     		Class<?> runDataClazz = classCache.get(cfg[0]);
240     		if (runDataClazz == null)
241     		{
242     		    runDataClazz = Class.forName(cfg[0]);
243     		    classCache.put(cfg[0], runDataClazz);
244     		}
245 
246             Class<?> parameterParserClazz = classCache.get(cfg[1]);
247             if (parameterParserClazz == null)
248             {
249                 parameterParserClazz = Class.forName(cfg[1]);
250                 classCache.put(cfg[1], parameterParserClazz);
251             }
252 
253             Class<?> cookieParserClazz = classCache.get(cfg[2]);
254             if (cookieParserClazz == null)
255             {
256                 cookieParserClazz = Class.forName(cfg[2]);
257                 classCache.put(cfg[2], cookieParserClazz);
258             }
259 
260             data = (TurbineRunData) pool.getInstance(runDataClazz);
261             @SuppressWarnings("unchecked") // ok
262             ParameterParser pp = parserService.getParser((Class<ParameterParser>)parameterParserClazz);
263             data.get(Turbine.class).put(ParameterParser.class, pp);
264 
265             @SuppressWarnings("unchecked") // ok
266             CookieParser cp = parserService.getParser((Class<CookieParser>)cookieParserClazz);
267             data.get(Turbine.class).put(CookieParser.class, cp);
268 
269             Locale locale = req.getLocale();
270 
271             if (locale == null)
272             {
273                 // get the default from the Turbine configuration
274                 locale = data.getLocale();
275             }
276 
277             // set the locale detected and propagate it to the parsers
278             data.setLocale(locale);
279         }
280         catch (PoolException pe)
281         {
282             throw new TurbineException("RunData configuration '" + key + "' is illegal caused a pool exception", pe);
283         }
284         catch (ClassNotFoundException | ClassCastException | InstantiationException x)
285         {
286             throw new TurbineException("RunData configuration '" + key + "' is illegal", x);
287         }
288 
289         // Set the request and response.
290         data.get(Turbine.class).put(HttpServletRequest.class, req);
291         data.get(Turbine.class).put(HttpServletResponse.class, res);
292 
293         // Set the servlet configuration.
294         data.get(Turbine.class).put(ServletConfig.class, config);
295         data.get(Turbine.class).put(ServletContext.class, config.getServletContext());
296 
297         // Set the ServerData.
298         data.get(Turbine.class).put(ServerData.class, new ServerData(req));
299 
300         return data;
301     }
302 
303     /**
304      * Puts the used RunData object back to the factory for recycling.
305      *
306      * @param data the used RunData object.
307      * @return true, if pooling is supported and the object was accepted.
308      */
309     @Override
310     public boolean putRunData(RunData data)
311     {
312         if (data instanceof TurbineRunData)
313         {
314             parserService.putParser(((TurbineRunData) data).getParameterParser());
315             parserService.putParser(((TurbineRunData) data).getCookieParser());
316 
317             return pool.putInstance(data);
318         }
319         else
320         {
321             return false;
322         }
323     }
324 }