0001: package org.apache.turbine;
0002:
0003: /*
0004: * Copyright 2001-2005 The Apache Software Foundation.
0005: *
0006: * Licensed under the Apache License, Version 2.0 (the "License")
0007: * you may not use this file except in compliance with the License.
0008: * You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing, software
0013: * distributed under the License is distributed on an "AS IS" BASIS,
0014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: * See the License for the specific language governing permissions and
0016: * limitations under the License.
0017: */
0018:
0019: import java.io.File;
0020: import java.io.FileInputStream;
0021: import java.io.FileNotFoundException;
0022: import java.io.IOException;
0023: import java.io.UnsupportedEncodingException;
0024:
0025: import java.util.Properties;
0026:
0027: import javax.servlet.ServletConfig;
0028: import javax.servlet.ServletContext;
0029: import javax.servlet.ServletException;
0030: import javax.servlet.http.HttpServlet;
0031: import javax.servlet.http.HttpServletRequest;
0032: import javax.servlet.http.HttpServletResponse;
0033:
0034: import org.apache.commons.configuration.Configuration;
0035: import org.apache.commons.configuration.ConfigurationFactory;
0036: import org.apache.commons.configuration.PropertiesConfiguration;
0037:
0038: import org.apache.commons.lang.StringUtils;
0039:
0040: import org.apache.commons.lang.exception.ExceptionUtils;
0041:
0042: import org.apache.commons.logging.Log;
0043: import org.apache.commons.logging.LogFactory;
0044:
0045: import org.apache.log4j.PropertyConfigurator;
0046:
0047: import org.apache.turbine.modules.ActionLoader;
0048: import org.apache.turbine.modules.PageLoader;
0049:
0050: import org.apache.turbine.services.ServiceManager;
0051: import org.apache.turbine.services.TurbineServices;
0052: import org.apache.turbine.services.avaloncomponent.AvalonComponentService;
0053: import org.apache.turbine.services.component.ComponentService;
0054: import org.apache.turbine.services.template.TemplateService;
0055: import org.apache.turbine.services.template.TurbineTemplate;
0056: import org.apache.turbine.services.rundata.RunDataService;
0057: import org.apache.turbine.services.rundata.TurbineRunDataFacade;
0058: import org.apache.turbine.services.velocity.VelocityService;
0059:
0060: import org.apache.turbine.util.RunData;
0061: import org.apache.turbine.util.ServerData;
0062: import org.apache.turbine.util.TurbineConfig;
0063: import org.apache.turbine.util.TurbineException;
0064: import org.apache.turbine.util.security.AccessControlList;
0065: import org.apache.turbine.util.template.TemplateInfo;
0066: import org.apache.turbine.util.uri.URIConstants;
0067:
0068: /**
0069: * Turbine is the main servlet for the entire system. It is <code>final</code>
0070: * because you should <i>not</i> ever need to subclass this servlet. If you
0071: * need to perform initialization of a service, then you should implement the
0072: * Services API and let your code be initialized by it.
0073: * If you need to override something in the <code>doGet()</code> or
0074: * <code>doPost()</code> methods, edit the TurbineResources.properties file and
0075: * specify your own classes there.
0076: * <p>
0077: * Turbine servlet recognizes the following initialization parameters.
0078: * <ul>
0079: * <li><code>properties</code> the path to TurbineResources.properties file
0080: * used by the default implementation of <code>ResourceService</code>, relative
0081: * to the application root.</li>
0082: * <li><code>basedir</code> this parameter is used <strong>only</strong> if your
0083: * application server does not support web applications, or the or does not
0084: * support <code>ServletContext.getRealPath(String)</code> method correctly.
0085: * You can use this parameter to specify the directory within the server's
0086: * filesystem, that is the base of your web application.</li>
0087: * </ul>
0088: *
0089: * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
0090: * @author <a href="mailto:bmclaugh@algx.net">Brett McLaughlin</a>
0091: * @author <a href="mailto:greg@shwoop.com">Greg Ritter</a>
0092: * @author <a href="mailto:john.mcnally@clearink.com">John D. McNally</a>
0093: * @author <a href="mailto:frank.kim@clearink.com">Frank Y. Kim</a>
0094: * @author <a href="mailto:krzewski@e-point.pl">Rafal Krzewski</a>
0095: * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
0096: * @author <a href="mailto:sean@informage.net">Sean Legassick</a>
0097: * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a>
0098: * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
0099: * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
0100: * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
0101: * @version $Id: Turbine.java 264152 2005-08-29 14:50:22Z henning $
0102: */
0103: public class Turbine extends HttpServlet implements TurbineConstants {
0104: /** SerialVersionUID for serialization */
0105: private static final long serialVersionUID = -6895381097045304308L;
0106:
0107: /**
0108: * Name of path info parameter used to indicate the redirected stage of
0109: * a given user's initial Turbine request
0110: */
0111: public static final String REDIRECTED_PATHINFO_NAME = "redirected";
0112:
0113: /** The base directory key */
0114: public static final String BASEDIR_KEY = "basedir";
0115:
0116: /**
0117: * In certain situations the init() method is called more than once,
0118: * somtimes even concurrently. This causes bad things to happen,
0119: * so we use this flag to prevent it.
0120: */
0121: private static boolean firstInit = true;
0122:
0123: /** Whether init succeeded or not. */
0124: private static Throwable initFailure = null;
0125:
0126: /**
0127: * Should initialization activities be performed during doGet() execution?
0128: */
0129: private static boolean firstDoGet = true;
0130:
0131: /**
0132: * Keep all the properties of the web server in a convenient data
0133: * structure
0134: */
0135: private static ServerData serverData = null;
0136:
0137: /** The base from which the Turbine application will operate. */
0138: private static String applicationRoot;
0139:
0140: /** Servlet config for this Turbine webapp. */
0141: private static ServletConfig servletConfig;
0142:
0143: /** Servlet context for this Turbine webapp. */
0144: private static ServletContext servletContext;
0145:
0146: /**
0147: * The webapp root where the Turbine application
0148: * is running in the servlet container.
0149: * This might differ from the application root.
0150: */
0151: private static String webappRoot;
0152:
0153: /** Our internal configuration object */
0154: private static Configuration configuration = null;
0155:
0156: /** A reference to the Template Service */
0157: private TemplateService templateService = null;
0158:
0159: /** A reference to the RunData Service */
0160: private RunDataService rundataService = null;
0161:
0162: /** Default Input encoding if the servlet container does not report an encoding */
0163: private String inputEncoding = null;
0164:
0165: /** Logging class from commons.logging */
0166: private static Log log = LogFactory.getLog(Turbine.class);
0167:
0168: /**
0169: * This init method will load the default resources from a
0170: * properties file.
0171: *
0172: * This method is called by init(ServletConfig config)
0173: *
0174: * @exception ServletException a servlet exception.
0175: */
0176: public final void init() throws ServletException {
0177: synchronized (this .getClass()) {
0178: super .init();
0179: ServletConfig config = getServletConfig();
0180:
0181: if (!firstInit) {
0182: log
0183: .info("Double initialization of Turbine was attempted!");
0184: return;
0185: }
0186: // executing init will trigger some static initializers, so we have
0187: // only one chance.
0188: firstInit = false;
0189:
0190: try {
0191: ServletContext context = config.getServletContext();
0192:
0193: configure(config, context);
0194:
0195: templateService = TurbineTemplate.getService();
0196: rundataService = TurbineRunDataFacade.getService();
0197:
0198: if (rundataService == null) {
0199: throw new TurbineException(
0200: "No RunData Service configured!");
0201: }
0202:
0203: } catch (Exception e) {
0204: // save the exception to complain loudly later :-)
0205: initFailure = e;
0206: log.fatal("Turbine: init() failed: ", e);
0207: throw new ServletException("Turbine: init() failed", e);
0208: }
0209: log.info("Turbine: init() Ready to Rumble!");
0210: }
0211: }
0212:
0213: /**
0214: * Read the master configuration file in, configure logging
0215: * and start up any early services.
0216: *
0217: * @param config The Servlet Configuration supplied by the container
0218: * @param context The Servlet Context supplied by the container
0219: *
0220: * @throws Exception A problem occured while reading the configuration or performing early startup
0221: */
0222:
0223: private void configure(ServletConfig config, ServletContext context)
0224: throws Exception {
0225: // Set the application root. This defaults to the webapp
0226: // context if not otherwise set. This is to allow 2.1 apps
0227: // to be developed from CVS. This feature will carry over
0228: // into 3.0.
0229: applicationRoot = findInitParameter(context, config,
0230: APPLICATION_ROOT_KEY, APPLICATION_ROOT_DEFAULT);
0231:
0232: webappRoot = config.getServletContext().getRealPath("/");
0233: // log.info("Web Application root is " + webappRoot);
0234: // log.info("Application root is " + applicationRoot);
0235:
0236: if (applicationRoot == null
0237: || applicationRoot.equals(WEB_CONTEXT)) {
0238: applicationRoot = webappRoot;
0239: // log.info("got empty or 'webContext' Application root. Application root now: " + applicationRoot);
0240: }
0241:
0242: // Set the applicationRoot for this webapp.
0243: setApplicationRoot(applicationRoot);
0244:
0245: // Create any directories that need to be setup for
0246: // a running Turbine application.
0247: createRuntimeDirectories(context, config);
0248:
0249: //
0250: // Now we run the Turbine configuration code. There are two ways
0251: // to configure Turbine:
0252: //
0253: // a) By supplying an web.xml init parameter called "configuration"
0254: //
0255: // <init-param>
0256: // <param-name>configuration</param-name>
0257: // <param-value>/WEB-INF/conf/turbine.xml</param-value>
0258: // </init-param>
0259: //
0260: // This loads an XML based configuration file.
0261: //
0262: // b) By supplying an web.xml init parameter called "properties"
0263: //
0264: // <init-param>
0265: // <param-name>properties</param-name>
0266: // <param-value>/WEB-INF/conf/TurbineResources.properties</param-value>
0267: // </init-param>
0268: //
0269: // This loads a Properties based configuration file. Actually, these are
0270: // extended properties as provided by commons-configuration
0271: //
0272: // If neither a) nor b) is supplied, Turbine will fall back to the
0273: // known behaviour of loading a properties file called
0274: // /WEB-INF/conf/TurbineResources.properties relative to the
0275: // web application root.
0276:
0277: String confFile = findInitParameter(context, config,
0278: TurbineConfig.CONFIGURATION_PATH_KEY, null);
0279:
0280: String confPath;
0281: String confStyle = "unset";
0282:
0283: if (StringUtils.isNotEmpty(confFile)) {
0284: confPath = getRealPath(confFile);
0285: ConfigurationFactory configurationFactory = new ConfigurationFactory(
0286: confPath);
0287: configurationFactory.setBasePath(getApplicationRoot());
0288: configuration = configurationFactory.getConfiguration();
0289: confStyle = "XML";
0290: } else {
0291: confFile = findInitParameter(context, config,
0292: TurbineConfig.PROPERTIES_PATH_KEY,
0293: TurbineConfig.PROPERTIES_PATH_DEFAULT);
0294:
0295: confPath = getRealPath(confFile);
0296:
0297: // This should eventually be a Configuration
0298: // interface so that service and app configuration
0299: // can be stored anywhere.
0300: configuration = (Configuration) new PropertiesConfiguration(
0301: confPath);
0302: confStyle = "Properties";
0303: }
0304:
0305: //
0306: // Set up logging as soon as possible
0307: //
0308: String log4jFile = configuration.getString(LOG4J_CONFIG_FILE,
0309: LOG4J_CONFIG_FILE_DEFAULT);
0310:
0311: if (StringUtils.isNotEmpty(log4jFile)
0312: && !log4jFile.equalsIgnoreCase("none")) {
0313: log4jFile = getRealPath(log4jFile);
0314:
0315: //
0316: // Load the config file above into a Properties object and
0317: // fix up the Application root
0318: //
0319: Properties p = new Properties();
0320: try {
0321: p.load(new FileInputStream(log4jFile));
0322: p.setProperty(APPLICATION_ROOT_KEY,
0323: getApplicationRoot());
0324: PropertyConfigurator.configure(p);
0325:
0326: log.info("Configured log4j from " + log4jFile);
0327: } catch (FileNotFoundException fnf) {
0328: System.err
0329: .println("Could not open Log4J configuration file "
0330: + log4jFile + ": ");
0331: fnf.printStackTrace();
0332: }
0333: }
0334:
0335: // Now report our successful configuration to the world
0336: log.info("Loaded configuration (" + confStyle + ") from "
0337: + confFile + " (" + confPath + ")");
0338:
0339: setTurbineServletConfig(config);
0340: setTurbineServletContext(context);
0341:
0342: getServiceManager().setApplicationRoot(applicationRoot);
0343:
0344: // We want to set a few values in the configuration so
0345: // that ${variable} interpolation will work for
0346: //
0347: // ${applicationRoot}
0348: // ${webappRoot}
0349: configuration
0350: .setProperty(APPLICATION_ROOT_KEY, applicationRoot);
0351: configuration.setProperty(WEBAPP_ROOT_KEY, webappRoot);
0352:
0353: //
0354: // Be sure, that our essential services get run early
0355: //
0356: configuration.setProperty(TurbineServices.SERVICE_PREFIX
0357: + ComponentService.SERVICE_NAME + ".earlyInit",
0358: Boolean.TRUE);
0359:
0360: configuration.setProperty(TurbineServices.SERVICE_PREFIX
0361: + AvalonComponentService.SERVICE_NAME + ".earlyInit",
0362: Boolean.TRUE);
0363:
0364: getServiceManager().setConfiguration(configuration);
0365:
0366: // Initialize the service manager. Services
0367: // that have its 'earlyInit' property set to
0368: // a value of 'true' will be started when
0369: // the service manager is initialized.
0370: getServiceManager().init();
0371:
0372: // Get the default input encoding
0373: inputEncoding = configuration.getString(
0374: TurbineConstants.PARAMETER_ENCODING_KEY,
0375: TurbineConstants.PARAMETER_ENCODING_DEFAULT);
0376:
0377: if (log.isDebugEnabled()) {
0378: log
0379: .debug("Input Encoding has been set to "
0380: + inputEncoding);
0381: }
0382: }
0383:
0384: /**
0385: * Create any directories that might be needed during
0386: * runtime. Right now this includes:
0387: *
0388: * <ul>
0389: *
0390: * <li>The directory to write the log files to (relative to the
0391: * web application root), or <code>null</code> for the default of
0392: * <code>/logs</code>. The directory is specified via the {@link
0393: * TurbineConstants#LOGGING_ROOT} parameter.</li>
0394: *
0395: * </ul>
0396: *
0397: * @param context Global initialization parameters.
0398: * @param config Initialization parameters specific to the Turbine
0399: * servlet.
0400: */
0401: private static void createRuntimeDirectories(
0402: ServletContext context, ServletConfig config) {
0403: String path = findInitParameter(context, config,
0404: LOGGING_ROOT_KEY, LOGGING_ROOT_DEFAULT);
0405:
0406: File logDir = new File(getRealPath(path));
0407: if (!logDir.exists()) {
0408: // Create the logging directory
0409: if (!logDir.mkdirs()) {
0410: System.err.println("Cannot create directory for logs!");
0411: }
0412: }
0413: }
0414:
0415: /**
0416: * Finds the specified servlet configuration/initialization
0417: * parameter, looking first for a servlet-specific parameter, then
0418: * for a global parameter, and using the provided default if not
0419: * found.
0420: */
0421: protected static final String findInitParameter(
0422: ServletContext context, ServletConfig config, String name,
0423: String defaultValue) {
0424: String path = null;
0425:
0426: // Try the name as provided first.
0427: boolean usingNamespace = name.startsWith(CONFIG_NAMESPACE);
0428: while (true) {
0429: path = config.getInitParameter(name);
0430: if (StringUtils.isEmpty(path)) {
0431: path = context.getInitParameter(name);
0432: if (StringUtils.isEmpty(path)) {
0433: // The named parameter didn't yield a value.
0434: if (usingNamespace) {
0435: path = defaultValue;
0436: } else {
0437: // Try again using Turbine's namespace.
0438: name = CONFIG_NAMESPACE + '.' + name;
0439: usingNamespace = true;
0440: continue;
0441: }
0442: }
0443: }
0444: break;
0445: }
0446:
0447: return path;
0448: }
0449:
0450: /**
0451: * Initializes the services which need <code>RunData</code> to
0452: * initialize themselves (post startup).
0453: *
0454: * @param data The first <code>GET</code> request.
0455: */
0456: public final void init(RunData data) {
0457: synchronized (Turbine.class) {
0458: if (firstDoGet) {
0459: // All we want to do here is save some servlet
0460: // information so that services and processes
0461: // that don't have direct access to a RunData
0462: // object can still know something about
0463: // the servlet environment.
0464: saveServletInfo(data);
0465:
0466: // Mark that we're done.
0467: firstDoGet = false;
0468: log.info("Turbine: first Request successful");
0469: }
0470: }
0471: }
0472:
0473: /**
0474: * Return the current configuration with all keys included
0475: *
0476: * @return a Configuration Object
0477: */
0478: public static Configuration getConfiguration() {
0479: return configuration;
0480: }
0481:
0482: /**
0483: * Return the server name.
0484: *
0485: * @return String server name
0486: */
0487: public static String getServerName() {
0488: return getDefaultServerData().getServerName();
0489: }
0490:
0491: /**
0492: * Return the server scheme.
0493: *
0494: * @return String server scheme
0495: */
0496: public static String getServerScheme() {
0497: return getDefaultServerData().getServerScheme();
0498: }
0499:
0500: /**
0501: * Return the server port.
0502: *
0503: * @return String server port
0504: */
0505: public static String getServerPort() {
0506: return Integer.toString(getDefaultServerData().getServerPort());
0507: }
0508:
0509: /**
0510: * Get the script name. This is the initial script name.
0511: * Actually this is probably not needed any more. I'll
0512: * check. jvz.
0513: *
0514: * @return String initial script name.
0515: */
0516: public static String getScriptName() {
0517: return getDefaultServerData().getScriptName();
0518: }
0519:
0520: /**
0521: * Return the context path.
0522: *
0523: * @return String context path
0524: */
0525: public static String getContextPath() {
0526: return getDefaultServerData().getContextPath();
0527: }
0528:
0529: /**
0530: * Return all the Turbine Servlet information (Server Name, Port,
0531: * Scheme in a ServerData structure. This is generated from the
0532: * values set when initializing the Turbine and may not be correct
0533: * if you're running in a clustered structure. This might be used
0534: * if you need a DataURI and have no RunData object handy-
0535: *
0536: * @return An initialized ServerData object
0537: */
0538: public static ServerData getDefaultServerData() {
0539: if (serverData == null) {
0540: log
0541: .error("ServerData Information requested from Turbine before first request!");
0542: // Will be overwritten once the first request is run;
0543: serverData = new ServerData(null, URIConstants.HTTP_PORT,
0544: URIConstants.HTTP, null, null);
0545: }
0546: return serverData;
0547: }
0548:
0549: /**
0550: * Set the servlet config for this turbine webapp.
0551: *
0552: * @param config New servlet config
0553: */
0554: public static void setTurbineServletConfig(ServletConfig config) {
0555: servletConfig = config;
0556: }
0557:
0558: /**
0559: * Get the servlet config for this turbine webapp.
0560: *
0561: * @return ServletConfig
0562: */
0563: public static ServletConfig getTurbineServletConfig() {
0564: return servletConfig;
0565: }
0566:
0567: /**
0568: * Set the servlet context for this turbine webapp.
0569: *
0570: * @param context New servlet context.
0571: */
0572: public static void setTurbineServletContext(ServletContext context) {
0573: servletContext = context;
0574: }
0575:
0576: /**
0577: * Get the servlet context for this turbine webapp.
0578: *
0579: * @return ServletContext
0580: */
0581: public static ServletContext getTurbineServletContext() {
0582: return servletContext;
0583: }
0584:
0585: /**
0586: * The <code>Servlet</code> destroy method. Invokes
0587: * <code>ServiceBroker</code> tear down method.
0588: */
0589: public final void destroy() {
0590: // Shut down all Turbine Services.
0591: getServiceManager().shutdownServices();
0592: System.gc();
0593:
0594: firstInit = true;
0595: firstDoGet = true;
0596: log.info("Turbine: Done shutting down!");
0597: }
0598:
0599: /**
0600: * The primary method invoked when the Turbine servlet is executed.
0601: *
0602: * @param req Servlet request.
0603: * @param res Servlet response.
0604: * @exception IOException a servlet exception.
0605: * @exception ServletException a servlet exception.
0606: */
0607: public final void doGet(HttpServletRequest req,
0608: HttpServletResponse res) throws IOException,
0609: ServletException {
0610: // set to true if the request is to be redirected by the page
0611: boolean requestRedirected = false;
0612:
0613: // Placeholder for the RunData object.
0614: RunData data = null;
0615: try {
0616: // Check to make sure that we started up properly.
0617: if (initFailure != null) {
0618: throw initFailure;
0619: }
0620:
0621: //
0622: // If the servlet container gives us no clear indication about the
0623: // Encoding of the contents, set it to our default value.
0624: if (req.getCharacterEncoding() == null) {
0625: if (log.isDebugEnabled()) {
0626: log.debug("Changing Input Encoding to "
0627: + inputEncoding);
0628: }
0629:
0630: try {
0631: req.setCharacterEncoding(inputEncoding);
0632: } catch (UnsupportedEncodingException uee) {
0633: log.warn("Could not change request encoding to "
0634: + inputEncoding, uee);
0635: }
0636: }
0637:
0638: // Get general RunData here...
0639: // Perform turbine specific initialization below.
0640: data = rundataService.getRunData(req, res,
0641: getServletConfig());
0642:
0643: // If this is the first invocation, perform some
0644: // initialization. Certain services need RunData to initialize
0645: // themselves.
0646: if (firstDoGet) {
0647: init(data);
0648: }
0649:
0650: // set the session timeout if specified in turbine's properties
0651: // file if this is a new session
0652: if (data.getSession().isNew()) {
0653: int timeout = configuration.getInt(SESSION_TIMEOUT_KEY,
0654: SESSION_TIMEOUT_DEFAULT);
0655:
0656: if (timeout != SESSION_TIMEOUT_DEFAULT) {
0657: data.getSession().setMaxInactiveInterval(timeout);
0658: }
0659: }
0660:
0661: // Fill in the screen and action variables.
0662: data.setScreen(data.getParameters().getString(
0663: URIConstants.CGI_SCREEN_PARAM));
0664: data.setAction(data.getParameters().getString(
0665: URIConstants.CGI_ACTION_PARAM));
0666:
0667: // Special case for login and logout, this must happen before the
0668: // session validator is executed in order either to allow a user to
0669: // even login, or to ensure that the session validator gets to
0670: // mandate its page selection policy for non-logged in users
0671: // after the logout has taken place.
0672: if (data.hasAction()) {
0673: String action = data.getAction();
0674: log.debug("action = " + action);
0675:
0676: if (action.equalsIgnoreCase(configuration.getString(
0677: ACTION_LOGIN_KEY, ACTION_LOGIN_DEFAULT))) {
0678: loginAction(data);
0679: } else if (action.equalsIgnoreCase(configuration
0680: .getString(ACTION_LOGOUT_KEY,
0681: ACTION_LOGOUT_DEFAULT))) {
0682: logoutAction(data);
0683: }
0684: }
0685:
0686: // This is where the validation of the Session information
0687: // is performed if the user has not logged in yet, then
0688: // the screen is set to be Login. This also handles the
0689: // case of not having a screen defined by also setting the
0690: // screen to Login. If you want people to go to another
0691: // screen other than Login, you need to change that within
0692: // TurbineResources.properties...screen.homepage; or, you
0693: // can specify your own SessionValidator action.
0694: ActionLoader.getInstance().exec(
0695: data,
0696: configuration.getString(
0697: ACTION_SESSION_VALIDATOR_KEY,
0698: ACTION_SESSION_VALIDATOR_DEFAULT));
0699:
0700: // Put the Access Control List into the RunData object, so
0701: // it is easily available to modules. It is also placed
0702: // into the session for serialization. Modules can null
0703: // out the ACL to force it to be rebuilt based on more
0704: // information.
0705: ActionLoader.getInstance().exec(
0706: data,
0707: configuration.getString(
0708: ACTION_ACCESS_CONTROLLER_KEY,
0709: ACTION_ACCESS_CONTROLLER_DEFAULT));
0710:
0711: // Start the execution phase. DefaultPage will execute the
0712: // appropriate action as well as get the Layout from the
0713: // Screen and then execute that. The Layout is then
0714: // responsible for executing the Navigation and Screen
0715: // modules.
0716: //
0717: // Note that by default, this cannot be overridden from
0718: // parameters passed in via post/query data. This is for
0719: // security purposes. You should really never need more
0720: // than just the default page. If you do, add logic to
0721: // DefaultPage to do what you want.
0722:
0723: String defaultPage = (templateService == null) ? null
0724: : templateService.getDefaultPageName(data);
0725:
0726: if (defaultPage == null) {
0727: /*
0728: * In this case none of the template services are running.
0729: * The application may be using ECS for views, or a
0730: * decendent of RawScreen is trying to produce output.
0731: * If there is a 'page.default' property in the TR.props
0732: * then use that, otherwise return DefaultPage which will
0733: * handle ECS view scenerios and RawScreen scenerios. The
0734: * app developer can still specify the 'page.default'
0735: * if they wish but the DefaultPage should work in
0736: * most cases.
0737: */
0738: defaultPage = configuration.getString(PAGE_DEFAULT_KEY,
0739: PAGE_DEFAULT_DEFAULT);
0740: }
0741:
0742: PageLoader.getInstance().exec(data, defaultPage);
0743:
0744: // If a module has set data.acl = null, remove acl from
0745: // the session.
0746: if (data.getACL() == null) {
0747: try {
0748: data.getSession().removeAttribute(
0749: AccessControlList.SESSION_KEY);
0750: } catch (IllegalStateException ignored) {
0751: }
0752: }
0753:
0754: // handle a redirect request
0755: requestRedirected = ((data.getRedirectURI() != null) && (data
0756: .getRedirectURI().length() > 0));
0757: if (requestRedirected) {
0758: if (data.getResponse().isCommitted()) {
0759: requestRedirected = false;
0760: log
0761: .warn("redirect requested, response already committed: "
0762: + data.getRedirectURI());
0763: } else {
0764: data.getResponse().sendRedirect(
0765: data.getRedirectURI());
0766: }
0767: }
0768:
0769: if (!requestRedirected) {
0770: try {
0771: if (data.isPageSet() == false
0772: && data.isOutSet() == false) {
0773: throw new Exception("Nothing to output");
0774: }
0775:
0776: // We are all done! if isPageSet() output that way
0777: // otherwise, data.getOut() has already been written
0778: // to the data.getOut().close() happens below in the
0779: // finally.
0780: if (data.isPageSet() && data.isOutSet() == false) {
0781: // Modules can override these.
0782: data.getResponse().setLocale(data.getLocale());
0783: data.getResponse().setContentType(
0784: data.getContentType());
0785:
0786: // Set the status code.
0787: data.getResponse().setStatus(
0788: data.getStatusCode());
0789: // Output the Page.
0790: data.getPage().output(data.getOut());
0791: }
0792: } catch (Exception e) {
0793: // The output stream was probably closed by the client
0794: // end of things ie: the client clicked the Stop
0795: // button on the browser, so ignore any errors that
0796: // result.
0797: log.debug("Output stream closed? ", e);
0798: }
0799: }
0800: } catch (Exception e) {
0801: handleException(data, res, e);
0802: } catch (Throwable t) {
0803: handleException(data, res, t);
0804: } finally {
0805: // Return the used RunData to the factory for recycling.
0806: rundataService.putRunData(data);
0807: }
0808: }
0809:
0810: /**
0811: * In this application doGet and doPost are the same thing.
0812: *
0813: * @param req Servlet request.
0814: * @param res Servlet response.
0815: * @exception IOException a servlet exception.
0816: * @exception ServletException a servlet exception.
0817: */
0818: public final void doPost(HttpServletRequest req,
0819: HttpServletResponse res) throws IOException,
0820: ServletException {
0821: doGet(req, res);
0822: }
0823:
0824: /**
0825: * This method is executed if the configured Login action should be
0826: * executed by Turbine.
0827: * <p>
0828: * This Action must be performed before the Session validation or we
0829: * get sent in an endless loop back to the Login screen before
0830: * the action can be performed
0831: *
0832: * @param data a RunData object
0833: *
0834: * @throws Exception A problem while logging in occured.
0835: */
0836: private void loginAction(RunData data) throws Exception {
0837: ActionLoader.getInstance().exec(data, data.getAction());
0838: cleanupTemplateContext(data);
0839: data.setAction(null);
0840: }
0841:
0842: /**
0843: * This method is executed if the configured Logout action should be
0844: * executed by Turbine.
0845: * <p>
0846: * This Action must be performed before the Session validation for the
0847: * session validator to send us back to the Login screen.
0848: * <p>
0849: * The existing session is invalidated before the logout action is
0850: * executed.
0851: *
0852: * @param data a RunData object
0853: *
0854: * @throws Exception A problem while logging out occured.
0855: */
0856: private void logoutAction(RunData data) throws Exception {
0857: ActionLoader.getInstance().exec(data, data.getAction());
0858: cleanupTemplateContext(data);
0859: data.setAction(null);
0860: data.getSession().invalidate();
0861: }
0862:
0863: /**
0864: * cleans the Velocity Context if available.
0865: *
0866: * @param data A RunData Object
0867: *
0868: * @throws Exception A problem while cleaning out the Template Context occured.
0869: */
0870: private void cleanupTemplateContext(RunData data) throws Exception {
0871: // This is Velocity specific and shouldn't be done here.
0872: // But this is a band aid until we get real listeners
0873: // here.
0874: TemplateInfo ti = data.getTemplateInfo();
0875: if (ti != null) {
0876: ti.removeTemp(VelocityService.CONTEXT);
0877: }
0878: }
0879:
0880: /**
0881: * Return the servlet info.
0882: *
0883: * @return a string with the servlet information.
0884: */
0885: public final String getServletInfo() {
0886: return "Turbine Servlet";
0887: }
0888:
0889: /**
0890: * This method is about making sure that we catch and display
0891: * errors to the screen in one fashion or another. What happens is
0892: * that it will attempt to show the error using your user defined
0893: * Error Screen. If that fails, then it will resort to just
0894: * displaying the error and logging it all over the place
0895: * including the servlet engine log file, the Turbine log file and
0896: * on the screen.
0897: *
0898: * @param data A Turbine RunData object.
0899: * @param res Servlet response.
0900: * @param t The exception to report.
0901: */
0902: private void handleException(RunData data, HttpServletResponse res,
0903: Throwable t) {
0904: // make sure that the stack trace makes it the log
0905: log.error("Turbine.handleException: ", t);
0906:
0907: String mimeType = "text/plain";
0908: try {
0909: // This is where we capture all exceptions and show the
0910: // Error Screen.
0911: data.setStackTrace(ExceptionUtils.getStackTrace(t), t);
0912:
0913: // setup the screen
0914: data.setScreen(configuration.getString(SCREEN_ERROR_KEY,
0915: SCREEN_ERROR_DEFAULT));
0916:
0917: // do more screen setup for template execution if needed
0918: if (data.getTemplateInfo() != null) {
0919: data.getTemplateInfo().setScreenTemplate(
0920: configuration.getString(TEMPLATE_ERROR_KEY,
0921: TEMPLATE_ERROR_VM));
0922: }
0923:
0924: // Make sure to not execute an action.
0925: data.setAction("");
0926:
0927: PageLoader.getInstance().exec(
0928: data,
0929: configuration.getString(PAGE_DEFAULT_KEY,
0930: PAGE_DEFAULT_DEFAULT));
0931:
0932: data.getResponse().setContentType(data.getContentType());
0933: data.getResponse().setStatus(data.getStatusCode());
0934: if (data.isPageSet()) {
0935: data.getOut().print(data.getPage().toString());
0936: }
0937: }
0938: // Catch this one because it occurs if some code hasn't been
0939: // completely re-compiled after a change..
0940: catch (java.lang.NoSuchFieldError e) {
0941: try {
0942: data.getResponse().setContentType(mimeType);
0943: data.getResponse().setStatus(200);
0944: } catch (Exception ignored) {
0945: // Ignored
0946: }
0947:
0948: try {
0949: data
0950: .getOut()
0951: .print(
0952: "java.lang.NoSuchFieldError: "
0953: + "Please recompile all of your source code.");
0954: } catch (IOException ignored) {
0955: }
0956:
0957: log.error(data.getStackTrace(), e);
0958: }
0959: // Attempt to do *something* at this point...
0960: catch (Throwable reallyScrewedNow) {
0961: StringBuffer msg = new StringBuffer();
0962: msg.append("Horrible Exception: ");
0963: if (data != null) {
0964: msg.append(data.getStackTrace());
0965: } else {
0966: msg.append(t);
0967: }
0968: try {
0969: res.setContentType(mimeType);
0970: res.setStatus(200);
0971: res.getWriter().print(msg.toString());
0972: } catch (Exception ignored) {
0973: }
0974:
0975: log.error(reallyScrewedNow.getMessage(), reallyScrewedNow);
0976: }
0977: }
0978:
0979: /**
0980: * Save some information about this servlet so that
0981: * it can be utilized by object instances that do not
0982: * have direct access to RunData.
0983: *
0984: * @param data
0985: */
0986: public static synchronized void saveServletInfo(RunData data) {
0987: // Store the context path for tools like ContentURI and
0988: // the UIManager that use webapp context path information
0989: // for constructing URLs.
0990:
0991: //
0992: // Bundle all the information above up into a convenient structure
0993: //
0994: serverData = (ServerData) data.getServerData().clone();
0995: }
0996:
0997: /**
0998: * Set the application root for the webapp.
0999: *
1000: * @param val New app root.
1001: */
1002: public static void setApplicationRoot(String val) {
1003: applicationRoot = val;
1004: }
1005:
1006: /**
1007: * Get the application root for this Turbine webapp. This
1008: * concept was started in 3.0 and will allow an app to be
1009: * developed from a standard CVS layout. With a simple
1010: * switch the app will work fully within the servlet
1011: * container for deployment.
1012: *
1013: * @return String applicationRoot
1014: */
1015: public static String getApplicationRoot() {
1016: return applicationRoot;
1017: }
1018:
1019: /**
1020: * Used to get the real path of configuration and resource
1021: * information. This can be used by an app being
1022: * developed in a standard CVS layout.
1023: *
1024: * @param path path translated to the application root
1025: * @return the real path
1026: */
1027: public static String getRealPath(String path) {
1028: if (path.startsWith("/")) {
1029: path = path.substring(1);
1030: }
1031:
1032: return new File(getApplicationRoot(), path).getAbsolutePath();
1033: }
1034:
1035: /**
1036: * Return an instance of the currently configured Service Manager
1037: *
1038: * @return A service Manager instance
1039: */
1040: private ServiceManager getServiceManager() {
1041: return TurbineServices.getInstance();
1042: }
1043: }
|