Package org.apache.turbine

Source Code of org.apache.turbine.Turbine

package org.apache.turbine;

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*        Apache Software Foundation (http://www.apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
*    "Apache Turbine" must not be used to endorse or promote products
*    derived from this software without prior written permission. For
*    written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
*    "Apache Turbine", nor may "Apache" appear in their name, without
*    prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

// Java Core Classes
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

// Java Servlet Classes
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// Turbine Modules
import org.apache.turbine.modules.ActionLoader;
import org.apache.turbine.modules.PageLoader;
import org.apache.turbine.modules.actions.sessionvalidator.SessionValidator;

// Turbine Utility Classes
import org.apache.turbine.util.DynamicURI;
import org.apache.turbine.util.Log;
import org.apache.turbine.util.RunData;
import org.apache.turbine.util.RunDataFactory;
import org.apache.turbine.util.StringUtils;
import org.apache.turbine.util.security.AccessControlList;

//Turbine Services
import org.apache.turbine.services.TurbineServices;
import org.apache.turbine.services.resources.TurbineResources;
import org.apache.turbine.services.logging.LoggingService;
import org.apache.turbine.services.template.TurbineTemplate;

/**
* Turbine is the main servlet for the entire system. It is <code>final</code>
* because you should <i>not</i> ever need to subclass this servlet.  If you
* need to perform initialization of a service, then you should implement the
* Services API and let your code be initialized by it.
* If you need to override something in the <code>doGet()</code> or
* <code>doPost()</code> methods, edit the TurbineResources.properties file and
* specify your own classes there.
*
* <p> Turbine servlet recognizes the following initialization parameters.
*
* <ul>
* <li><code>resources</code> the implementation of
* {@link org.apache.turbine.services.resources.ResourceService} to be used</li>
* <li><code>properties</code> the path to TurbineResources.properties file
* used by the default implementation of <code>ResourceService</code>, relative
* to the application root.</li>
* <li><code>basedir</code> this parameter is used <strong>only</strong> if your
* application server does not support web applications, or the or does not
* support <code>ServletContext.getRealPath(String)</code> method correctly.
* You can use this parameter to specify the directory within the server's
* filesystem, that is the base of your web application.</li>
* </ul><br>
*
* @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
* @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
* @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
* @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
* @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
* @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
* @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
* @version $Id$
*/
public class Turbine extends HttpServlet
{
    /**
     * Name of path info parameter used to indicate the redirected stage of
     * a given user's initial Turbine request
     */
    public static final String REDIRECTED_PATHINFO_NAME = "redirected";

    /**
     * The base directory key
     */
    public static final String BASEDIR_KEY = "basedir";

    /**
     * In certain situations the init() method is called more than once,
     * somtimes even concurrently. This causes bad things to happen,
     * so we use this flag to prevent it.
     */
    private static boolean firstInit = true;

    /**
     * Whether init succeeded or not.
     */
    private static Throwable initFailure = null;

    /**
     * Should initialization activities be performed during doGet()
     * execution?
     */
    private static boolean firstDoGet = true;

    /**
     * This init method will load the default resources from a
     * properties file.
     *
     * @param config typical Servlet initialization parameter.
     * @exception ServletException a servlet exception.
     */
    public final void init(ServletConfig config)
        throws ServletException
    {
        super.init(config);

        synchronized ( this.getClass() )
        {
            if(!firstInit)
            {
                log ("Double initializaton of Turbine was attempted!");
                return;
            }
            // executing init will trigger some static initializers, so we have
            // only one chance.
            firstInit = false;

            try
            {
                // Initalize TurbineServices and init bootstrap services
                TurbineServices services =
                    (TurbineServices) TurbineServices.getInstance();

                // Initialize essential services (Resources & Logging)
                services.initPrimaryServices(config);

                // Initialize other services that require early init
                services.initServices(config, false);
            }
            catch ( Exception e )
            {
                // save the exception to complain loudly later :-)
                initFailure = e;
                log ("Turbine: init() failed: " + StringUtils.stackTrace(e));
                return;
            }
            log ("Turbine: init() Ready to Rumble!");
        }
    }

    /**
     * Initializes the services which need <code>RunData</code> to
     * initialize themselves (post startup).
     *
     * @param data The first <code>GET</code> request.
     */
    public final void init(RunData data)
    {
        if (firstDoGet)
        {
            synchronized (Turbine.class)
            {
                if (firstDoGet)
                {
                    log("Turbine: Starting HTTP initialization of services");
                    TurbineServices.getInstance().initServices(data);
                    log("Turbine: Completed HTTP initialization of services");

                    // Mark that we're done.
                    firstDoGet = false;
               }
            }
        }
    }

    /**
     * The <code>Servlet</code> destroy method.  Invokes
     * <code>ServiceBroker</code> tear down method.
     */
    public final void destroy()
    {
        // Shut down all Turbine Services.
        TurbineServices.getInstance().shutdownServices();
        System.gc();

        log("Turbine: Done shutting down!");
    }

    /**
     * The primary method invoked when the Turbine servlet is executed.
     *
     * @param req Servlet request.
     * @param res Servlet response.
     * @exception IOException a servlet exception.
     * @exception ServletException a servlet exception.
     */
    public final void doGet (HttpServletRequest req,
                       HttpServletResponse res)
        throws IOException,
               ServletException
    {
        // Placeholder for the RunData object.
        RunData data = null;
        try
        {
            // Check to make sure that we started up properly.
            if (initFailure != null)
            {
                throw initFailure;
            }

            // Get general RunData here...
            // Perform turbine specific initialization below.
            data = RunDataFactory.getRunData( req, res,
                                              getServletConfig() );

            // If this is the first invocation, perform some
            // initialization.  Certain services need RunData to initialize
            // themselves.
            init(data);

            // Get the instance of the Session Validator.
            SessionValidator sessionValidator = (SessionValidator)ActionLoader
                .getInstance().getInstance(TurbineResources.getString(
                    "action.sessionvalidator"));

            // if this is the redirected stage of the initial request,
            // check that the session is now not new.
            // If it is not, then redirect back to the
            // original URL (i.e. remove the "redirected" pathinfo)
            if (data.getParameters()
                .getString(REDIRECTED_PATHINFO_NAME, "false").equals("true"))
            {
                if (data.getSession().isNew())
                {
                    String message = "Infinite redirect detected...";
                    log(message);
                    Log.error(message);
                    throw new Exception(message);
                }
                else
                {
                    DynamicURI duri = new DynamicURI (data, true);

                    // Pass on the sent data in pathinfo.
                    for (Enumeration e = data.getParameters().keys() ;
                         e.hasMoreElements() ;)
                    {
                        String key = (String) e.nextElement();
                        if (!key.equals(REDIRECTED_PATHINFO_NAME))
                        {
                            String value =
                                (String) data.getParameters().getString ( key );
                            duri.addPathInfo((String)key, (String)value );
                        }
                    }

                    data.getResponse().sendRedirect( duri.toString() );
                    return;
                }
            }
            else
            {
                // Insist that the client starts a session before access
                // to data is allowed. this is done by redirecting them to
                // the "screen.homepage" page but you could have them go
                // to any page as a starter (ie: the homepage)
                // "data.getResponse()" represents the HTTP servlet
                // response.
                if ( sessionValidator.requiresNewSession(data) &&
                     data.getSession().isNew() )
                {
                    DynamicURI duri = new DynamicURI (data, true);

                    // Pass on the sent data in pathinfo.
                    for (Enumeration e = data.getParameters().keys() ;
                         e.hasMoreElements() ;)
                    {
                        String key = (String) e.nextElement();
                        String value =
                            (String) data.getParameters().getString ( key );
                        duri.addPathInfo((String)key, (String)value );
                    }

                    // add a dummy bit of path info to fool browser into
                    // thinking this is a new URL
                    if (!data.getParameters()
                        .containsKey(REDIRECTED_PATHINFO_NAME))
                    {
                        duri.addPathInfo(REDIRECTED_PATHINFO_NAME, "true");
                    }

                    // as the session is new take this opportunity to
                    // set the session timeout if specified in TR.properties
                    int timeout =
                        TurbineResources.getInt("session.timeout", -1);
                   
                    if (timeout != -1)
                    {
                        data.getSession().setMaxInactiveInterval(timeout);
                    }                       

                    data.getResponse().sendRedirect( duri.toString() );
                    return;
                }
            }

            // Fill in the screen and action variables.
            data.setScreen ( data.getParameters().getString("screen") );
            data.setAction ( data.getParameters().getString("action") );

            // Special case for login and logout, this must happen before the
            // session validator is executed in order either to allow a user to
            // even login, or to ensure that the session validator gets to
            // mandate its page selection policy for non-logged in users
            // after the logout has taken place.
            if ( data.hasAction()
                    && data.getAction().equalsIgnoreCase(TurbineResources
                            .getString("action.login"))
                    || data.getAction().equalsIgnoreCase(TurbineResources
                            .getString("action.logout")))
            {
                // If a User is logging in, we should refresh the
                // session here.  Invalidating session and starting a
                // new session would seem to be a good method, but I
                // (JDM) could not get this to work well (it always
                // required the user to login twice).  Maybe related
                // to JServ?  If we do not clear out the session, it
                // is possible a new User may accidently (if they
                // login incorrectly) continue on with information
                // associated with the previous User.  Currently the
                // only keys stored in the session are "turbine.user"
                // and "turbine.acl".
                if (data.getAction().equalsIgnoreCase(TurbineResources
                        .getString("action.login")))
                {
                    String[] names = data.getSession().getValueNames();
                    if (names != null)
                    {
                        for (int i=0; i< names.length; i++)
                        {
                            data.getSession().removeValue(names[i]);
                        }
                    }
                }
                ActionLoader.getInstance().exec ( data, data.getAction() );
                data.setAction(null);
            }

            // This is where the validation of the Session information
            // is performed if the user has not logged in yet, then
            // the screen is set to be Login. This also handles the
            // case of not having a screen defined by also setting the
            // screen to Login. If you want people to go to another
            // screen other than Login, you need to change that within
            // TurbineResources.properties...screen.homepage; or, you
            // can specify your own SessionValidator action.
            ActionLoader.getInstance().exec(
                data,TurbineResources.getString("action.sessionvalidator") );

            // Put the Access Control List into the RunData object, so
            // it is easily available to modules.  It is also placed
            // into the session for serialization.  Modules can null
            // out the ACL to force it to be rebuilt based on more
            // information.
            ActionLoader.getInstance().exec(
                data,TurbineResources.getString("action.accesscontroller"));

            // Start the execution phase. DefaultPage will execute the
            // appropriate action as well as get the Layout from the
            // Screen and then execute that. The Layout is then
            // responsible for executing the Navigation and Screen
            // modules.
            //
            // Note that by default, this cannot be overridden from
            // parameters passed in via post/query data. This is for
            // security purposes.  You should really never need more
            // than just the default page.  If you do, add logic to
            // DefaultPage to do what you want.
           
            String defaultPage = TurbineTemplate.getDefaultPageName(data);
           
            if (defaultPage == null)
            {
                /*
                 * In this case none of the template services are running.
                 * The application may be using ECS for views, or a
                 * decendent of RawScreen is trying to produce output.
                 * If there is a 'page.default' property in the TR.props
                 * then use that, otherwise return DefaultPage which will
                 * handle ECS view scenerios and RawScreen scenerios. The
                 * app developer can still specify the 'page.default'
                 * if they wish but the DefaultPage should work in
                 * most cases.
                 */
                defaultPage = TurbineResources.getString(
                    "page.default", "DefaultPage");
            }
           
            PageLoader.getInstance().exec(data, defaultPage);

            // If a module has set data.acl = null, remove acl from
            // the session.
            if ( data.getACL() == null )
            {
                data.getSession().removeValue(
                    AccessControlList.SESSION_KEY);
            }

            try
            {
                if ( data.isPageSet() == false &&
                     data.isOutSet() == false )
                    throw new Exception ( "Nothing to output" );

                // We are all done! if isPageSet() output that way
                // otherwise, data.getOut() has already been written
                // to the data.getOut().close() happens below in the
                // finally.
                if ( data.isPageSet() && data.isOutSet() == false )
                {
                    // Modules can override these.
                    data.getResponse()
                        .setLocale( data.getLocale() );
                    data.getResponse()
                        .setContentType( data.getContentType() );

                    // Handle the case where a module may want to send
                    // a redirect.
                    if ( ( data.getStatusCode() == 301 ||
                           data.getStatusCode() ==  302 ) &&
                         data.getRedirectURI() != null )
                    {
                        data.getResponse()
                            .sendRedirect ( data.getRedirectURI() );
                    }

                    // Set the status code.
                    data.getResponse().setStatus ( data.getStatusCode() );
                    // Output the Page.
                    data.getPage().output (data.getOut());
                }
            }
            catch ( Exception e )
            {
                // The output stream was probably closed by the client
                // end of things ie: the client clicked the Stop
                // button on the browser, so ignore any errors that
                // result.
            }
        }
        catch ( Exception e )
        {
            handleException(data, res, e);
        }
        catch (Throwable t)
        {
            handleException(data, res, t);
        }
        finally
        {
            // Make sure to close the outputstream when we are done.
            try
            {
                data.getOut().close();
            }
            catch (Exception e)
            {
                // Ignore.
            }

            // Return the used RunData to the factory for recycling.
            RunDataFactory.putRunData(data);
        }
    }

    /**
     * In this application doGet and doPost are the same thing.
     *
     * @param req Servlet request.
     * @param res Servlet response.
     * @exception IOException a servlet exception.
     * @exception ServletException a servlet exception.
     */
    public final void doPost (HttpServletRequest req,
                        HttpServletResponse res)
        throws IOException,
               ServletException
    {
        doGet ( req, res );
    }

    /**
     * Return the servlet info.
     *
     * @return a string with the servlet information.
     */
    public final String getServletInfo()
    {
        return "Turbine Servlet";
    }

    /**
     * This method is about making sure that we catch and display
     * errors to the screen in one fashion or another. What happens is
     * that it will attempt to show the error using your user defined
     * Error Screen. If that fails, then it will resort to just
     * displaying the error and logging it all over the place
     * including the servlet engine log file, the Turbine log file and
     * on the screen.
     *
     * @param data A Turbine RunData object.
     * @param res Servlet response.
     * @param e The exception to report.
     */
    private final void handleException(RunData data,
                                 HttpServletResponse res,
                                 Throwable t)
    {
        // make sure that the stack trace makes it the log
        Log.error("Turbine.handleException: "+t.getMessage());
        Log.error(t);

        String mimeType = "text/plain";
        try
        {
            // This is where we capture all exceptions and show the
            // Error Screen.
            data.setStackTrace(StringUtils.stackTrace(t),t);

            // setup the screen
            data.setScreen(TurbineResources
                           .getString("screen.error"));

            // do more screen setup for template execution if needed
            if (data.getTemplateInfo() != null)
                data.getTemplateInfo()
                    .setScreenTemplate(TurbineResources
                                       .getString("template.error"));

            // Make sure to not execute an action.
            data.setAction ("");

            PageLoader.getInstance()
                .exec(data,
                      TurbineResources.getString("page.default",
                      "DefaultPage"));

            data.getResponse().setContentType( data.getContentType() );
            data.getResponse().setStatus ( data.getStatusCode() );
            if( data.isPageSet() )
                data.getOut().print ( data.getPage().toString() );
        }
        // Catch this one because it occurs if some code hasn't been
        // completely re-compiled after a change..
        catch ( java.lang.NoSuchFieldError e )
        {
            try
            {
                data.getResponse().setContentType( mimeType );
                data.getResponse().setStatus ( 200 );
            }
            catch (Exception ignored) {}

            try
            {
                data.getOut().print ("java.lang.NoSuchFieldError: " +
                                     "Please recompile all of your " +
                                     "source code.");
            }
            catch (IOException ignored) {}

            log ( data.getStackTrace() );
            org.apache.turbine.util.Log.error ( e.getMessage(), e );
        }
        // Attempt to do *something* at this point...
        catch ( Throwable reallyScrewedNow )
        {
            StringBuffer msg = new StringBuffer();
            msg.append("Horrible Exception: ");
            if (data != null)
            {
                msg.append(data.getStackTrace());
            }
            else
            {
                msg.append(t);
            }
            try
            {
                res.setContentType( mimeType );
                res.setStatus ( 200 );
                res.getWriter().print (msg.toString());
            }
            catch (Exception ignored) {}
            org.apache.turbine.util.Log.error (
                    reallyScrewedNow.getMessage(), reallyScrewedNow );
        }
    }
}
TOP

Related Classes of org.apache.turbine.Turbine

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.