    001    package org.apache.turbine;
    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.io.BufferedReader;
    023    import java.io.File;
    024    import java.io.FileInputStream;
    025    import java.io.FileNotFoundException;
    026    import java.io.FileReader;
    027    import java.io.IOException;
    028    import java.io.Reader;
    029    import java.io.UnsupportedEncodingException;
    030    import java.util.Iterator;
    031    import java.util.Properties;
    032
    033    import javax.servlet.ServletConfig;
    034    import javax.servlet.ServletContext;
    035    import javax.servlet.ServletException;
    036    import javax.servlet.http.HttpServlet;
    037    import javax.servlet.http.HttpServletRequest;
    038    import javax.servlet.http.HttpServletResponse;
    039
    040    import org.apache.commons.configuration.Configuration;
    041    import org.apache.commons.configuration.ConfigurationFactory;
    042    import org.apache.commons.configuration.PropertiesConfiguration;
    043    import org.apache.commons.lang.StringUtils;
    044    import org.apache.commons.lang.exception.ExceptionUtils;
    045    import org.apache.commons.logging.Log;
    046    import org.apache.commons.logging.LogFactory;
    047    import org.apache.log4j.PropertyConfigurator;
    048    import org.apache.turbine.modules.PageLoader;
    049    import org.apache.turbine.pipeline.Pipeline;
    050    import org.apache.turbine.pipeline.PipelineData;
    051    import org.apache.turbine.pipeline.TurbinePipeline;
    052    import org.apache.turbine.services.Initable;
    053    import org.apache.turbine.services.InitializationException;
    054    import org.apache.turbine.services.ServiceManager;
    055    import org.apache.turbine.services.TurbineServices;
    056    import org.apache.turbine.services.rundata.RunDataService;
    057    import org.apache.turbine.services.template.TemplateService;
    058    import org.apache.turbine.services.template.TurbineTemplate;
    059    import org.apache.turbine.util.RunData;
    060    import org.apache.turbine.util.ServerData;
    061    import org.apache.turbine.util.TurbineConfig;
    062    import org.apache.turbine.util.TurbineException;
    063    import org.apache.turbine.util.uri.URIConstants;
    064
    065    import com.thoughtworks.xstream.XStream;
    066    import com.thoughtworks.xstream.io.xml.DomDriver;
    067
    068    /**
    069     * Turbine is the main servlet for the entire system. It is <code>final</code>
    070     * because you should <i>not</i> ever need to subclass this servlet.  If you
    071     * need to perform initialization of a service, then you should implement the
    072     * Services API and let your code be initialized by it.
    073     * If you need to override something in the <code>doGet()</code> or
    074     * <code>doPost()</code> methods, edit the TurbineResources.properties file and
    075     * specify your own classes there.
    076     * <p>
    077     * Turbine servlet recognizes the following initialization parameters.
    078     * <ul>
    079     * <li><code>properties</code> the path to TurbineResources.properties file
    080     * used by the default implementation of <code>ResourceService</code>, relative
    081     * to the application root.</li>
    082     * <li><code>basedir</code> this parameter is used <strong>only</strong> if your
    083     * application server does not support web applications, or the or does not
    084     * support <code>ServletContext.getRealPath(String)</code> method correctly.
    085     * You can use this parameter to specify the directory within the server's
    086     * filesystem, that is the base of your web application.</li>
    087     * </ul>
    088     *
    089     * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
    090     * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
    091     * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
    092     * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
    093     * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
    094     * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
    095     * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
    096     * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
    097     * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
    098     * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    099     * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
    100     * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    101     * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
    102     * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
    103     * @version $Id: Turbine.java 1096586 2011-04-25 20:31:43Z ludwig $
    104     */
    105    public class Turbine
    106            extends HttpServlet
    107    {
    108        /** Serialversion */
    109        private static final long serialVersionUID = -6317118078613623990L;
    110
    111        /**
    112         * Name of path info parameter used to indicate the redirected stage of
    113         * a given user's initial Turbine request
    114         */
    115        public static final String REDIRECTED_PATHINFO_NAME = "redirected";
    116
    117        /** The base directory key */
    118        public static final String BASEDIR_KEY = "basedir";
    119
    120        /**
    121         * In certain situations the init() method is called more than once,
    122         * somtimes even concurrently. This causes bad things to happen,
    123         * so we use this flag to prevent it.
    124         */
    125        private static boolean firstInit = true;
    126
    127            /**
    128             * The pipeline to use when processing requests.
    129             */
    130            private static Pipeline pipeline = null;
    131
    132        /** Whether init succeeded or not. */
    133        private static Throwable initFailure = null;
    134
    135        /**
    136         * Should initialization activities be performed during doGet() execution?
    137         */
    138        private static boolean firstDoGet = true;
    139
    140        /**
    141         * Keep all the properties of the web server in a convenient data
    142         * structure
    143         */
    144        private static ServerData serverData = null;
    145
    146        /** The base from which the Turbine application will operate. */
    147        private static String applicationRoot;
    148
    149        /** Servlet config for this Turbine webapp. */
    150        private static ServletConfig servletConfig;
    151
    152        /** Servlet context for this Turbine webapp. */
    153        private static ServletContext servletContext;
    154
    155        /**
    156         * The webapp root where the Turbine application
    157         * is running in the servlet container.
    158         * This might differ from the application root.
    159         */
    160        private static String webappRoot;
    161
    162        /** Our internal configuration object */
    163        private static Configuration configuration = null;
    164
    165        /** Default Input encoding if the servlet container does not report an encoding */
    166        private String inputEncoding = null;
    167
    168        /** Logging class from commons.logging */
    169        private static Log log = LogFactory.getLog(Turbine.class);
    170
    171        /**
    172         * This init method will load the default resources from a
    173         * properties file.
    174         *
    175         * This method is called by init(ServletConfig config)
    176         *
    177         * @exception ServletException a servlet exception.
    178         */
    179        public final void init() throws ServletException
    180        {
    181            synchronized (this.getClass())
    182            {
    183                super.init();
    184                ServletConfig config = getServletConfig();
    185
    186                if (!firstInit)
    187                {
    188                    log.info("Double initialization of Turbine was attempted!");
    189                    return;
    190                }
    191                // executing init will trigger some static initializers, so we have
    192                // only one chance.
    193                firstInit = false;
    194
    195                try
    196                {
    197                    ServletContext context = config.getServletContext();
    198
    199                    configure(config, context);
    200
    201                    TemplateService templateService = TurbineTemplate.getService();
    202                    if (templateService == null)
    203                    {
    204                        throw new TurbineException(
    205                                "No Template Service configured!");
    206                    }
    207
    208                    if (getRunDataService() == null)
    209                    {
    210                        throw new TurbineException(
    211                                "No RunData Service configured!");
    212                    }
    213
    214                }
    215                catch (Exception e)
    216                {
    217                    // save the exception to complain loudly later :-)
    218                    initFailure = e;
    219                    log.fatal("Turbine: init() failed: ", e);
    220                    throw new ServletException("Turbine: init() failed", e);
    221                }
    222
    223                log.info("Turbine: init() Ready to Rumble!");
    224            }
    225        }
    226
    227        /**
    228         * Read the master configuration file in, configure logging
    229         * and start up any early services.
    230         *
    231         * @param config The Servlet Configuration supplied by the container
    232         * @param context The Servlet Context supplied by the container
    233         *
    234         * @throws Exception A problem occured while reading the configuration or performing early startup
    235         */
    236
    237        private void configure(ServletConfig config, ServletContext context)
    238                throws Exception
    239        {
    240
    241            // Set the application root. This defaults to the webapp
    242            // context if not otherwise set. This is to allow 2.1 apps
    243            // to be developed from CVS. This feature will carry over
    244            // into 3.0.
    245            applicationRoot = findInitParameter(context, config,
    246                    TurbineConstants.APPLICATION_ROOT_KEY,
    247                    TurbineConstants.APPLICATION_ROOT_DEFAULT);
    248
    249            webappRoot = config.getServletContext().getRealPath("/");
    250            // log.info("Web Application root is " + webappRoot);
    251            // log.info("Application root is "     + applicationRoot);
    252
    253            if (applicationRoot == null || applicationRoot.equals(TurbineConstants.WEB_CONTEXT))
    254            {
    255                applicationRoot = webappRoot;
    256                // log.info("got empty or 'webContext' Application root. Application root now: " + applicationRoot);
    257            }
    258
    259            // Set the applicationRoot for this webapp.
    260            setApplicationRoot(applicationRoot);
    261
    262            // Create any directories that need to be setup for
    263            // a running Turbine application.
    264            createRuntimeDirectories(context, config);
    265
    266            //
    267            // Now we run the Turbine configuration code. There are two ways
    268            // to configure Turbine:
    269            //
    270            // a) By supplying an web.xml init parameter called "configuration"
    271            //
    272            // <init-param>
    273            //   <param-name>configuration</param-name>
    274            //   <param-value>/WEB-INF/conf/turbine.xml</param-value>
    275            // </init-param>
    276            //
    277            // This loads an XML based configuration file.
    278            //
    279            // b) By supplying an web.xml init parameter called "properties"
    280            //
    281            // <init-param>
    282            //   <param-name>properties</param-name>
    283            //   <param-value>/WEB-INF/conf/TurbineResources.properties</param-value>
    284            // </init-param>
    285            //
    286            // This loads a Properties based configuration file. Actually, these are
    287            // extended properties as provided by commons-configuration
    288            //
    289            // If neither a) nor b) is supplied, Turbine will fall back to the
    290            // known behaviour of loading a properties file called
    291            // /WEB-INF/conf/TurbineResources.properties relative to the
    292            // web application root.
    293
    294            String confFile= findInitParameter(context, config,
    295                    TurbineConfig.CONFIGURATION_PATH_KEY,
    296                    null);
    297
    298            String confPath;
    299            String confStyle = "unset";
    300
    301            if (StringUtils.isNotEmpty(confFile))
    302            {
    303                confPath = getRealPath(confFile);
    304                ConfigurationFactory configurationFactory = new ConfigurationFactory(confPath);
    305                configurationFactory.setBasePath(getApplicationRoot());
    306                configuration = configurationFactory.getConfiguration();
    307                confStyle = "XML";
    308            }
    309            else
    310            {
    311                confFile = findInitParameter(context, config,
    312                        TurbineConfig.PROPERTIES_PATH_KEY,
    313                        TurbineConfig.PROPERTIES_PATH_DEFAULT);
    314
    315                confPath = getRealPath(confFile);
    316
    317                // This should eventually be a Configuration
    318                // interface so that service and app configuration
    319                // can be stored anywhere.
    320                configuration = new PropertiesConfiguration(confPath);
    321                confStyle = "Properties";
    322            }
    323
    324
    325            //
    326            // Set up logging as soon as possible
    327            //
    328            String log4jFile = configuration.getString(TurbineConstants.LOG4J_CONFIG_FILE,
    329                                                       TurbineConstants.LOG4J_CONFIG_FILE_DEFAULT);
    330
    331            if (StringUtils.isNotEmpty(log4jFile) &&
    332                    !log4jFile.equalsIgnoreCase("none"))
    333            {
    334                log4jFile = getRealPath(log4jFile);
    335
    336                //
    337                // Load the config file above into a Properties object and
    338                // fix up the Application root
    339                //
    340                Properties p = new Properties();
    341                try
    342                {
    343                    p.load(new FileInputStream(log4jFile));
    344                    p.setProperty(TurbineConstants.APPLICATION_ROOT_KEY, getApplicationRoot());
    345                    PropertyConfigurator.configure(p);
    346
    347                    //
    348                    // Rebuild our log object with a configured commons-logging
    349                    log = LogFactory.getLog(this.getClass());
    350
    351                    log.info("Configured log4j from " + log4jFile);
    352                }
    353                catch (FileNotFoundException fnf)
    354                {
    355                    System.err.println("Could not open Log4J configuration file "
    356                                       + log4jFile + ": ");
    357                    fnf.printStackTrace();
    358                }
    359            }
    360
    361            // Now report our successful configuration to the world
    362            log.info("Loaded configuration  (" + confStyle + ") from " + confFile + " (" + confPath + ")");
    363
    364            setTurbineServletConfig(config);
    365            setTurbineServletContext(context);
    366
    367            getServiceManager().setApplicationRoot(applicationRoot);
    368
    369            // We want to set a few values in the configuration so
    370            // that ${variable} interpolation will work for
    371            //
    372            // ${applicationRoot}
    373            // ${webappRoot}
    374            configuration.setProperty(TurbineConstants.APPLICATION_ROOT_KEY, applicationRoot);
    375            configuration.setProperty(TurbineConstants.WEBAPP_ROOT_KEY, webappRoot);
    376
    377            // Get the default input encoding
    378            inputEncoding = configuration.getString(
    379                    TurbineConstants.PARAMETER_ENCODING_KEY,
    380                    TurbineConstants.PARAMETER_ENCODING_DEFAULT);
    381
    382            if (log.isDebugEnabled())
    383            {
    384                log.debug("Input Encoding has been set to " + inputEncoding);
    385            }
    386
    387            getServiceManager().setConfiguration(configuration);
    388
    389            // Initialize the service manager. Services
    390            // that have its 'earlyInit' property set to
    391            // a value of 'true' will be started when
    392            // the service manager is initialized.
    393            getServiceManager().init();
    394
    395            // Retrieve the pipeline class and then initialize it.  The pipeline
    396            // handles the processing of a webrequest/response cycle.
    397
    398                String descriptorPath =
    399                            configuration.getString(
    400                              "pipeline.default.descriptor",
    401                                              TurbinePipeline.CLASSIC_PIPELINE);
    402
    403            descriptorPath = getRealPath(descriptorPath);
    404
    405                    log.debug("Using descriptor path: " + descriptorPath);
    406            Reader reader = new BufferedReader(new FileReader(descriptorPath));
    407            XStream pipelineMapper = new XStream(new DomDriver()); // does not require XPP3 library
    408            pipeline = (Pipeline) pipelineMapper.fromXML(reader);
    409
    410                    log.debug("Initializing pipeline");
    411
    412                    pipeline.initialize();
    413        }
    414
    415        /**
    416         * Create any directories that might be needed during
    417         * runtime. Right now this includes:
    418         *
    419         * <ul>
    420         *
    421         * <li>The directory to write the log files to (relative to the
    422         * web application root), or <code>null</code> for the default of
    423         * <code>/logs</code>.  The directory is specified via the {@link
    424         * TurbineConstants#LOGGING_ROOT} parameter.</li>
    425         *
    426         * </ul>
    427         *
    428         * @param context Global initialization parameters.
    429         * @param config Initialization parameters specific to the Turbine
    430         * servlet.
    431         */
    432        private static void createRuntimeDirectories(ServletContext context,
    433                                                     ServletConfig config)
    434        {
    435            String path = findInitParameter(context, config,
    436                                            TurbineConstants.LOGGING_ROOT_KEY,
    437                                            TurbineConstants.LOGGING_ROOT_DEFAULT);
    438
    439            File logDir = new File(getRealPath(path));
    440            if (!logDir.exists())
    441            {
    442                // Create the logging directory
    443                if (!logDir.mkdirs())
    444                {
    445                    System.err.println("Cannot create directory for logs!");
    446                }
    447            }
    448        }
    449
    450        /**
    451         * Finds the specified servlet configuration/initialization
    452         * parameter, looking first for a servlet-specific parameter, then
    453         * for a global parameter, and using the provided default if not
    454         * found.
    455         */
    456        protected static final String findInitParameter(ServletContext context,
    457                ServletConfig config, String name, String defaultValue)
    458        {
    459            String path = null;
    460
    461            // Try the name as provided first.
    462            boolean usingNamespace = name.startsWith(TurbineConstants.CONFIG_NAMESPACE);
    463            while (true)
    464            {
    465                path = config.getInitParameter(name);
    466                if (StringUtils.isEmpty(path))
    467                {
    468                    path = context.getInitParameter(name);
    469                    if (StringUtils.isEmpty(path))
    470                    {
    471                        // The named parameter didn't yield a value.
    472                        if (usingNamespace)
    473                        {
    474                            path = defaultValue;
    475                        }
    476                        else
    477                        {
    478                            // Try again using Turbine's namespace.
    479                            name = TurbineConstants.CONFIG_NAMESPACE + '.' + name;
    480                            usingNamespace = true;
    481                            continue;
    482                        }
    483                    }
    484                }
    485                break;
    486            }
    487
    488            return path;
    489        }
    490
    491        /**
    492         * Initializes the services which need <code>PipelineData</code> to
    493         * initialize themselves (post startup).
    494         *
    495         * @param data The first <code>GET</code> request.
    496         */
    497        public final void init(PipelineData data)
    498        {
    499            synchronized (Turbine.class)
    500            {
    501                if (firstDoGet)
    502                {
    503                    // All we want to do here is save some servlet
    504                    // information so that services and processes
    505                    // that don't have direct access to a RunData
    506                    // object can still know something about
    507                    // the servlet environment.
    508                    saveServletInfo(data);
    509
    510                    // Initialize services with the PipelineData instance
    511                    TurbineServices services = (TurbineServices)TurbineServices.getInstance();
    512
    513                    for (Iterator i = services.getServiceNames(); i.hasNext();)
    514                    {
    515                            String serviceName = (String)i.next();
    516                            Object service = services.getService(serviceName);
    517
    518                            if (service instanceof Initable)
    519                            {
    520                                    try
    521                                    {
    522                                                            ((Initable)service).init(data);
    523                                                    }
    524                                    catch (InitializationException e)
    525                                    {
    526                                            log.warn("Could not initialize Initable " + serviceName + " with PipelineData", e);
    527                                                    }
    528                            }
    529                    }
    530
    531                    // Mark that we're done.
    532                    firstDoGet = false;
    533                    log.info("Turbine: first Request successful");
    534                }
    535            }
    536        }
    537
    538        /**
    539         * Return the current configuration with all keys included
    540         *
    541         * @return a Configuration Object
    542         */
    543        public static Configuration getConfiguration()
    544        {
    545            return configuration;
    546        }
    547
    548        /**
    549         * Return the server name.
    550         *
    551         * @return String server name
    552         */
    553        public static String getServerName()
    554        {
    555            return getDefaultServerData().getServerName();
    556        }
    557
    558        /**
    559         * Return the server scheme.
    560         *
    561         * @return String server scheme
    562         */
    563        public static String getServerScheme()
    564        {
    565            return getDefaultServerData().getServerScheme();
    566        }
    567
    568        /**
    569         * Return the server port.
    570         *
    571         * @return String server port
    572         */
    573        public static String getServerPort()
    574        {
    575            return Integer.toString(getDefaultServerData().getServerPort());
    576        }
    577
    578        /**
    579         * Get the script name. This is the initial script name.
    580         * Actually this is probably not needed any more. I'll
    581         * check. jvz.
    582         *
    583         * @return String initial script name.
    584         */
    585        public static String getScriptName()
    586        {
    587            return getDefaultServerData().getScriptName();
    588        }
    589
    590        /**
    591         * Return the context path.
    592         *
    593         * @return String context path
    594         */
    595        public static String getContextPath()
    596        {
    597            return getDefaultServerData().getContextPath();
    598        }
    599
    600        /**
    601         * Return all the Turbine Servlet information (Server Name, Port,
    602         * Scheme in a ServerData structure. This is generated from the
    603         * values set when initializing the Turbine and may not be correct
    604         * if you're running in a clustered structure. You can provide default
    605         * values in your configuration for cases where access is requied before
    606         * your application is first accessed by a user.  This might be used
    607         * if you need a DataURI and have no RunData object handy.
    608         *
    609         * @return An initialized ServerData object
    610         */
    611        public static ServerData getDefaultServerData()
    612        {
    613            if (serverData == null)
    614            {
    615                String serverName
    616                        = configuration.getString(TurbineConstants.DEFAULT_SERVER_NAME_KEY);
    617                if (serverName == null)
    618                {
    619                    log.error("ServerData Information requested from Turbine before first request!");
    620                }
    621                else
    622                {
    623                    log.info("ServerData Information retrieved from configuration.");
    624                }
    625                // Will be overwritten once the first request is run;
    626                serverData = new ServerData(serverName,
    627                        configuration.getInt(TurbineConstants.DEFAULT_SERVER_PORT_KEY,
    628                                URIConstants.HTTP_PORT),
    629                        configuration.getString(TurbineConstants.DEFAULT_SERVER_SCHEME_KEY,
    630                                URIConstants.HTTP),
    631                        configuration.getString(TurbineConstants.DEFAULT_SCRIPT_NAME_KEY),
    632                        configuration.getString(TurbineConstants.DEFAULT_CONTEXT_PATH_KEY));
    633            }
    634            return serverData;
    635        }
    636
    637        /**
    638         * Set the servlet config for this turbine webapp.
    639         *
    640         * @param config New servlet config
    641         */
    642        public static void setTurbineServletConfig(ServletConfig config)
    643        {
    644            servletConfig = config;
    645        }
    646
    647        /**
    648         * Get the servlet config for this turbine webapp.
    649         *
    650         * @return ServletConfig
    651         */
    652        public static ServletConfig getTurbineServletConfig()
    653        {
    654            return servletConfig;
    655        }
    656
    657        /**
    658         * Set the servlet context for this turbine webapp.
    659         *
    660         * @param context New servlet context.
    661         */
    662        public static void setTurbineServletContext(ServletContext context)
    663        {
    664            servletContext = context;
    665        }
    666
    667        /**
    668         * Get the servlet context for this turbine webapp.
    669         *
    670         * @return ServletContext
    671         */
    672        public static ServletContext getTurbineServletContext()
    673        {
    674            return servletContext;
    675        }
    676
    677        /**
    678         * The <code>Servlet</code> destroy method.  Invokes
    679         * <code>ServiceBroker</code> tear down method.
    680         */
    681        public final void destroy()
    682        {
    683            // Shut down all Turbine Services.
    684            getServiceManager().shutdownServices();
    685            System.gc();
    686
    687            firstInit = true;
    688            firstDoGet = true;
    689            log.info("Turbine: Done shutting down!");
    690        }
    691
    692        /**
    693         * The primary method invoked when the Turbine servlet is executed.
    694         *
    695         * @param req Servlet request.
    696         * @param res Servlet response.
    697         * @exception IOException a servlet exception.
    698         * @exception ServletException a servlet exception.
    699         */
    700        public final void doGet(HttpServletRequest req, HttpServletResponse res)
    701                throws IOException, ServletException
    702        {
    703            PipelineData pipelineData = null;
    704
    705            try
    706            {
    707                // Check to make sure that we started up properly.
    708                if (initFailure != null)
    709                {
    710                    throw initFailure;
    711                }
    712
    713                //
    714                // If the servlet container gives us no clear indication about the
    715                // Encoding of the contents, set it to our default value.
    716                if (req.getCharacterEncoding() == null)
    717                {
    718                    if (log.isDebugEnabled())
    719                    {
    720                        log.debug("Changing Input Encoding to " + inputEncoding);
    721                    }
    722
    723                    try
    724                    {
    725                        req.setCharacterEncoding(inputEncoding);
    726                    }
    727                    catch (UnsupportedEncodingException uee)
    728                    {
    729                        log.warn("Could not change request encoding to " + inputEncoding, uee);
    730                    }
    731                }
    732
    733                // Get general RunData here...
    734                // Perform turbine specific initialization below.
    735                pipelineData = getRunDataService().getRunData(req, res, getServletConfig());
    736               // Map runDataMap = new HashMap();
    737                //runDataMap.put(RunData.class, data);
    738                // put the data into the pipeline
    739               // pipelineData.put(RunData.class, runDataMap);
    740
    741                // If this is the first invocation, perform some
    742                // initialization.  Certain services need RunData to initialize
    743                // themselves.
    744                if (firstDoGet)
    745                {
    746                    init(pipelineData);
    747                }
    748
    749                // Stages of Pipeline implementation execution
    750                            // configurable via attached Valve implementations in a
    751                            // XML properties file.
    752                            pipeline.invoke(pipelineData);
    753
    754            }
    755            catch (Exception e)
    756            {
    757                handleException(pipelineData, res, e);
    758            }
    759            catch (Throwable t)
    760            {
    761                handleException(pipelineData, res, t);
    762            }
    763            finally
    764            {
    765                // Return the used RunData to the factory for recycling.
    766                getRunDataService().putRunData((RunData)pipelineData);
    767            }
    768        }
    769
    770        /**
    771         * In this application doGet and doPost are the same thing.
    772         *
    773         * @param req Servlet request.
    774         * @param res Servlet response.
    775         * @exception IOException a servlet exception.
    776         * @exception ServletException a servlet exception.
    777         */
    778        public final void doPost(HttpServletRequest req, HttpServletResponse res)
    779                throws IOException, ServletException
    780        {
    781            doGet(req, res);
    782        }
    783
    784        /**
    785         * Return the servlet info.
    786         *
    787         * @return a string with the servlet information.
    788         */
    789        public final String getServletInfo()
    790        {
    791            return "Turbine Servlet";
    792        }
    793
    794        /**
    795         * This method is about making sure that we catch and display
    796         * errors to the screen in one fashion or another. What happens is
    797         * that it will attempt to show the error using your user defined
    798         * Error Screen. If that fails, then it will resort to just
    799         * displaying the error and logging it all over the place
    800         * including the servlet engine log file, the Turbine log file and
    801         * on the screen.
    802         *
    803         * @param data A Turbine PipelineData object.
    804         * @param res Servlet response.
    805         * @param t The exception to report.
    806         */
    807        private final void handleException(PipelineData pipelineData, HttpServletResponse res,
    808                                           Throwable t)
    809        {
    810            RunData data = getRunData(pipelineData);
    811            // make sure that the stack trace makes it the log
    812            log.error("Turbine.handleException: ", t);
    813
    814            String mimeType = "text/plain";
    815            try
    816            {
    817                // This is where we capture all exceptions and show the
    818                // Error Screen.
    819                data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
    820
    821                // setup the screen
    822                data.setScreen(configuration.getString(
    823                        TurbineConstants.SCREEN_ERROR_KEY,
    824                        TurbineConstants.SCREEN_ERROR_DEFAULT));
    825
    826                // do more screen setup for template execution if needed
    827                if (data.getTemplateInfo() != null)
    828                {
    829                    data.getTemplateInfo()
    830                        .setScreenTemplate(configuration.getString(
    831                                TurbineConstants.TEMPLATE_ERROR_KEY,
    832                                TurbineConstants.TEMPLATE_ERROR_VM));
    833                }
    834
    835                // Make sure to not execute an action.
    836                data.setAction("");
    837
    838                PageLoader.getInstance().exec(pipelineData,
    839                        configuration.getString(TurbineConstants.PAGE_DEFAULT_KEY,
    840                                TurbineConstants.PAGE_DEFAULT_DEFAULT));
    841
    842                data.getResponse().setContentType(data.getContentType());
    843                data.getResponse().setStatus(data.getStatusCode());
    844            }
    845            // Catch this one because it occurs if some code hasn't been
    846            // completely re-compiled after a change..
    847            catch (java.lang.NoSuchFieldError e)
    848            {
    849                try
    850                {
    851                    data.getResponse().setContentType(mimeType);
    852                    data.getResponse().setStatus(200);
    853                }
    854                catch (Exception ignored)
    855                {
    856                    // ignore
    857                }
    858
    859                try
    860                {
    861                                    data.getResponse().getWriter().print("java.lang.NoSuchFieldError: "
    862                            + "Please recompile all of your source code.");
    863                }
    864                catch (IOException ignored)
    865                {
    866                    // ignore
    867                }
    868
    869                log.error(data.getStackTrace(), e);
    870            }
    871            // Attempt to do *something* at this point...
    872            catch (Throwable reallyScrewedNow)
    873            {
    874                StringBuffer msg = new StringBuffer();
    875                msg.append("Horrible Exception: ");
    876                if (data != null)
    877                {
    878                    msg.append(data.getStackTrace());
    879                }
    880                else
    881                {
    882                    msg.append(t);
    883                }
    884                try
    885                {
    886                    res.setContentType(mimeType);
    887                    res.setStatus(200);
    888                    res.getWriter().print(msg.toString());
    889                }
    890                catch (Exception ignored)
    891                {
    892                    // ignore
    893                }
    894
    895                log.error(reallyScrewedNow.getMessage(), reallyScrewedNow);
    896            }
    897        }
    898
    899        /**
    900         * Save some information about this servlet so that
    901         * it can be utilized by object instances that do not
    902         * have direct access to RunData.
    903         *
    904         * @param data Turbine request data
    905         */
    906        public static synchronized void saveServletInfo(PipelineData data)
    907        {
    908            // Store the context path for tools like ContentURI and
    909            // the UIManager that use webapp context path information
    910            // for constructing URLs.
    911
    912            //
    913            // Bundle all the information above up into a convenient structure
    914            //
    915            ServerData requestServerData = (ServerData) data.get(Turbine.class, ServerData.class);
    916            serverData = (ServerData) requestServerData.clone();
    917        }
    918
    919        /**
    920         * Set the application root for the webapp.
    921         *
    922         * @param val New app root.
    923         */
    924        public static void setApplicationRoot(String val)
    925        {
    926            applicationRoot = val;
    927        }
    928
    929        /**
    930         * Get the application root for this Turbine webapp. This
    931         * concept was started in 3.0 and will allow an app to be
    932         * developed from a standard CVS layout. With a simple
    933         * switch the app will work fully within the servlet
    934         * container for deployment.
    935         *
    936         * @return String applicationRoot
    937         */
    938        public static String getApplicationRoot()
    939        {
    940            return applicationRoot;
    941        }
    942
    943        /**
    944         * Used to get the real path of configuration and resource
    945         * information. This can be used by an app being
    946         * developed in a standard CVS layout.
    947         *
    948         * @param path path translated to the application root
    949         * @return the real path
    950         */
    951        public static String getRealPath(String path)
    952        {
    953            if (path.startsWith("/"))
    954            {
    955                path = path.substring(1);
    956            }
    957
    958            return new File(getApplicationRoot(), path).getAbsolutePath();
    959        }
    960
    961        /**
    962         * Return an instance of the currently configured Service Manager
    963         *
    964         * @return A service Manager instance
    965         */
    966        private ServiceManager getServiceManager()
    967        {
    968            return TurbineServices.getInstance();
    969        }
    970
    971        /**
    972         * Get a RunData from the pipelineData. Once RunData is fully replaced
    973         * by PipelineData this should not be required.
    974         * @param pipelineData
    975         * @return
    976         */
    977        private RunData getRunData(PipelineData pipelineData)
    978        {
    979            RunData data = null;
    980            data = (RunData)pipelineData;
    981            return data;
    982        }
    983
    984
    985        /**
    986         * Returns the default input encoding for the servlet.
    987         *
    988         * @return the default input encoding.
    989         */
    990        public String getDefaultInputEncoding() {
    991            return inputEncoding;
    992        }
    993
    994        /**
    995         * Static Helper method for looking up the RunDataService
    996         * @return A RunDataService
    997         */
    998        private static RunDataService getRunDataService()
    999        {
    1000            return (RunDataService) TurbineServices
    1001            .getInstance().getService(RunDataService.SERVICE_NAME);
    1002        }
    1003    }