001 package org.apache.turbine.services.rundata;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.util.HashMap;
023 import java.util.Iterator;
024 import java.util.Locale;
025 import java.util.Map;
026
027 import javax.servlet.ServletConfig;
028 import javax.servlet.http.HttpServletRequest;
029 import javax.servlet.http.HttpServletResponse;
030
031 import org.apache.commons.configuration.Configuration;
032 import org.apache.fulcrum.parser.CookieParser;
033 import org.apache.fulcrum.parser.DefaultCookieParser;
034 import org.apache.fulcrum.parser.DefaultParameterParser;
035 import org.apache.fulcrum.parser.ParameterParser;
036 import org.apache.fulcrum.parser.ParserService;
037 import org.apache.fulcrum.pool.PoolException;
038 import org.apache.fulcrum.pool.PoolService;
039 import org.apache.turbine.Turbine;
040 import org.apache.turbine.services.InitializationException;
041 import org.apache.turbine.services.TurbineBaseService;
042 import org.apache.turbine.services.TurbineServices;
043 import org.apache.turbine.util.RunData;
044 import org.apache.turbine.util.ServerData;
045 import org.apache.turbine.util.TurbineException;
046
047 /**
048 * The RunData Service provides the implementations for RunData and
049 * related interfaces required by request processing. It supports
050 * different configurations of implementations, which can be selected
051 * by specifying a configuration key. It may use pooling, in which case
052 * the implementations should implement the Recyclable interface.
053 *
054 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
055 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
056 * @version $Id: TurbineRunDataService.java 1078552 2011-03-06 19:58:46Z tv $
057 */
058 public class TurbineRunDataService
059 extends TurbineBaseService
060 implements RunDataService
061 {
062
063 /** The default implementation of the RunData object*/
064 private static final String DEFAULT_RUN_DATA =
065 DefaultTurbineRunData.class.getName();
066
067 /** The default implementation of the Parameter Parser object */
068 private static final String DEFAULT_PARAMETER_PARSER =
069 DefaultParameterParser.class.getName();
070
071 /** The default implementation of the Cookie parser object */
072 private static final String DEFAULT_COOKIE_PARSER =
073 DefaultCookieParser.class.getName();
074
075 /** The map of configurations. */
076 private final Map<String, Object> configurations = new HashMap<String, Object>();
077
078 /** Private reference to the pool service for object recycling */
079 private PoolService pool = null;
080
081 /** Private reference to the parser service for parser recycling */
082 private ParserService parserService = null;
083
084 /**
085 * Constructs a RunData Service.
086 */
087 public TurbineRunDataService()
088 {
089 super();
090 }
091
092 /**
093 * Initializes the service by setting the pool capacity.
094 *
095 * @throws InitializationException if initialization fails.
096 */
097 @SuppressWarnings("unchecked")
098 @Override
099 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 }