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 }