001package 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 022import java.util.HashMap; 023import java.util.Iterator; 024import java.util.Locale; 025import java.util.Map; 026 027import javax.servlet.ServletConfig; 028import javax.servlet.ServletContext; 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpServletResponse; 031 032import org.apache.commons.configuration.Configuration; 033import org.apache.fulcrum.parser.CookieParser; 034import org.apache.fulcrum.parser.DefaultCookieParser; 035import org.apache.fulcrum.parser.DefaultParameterParser; 036import org.apache.fulcrum.parser.ParameterParser; 037import org.apache.fulcrum.parser.ParserService; 038import org.apache.fulcrum.pool.PoolException; 039import org.apache.fulcrum.pool.PoolService; 040import org.apache.turbine.Turbine; 041import org.apache.turbine.services.InitializationException; 042import org.apache.turbine.services.TurbineBaseService; 043import org.apache.turbine.services.TurbineServices; 044import org.apache.turbine.util.RunData; 045import org.apache.turbine.util.ServerData; 046import org.apache.turbine.util.TurbineException; 047 048/** 049 * The RunData Service provides the implementations for RunData and 050 * related interfaces required by request processing. It supports 051 * different configurations of implementations, which can be selected 052 * by specifying a configuration key. It may use pooling, in which case 053 * the implementations should implement the Recyclable interface. 054 * 055 * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a> 056 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> 057 * @version $Id: TurbineRunDataService.java 1706239 2015-10-01 13:18:35Z tv $ 058 */ 059public class TurbineRunDataService 060 extends TurbineBaseService 061 implements RunDataService 062{ 063 064 /** The default implementation of the RunData object*/ 065 private static final String DEFAULT_RUN_DATA = 066 DefaultTurbineRunData.class.getName(); 067 068 /** The default implementation of the Parameter Parser object */ 069 private static final String DEFAULT_PARAMETER_PARSER = 070 DefaultParameterParser.class.getName(); 071 072 /** The default implementation of the Cookie parser object */ 073 private static final String DEFAULT_COOKIE_PARSER = 074 DefaultCookieParser.class.getName(); 075 076 /** The map of configurations. */ 077 private final Map<String, Object> configurations = new HashMap<String, Object>(); 078 079 /** A class cache. */ 080 private final Map<String, Class<?>> classCache = new HashMap<String, Class<?>>(); 081 082 /** Private reference to the pool service for object recycling */ 083 private PoolService pool = null; 084 085 /** Private reference to the parser service for parser recycling */ 086 private ParserService parserService = null; 087 088 /** 089 * Constructs a RunData Service. 090 */ 091 public TurbineRunDataService() 092 { 093 super(); 094 } 095 096 /** 097 * Initializes the service by setting the pool capacity. 098 * 099 * @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}