/* * Copyright 2001-2008 Geert Bevin (gbevin[remove] at uwyn dot com) * Licensed under the Apache License, Version 2.0 (the "License") * $Id: Gate.java 3928 2008-04-22 16:25:18Z gbevin $ */ package com.uwyn.rife.engine; import com.uwyn.rife.Version; import com.uwyn.rife.config.Config; import com.uwyn.rife.config.RifeConfig; import com.uwyn.rife.engine.exceptions.DeferException; import com.uwyn.rife.engine.exceptions.ElementCompilationFailedException; import com.uwyn.rife.engine.exceptions.EngineException; import com.uwyn.rife.rep.Rep; import com.uwyn.rife.rep.participants.ParticipantSite; import com.uwyn.rife.resources.ResourceFinderClasspath; import com.uwyn.rife.template.Template; import com.uwyn.rife.template.TemplateFactory; import com.uwyn.rife.template.exceptions.SyntaxErrorException; import com.uwyn.rife.tools.ExceptionFormattingUtils; import com.uwyn.rife.tools.ExceptionUtils; import com.uwyn.rife.tools.StringUtils; import com.uwyn.rife.tools.TerracottaUtils; import java.io.File; import java.io.IOException; import java.util.Set; import java.util.logging.Logger; public class Gate { public final static String INIT_PARAM_SITE_XML_PATH = "site.xml.path"; public final static String REQUEST_ATTRIBUTE_RIFE_ENGINE_EXCEPTION = "rife.engine.exception"; private Throwable mInitException = null; private InitConfig mInitConfig = null; private Site mSiteFromInit = null; private boolean mSiteFromRep = false; private volatile Boolean mTempPathSet = false; private String mWebappContextPath = null; public Gate() { mWebappContextPath = RifeConfig.Engine.getWebappContextPath(); } public Gate(Site site) { this(); mSiteFromInit = site; } public void init(InitConfig config) { mInitConfig = config; setApplicationClasspath(config); if (TerracottaUtils.isTcPresent()) { // force early creating of the TemplateClassLoader - needed for DSO page-in on second node TemplateFactory.HTML.get("errors.rife.engine_error_default"); } } private void setApplicationClasspath(InitConfig config) { // initialize the classpath of the web application // this is the best possible effort that can be made // since there's no standard way to obtain the classpath // of a web application StringBuilder application_classpath = new StringBuilder(); if (mInitConfig.getServletContext() != null) { // get the root of the web application String webapp_root = mInitConfig.getServletContext().getRealPath("/"); if (webapp_root != null) { if (webapp_root.endsWith(File.separator)) { webapp_root = webapp_root.substring(0, webapp_root.length()-File.separator.length()); } // add the classes dir application_classpath.append(webapp_root); application_classpath.append(File.separator).append("WEB-INF").append(File.separator).append("classes"); // add the jars in the lib dir Set<String> lib_resources = mInitConfig.getServletContext().getResourcePaths("/WEB-INF/lib"); if (lib_resources != null) { for (String lib_resource : lib_resources) { if (lib_resource.endsWith(".jar")) { application_classpath.append(File.pathSeparator); application_classpath.append(webapp_root); application_classpath.append(lib_resource); } } } } else { Logger.getLogger("com.uwyn.rife.engine").warning("Due to the behavior of your servlet container ("+mInitConfig.getServletContext().getServerInfo()+"), automatic element compilation is not supported and might fail while resolving imported classes."); } } // add the rife.webapp.path paths if (EngineClassLoaderRifeWebappPath.RIFE_WEBAPP_PATH != null) { for (String path : EngineClassLoaderRifeWebappPath.RIFE_WEBAPP_PATH) { application_classpath.append(File.pathSeparator); application_classpath.append(path); } } if (application_classpath.length() > 0) { RifeConfig.Global.setApplicationClassPath(application_classpath.toString()); } } public boolean handleRequest(String gateUrl, String elementUrl, Request request, Response response) { ensureExistingTempPath(request); // check if the gateUrl hasn't been overridden by a webapp context path configuration parameter if (mWebappContextPath != null) { gateUrl = mWebappContextPath; } // ensure a valid element url if (null == elementUrl || 0 == elementUrl.length()) { elementUrl = "/"; } // strip away the optional path parameters int path_parameters_index = elementUrl.indexOf(";"); if (path_parameters_index != -1) { elementUrl = elementUrl.substring(0, path_parameters_index); } // If an internal error occured, try to build the site at each request Site site = getSite(); // Handle the request // check if an exception occurred during the initialization if (mInitException != null) { printExceptionDetails(response, mInitException); return true; } // if no site is ready yet, don't continue processing if (null == site) { return handleSiteNotReady(elementUrl, response); } // Set up the element request and process it. try { ElementToService element_match = site.findElementForRequest(elementUrl); // If no element was found, don't continue executing the gate logic. // This could allow a next filter in the chain to be executed. if (null == element_match) { return false; } ElementInfo element_info = element_match.getElementInfo(); StateStore state_store = element_info.getStateStore(); state_store.init(request); request.init(state_store); RequestState request_state = RequestState.getInstance(mInitConfig, site, request, response, gateUrl, state_store.restoreResultStates(request), element_match.getPathInfo(), element_info); request_state.service(); response.close(); } catch (DeferException e) { return false; } catch (Throwable e) { handleRequestException(site, request, response, e); } return true; } private void ensureExistingTempPath(Request request) throws EngineException { if (!mTempPathSet && (!Config.hasRepInstance() || !Config.getRepInstance().hasParameter(RifeConfig.Global.PARAM_TEMP_PATH))) { // construct a temp path which is unique for the server and virtual host String tmpdir = System.getProperty("java.io.tmpdir"); tmpdir = StringUtils.stripFromEnd(tmpdir, File.separator); String context_path = request.getContextPath(); context_path = context_path.replace('/', File.separatorChar); RifeConfig.Global.setTempPath(tmpdir + File.separator + "rife_" + request.getServerName() + "_" + request.getServerPort() + "_" + context_path); // ensure that the temp path exists File file_temp_path = new File(RifeConfig.Global.getTempPath()); if (!file_temp_path.exists()) { if (!file_temp_path.mkdirs()) { throw new EngineException("Couldn't create the temporary directory : '" + RifeConfig.Global.getTempPath() + "'."); } } else if (!file_temp_path.isDirectory()) { throw new EngineException("The element package directory '" + RifeConfig.Global.getTempPath() + "' exists but is not a directory."); } else if (!file_temp_path.canWrite()) { throw new EngineException("The element package directory '" + RifeConfig.Global.getTempPath() + "' is not writable."); } // set the flag that indicates that the temp path has been set synchronized (mTempPathSet) { mTempPathSet = true; } } else { synchronized (mTempPathSet) { mTempPathSet = true; } } } public Site getSite() { // if the site has previously been initialized from init, return the // result immediately if (mSiteFromInit != null) { return mSiteFromInit; } Site result = null; try { // Ensure the presence of a servlet init configuration if (null == mInitConfig) { throw new Exception("No servlet configuration is available, it is required to set up the site structure."); } // Clear the init exception mInitException = null; // If there's a site participant, try to obtain a site instance from it if (Site.hasRepInstance()) { // only continue if the default repository has finished initializing if (!Site.getRepParticipant().isFinished() && !RifeConfig.Engine.getResponseRequiresSite()) { return null; } result = Site.getRepInstance(); if (null == result && Rep.getParticipant(Site.DEFAULT_PARTICIPANT_NAME) instanceof ParticipantSite && ((ParticipantSite)Rep.getParticipant(Site.DEFAULT_PARTICIPANT_NAME)).getException() != null) { throw ((ParticipantSite)Rep.getParticipant(Site.DEFAULT_PARTICIPANT_NAME)).getException(); } } // only proces the init parameter if the site hasn't previously been // obtained frop the rep if (!mSiteFromRep) { // Try to obtain a site xml path specification from an init config String site_xml_path = mInitConfig.getInitParameter(INIT_PARAM_SITE_XML_PATH); if (null == site_xml_path) { // If it doesn't exist, launch an error when the site hasn't been // obtained through a participant earlier on. if (null == result) { throw new Exception("A site couldn't be obtained through the repository from the "+Site.DEFAULT_PARTICIPANT_NAME+" participant, nor through the init parameter '"+INIT_PARAM_SITE_XML_PATH+"'."); } // since no init param exists and a site could be obtained // from the rep, remember this setup else { mSiteFromRep = true; } } else { // If the site xml path could be found, use it to populate a new // site instance. This will override a prior site instance that was // obtained through a participant. // Like that, a site xml path specified in an init config overrides // a site participant. ResourceFinderClasspath resourcefinder = ResourceFinderClasspath.getInstance(); SiteBuilder builder = new SiteBuilder(site_xml_path, resourcefinder); result = builder.getSite(); mSiteFromInit = result; mSiteFromRep = false; } } } catch (Throwable e) { handleSiteInitException(e); } return result; } private void handleSiteInitException(Throwable e) { // ensure the later init exceptions don't overwrite earlier ones if (null == mInitException) { if (RifeConfig.Engine.getPrettyEngineExceptions()) { mInitException = e; } else { if (e instanceof RuntimeException) { throw (RuntimeException)e; } else { throw new RuntimeException(e); } } } } private boolean handleSiteNotReady(String elementUrl, Response response) { // check if the url matches one of the passthrough suffixes boolean passthrough = false; for (String suffix : RifeConfig.Engine.getSiteInitializingPassthroughSuffixes()) { if (elementUrl.endsWith(suffix)) { passthrough = true; break; } } if (passthrough) { return false; } else { String initializing_url = RifeConfig.Engine.getSiteInitializingRedirectUrl(); try { // either set the 'service is unavailable' status if (null == initializing_url) { response.sendError(503); // SC_SERVICE_UNAVAILABLE } // either redirect to a dedicated URL else { response.sendRedirect(initializing_url); } } catch (EngineException e) { response.sendError(503); // SC_SERVICE_UNAVAILABLE } return true; } } private void handleRequestException(Site site, Request request, Response response, Throwable e) { if (site != null) { site.resetLastModificationCheck(); } String message = "Error on host " + request.getServerName() + ":" + request.getServerPort() + "/" + request.getContextPath(); if (RifeConfig.Engine.getLogEngineExceptions()) { Logger.getLogger("com.uwyn.rife.engine").severe(message + "\n" + ExceptionUtils.getExceptionStackTrace(e)); } if (!RifeConfig.Engine.getPrettyEngineExceptions()) { request.setAttribute(REQUEST_ATTRIBUTE_RIFE_ENGINE_EXCEPTION, e); if (e instanceof RuntimeException) { throw (RuntimeException)e; } else { throw new RuntimeException(message, e); } } printExceptionDetails(response, e); } private void printExceptionDetails(Response response, Throwable exception) { TemplateFactory template_factory = null; if (response.isContentTypeSet()) { String content_type = response.getContentType(); if (content_type.startsWith("text/xml") || content_type.startsWith("application/xhtml+xml")) { template_factory = TemplateFactory.XML; response.setContentType("text/xml"); } } if (null == template_factory) { template_factory = TemplateFactory.HTML; response.setContentType("text/html"); } // pretty exception formatting and outputting instead of the default servlet // engine's formatting Template template = null; Throwable cause = exception; while (cause != null && cause.getCause() != cause) { if (cause instanceof ElementCompilationFailedException || cause instanceof SyntaxErrorException) { template = template_factory.get("errors.rife.engine_error_compilation"); break; } cause = cause.getCause(); } if (null == template) { template = template_factory.get("errors.rife.engine_error_default"); } template.setValue("exceptions", ExceptionFormattingUtils.formatExceptionStackTrace(exception, template)); template.setValue("RIFE_VERSION", template.getEncoder().encode(Version.getVersion())); try { response.getWriter().print(template.getContent()); } catch (IOException e2) { throw new RuntimeException(e2); } } }