    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    }