org.ireland.jnetty.jsp.JspServletComposite.java Source code

Java tutorial

Introduction

Here is the source code for org.ireland.jnetty.jsp.JspServletComposite.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ireland.jnetty.jsp;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.jasper.Constants;
import org.apache.jasper.EmbeddedServletOptions;
import org.apache.jasper.Options;
import org.apache.jasper.compiler.JspRuntimeContext;
import org.apache.jasper.compiler.Localizer;
import org.apache.jasper.security.SecurityUtil;
import org.apache.jasper.servlet.JspServletWrapper;
import org.apache.jasper.util.ExceptionUtils;

import org.apache.tomcat.PeriodicEventListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * org.apache.jasper.servlet.JspServlet???Jsp?, ??Servlet,JspServletComposite
 * 
 * ??JSP?,???*.jsp?,*.jsp
 * 
 * @author KEN
 * 
 */
public class JspServletComposite extends HttpServlet implements PeriodicEventListener {

    private static final long serialVersionUID = 1L;

    // Logger
    private static final Log log = LogFactory.getLog(JspServletComposite.class);

    private static final boolean debug = log.isDebugEnabled();

    private transient ServletContext context;

    private ServletConfig config;

    //Jsp?
    private transient Options options;

    //JSP??JspServletWrapper
    private transient JspRuntimeContext rctxt;

    /*
     * Initializes this JspServletComposite.
     */
    @Override
    public void init(ServletConfig config) throws ServletException {

        super.init(config);
        this.config = config;
        this.context = config.getServletContext();

        //JSPServletContext
        Thread.currentThread().setContextClassLoader(context.getClassLoader());

        // Initialize the JSP Runtime Context
        // Check for a custom Options implementation
        String engineOptionsName = config.getInitParameter("engineOptionsClass"); //
        if (engineOptionsName != null) {
            // Instantiate the indicated Options implementation
            try {
                ClassLoader loader = Thread.currentThread().getContextClassLoader();
                Class<?> engineOptionsClass = loader.loadClass(engineOptionsName);
                Class<?>[] ctorSig = { ServletConfig.class, ServletContext.class };
                Constructor<?> ctor = engineOptionsClass.getConstructor(ctorSig);
                Object[] args = { config, context };
                options = (Options) ctor.newInstance(args);
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                // Need to localize this.
                log.warn("Failed to load engineOptionsClass", e);
                // Use the default Options implementation
                options = new EmbeddedServletOptions(config, context);
            }
        } else {
            // Use the default Options implementation
            options = new EmbeddedServletOptions(config, context);
        }

        rctxt = new JspRuntimeContext(context, options);

        if (debug) {
            log.debug(Localizer.getMessage("jsp.message.scratch.dir.is", options.getScratchDir().toString()));
            log.debug(Localizer.getMessage("jsp.message.dont.modify.servlets"));
        }
    }

    /**
     * Returns the number of JSPs for which JspServletWrappers exist, i.e., the number of JSPs that have been loaded
     * into the webapp with which this JspServlet is associated.
     * 
     * <p>
     * This info may be used for monitoring purposes.
     * 
     * @return The number of JSPs that have been loaded into the webapp with which this JspServlet is associated
     */
    public int getJspCount() {
        return this.rctxt.getJspCount();
    }

    /**
     * Resets the JSP reload counter.
     * 
     * @param count
     *            Value to which to reset the JSP reload counter
     */
    public void setJspReloadCount(int count) {
        this.rctxt.setJspReloadCount(count);
    }

    /**
     * Gets the number of JSPs that have been reloaded.
     * 
     * <p>
     * This info may be used for monitoring purposes.
     * 
     * @return The number of JSPs (in the webapp with which this JspServlet is associated) that have been reloaded
     */
    public int getJspReloadCount() {
        return this.rctxt.getJspReloadCount();
    }

    /**
     * Gets the number of JSPs that are in the JSP limiter queue
     * 
     * <p>
     * This info may be used for monitoring purposes.
     * 
     * @return The number of JSPs (in the webapp with which this JspServlet is associated) that are in the JSP limiter
     *         queue
     */
    public int getJspQueueLength() {
        return this.rctxt.getJspQueueLength();
    }

    /**
     * Gets the number of JSPs that have been unloaded.
     * 
     * <p>
     * This info may be used for monitoring purposes.
     * 
     * @return The number of JSPs (in the webapp with which this JspServlet is associated) that have been unloaded
     */
    public int getJspUnloadCount() {
        return this.rctxt.getJspUnloadCount();
    }

    /**
     *<p> 
     *??JSPServlet
     *</p>
     * <p>
     * Look for a <em>precompilation request</em> as described in Section 8.4.2 of the JSP 1.2 Specification.
     * <strong>WARNING</strong> - we cannot use <code>request.getParameter()</code> for this, because that will trigger
     * parsing all of the request parameters, and not give a servlet the opportunity to call
     * <code>request.setCharacterEncoding()</code> first.
     * </p>
     * 
     * @param request
     *            The servlet request we are processing
     * 
     * @exception ServletException
     *                if an invalid parameter value for the <code>jsp_precompile</code> parameter name is specified
     */
    boolean isPreCompile(HttpServletRequest request) throws ServletException {

        String queryString = request.getQueryString();
        if (queryString == null) {
            return (false);
        }
        int start = queryString.indexOf(Constants.PRECOMPILE);
        if (start < 0) {
            return (false);
        }
        queryString = queryString.substring(start + Constants.PRECOMPILE.length());
        if (queryString.length() == 0) {
            return (true); // ?jsp_precompile
        }
        if (queryString.startsWith("&")) {
            return (true); // ?jsp_precompile&foo=bar...
        }
        if (!queryString.startsWith("=")) {
            return (false); // part of some other name or value
        }
        int limit = queryString.length();
        int ampersand = queryString.indexOf("&");
        if (ampersand > 0) {
            limit = ampersand;
        }
        String value = queryString.substring(1, limit);
        if (value.equals("true")) {
            return (true); // ?jsp_precompile=true
        } else if (value.equals("false")) {
            // Spec says if jsp_precompile=false, the request should not
            // be delivered to the JSP page; the easiest way to implement
            // this is to set the flag to true, and precompile the page anyway.
            // This still conforms to the spec, since it says the
            // precompilation request can be ignored.
            return (true); // ?jsp_precompile=false
        } else {
            throw new ServletException(
                    "Cannot have request parameter " + Constants.PRECOMPILE + " set to " + value);
        }

    }

    @Override
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String jspUri = request.getRequestURI();

        if (jspUri == null) {
            // JSP specified via <jsp-file> in <servlet> declaration and supplied through
            // custom servlet container code
            jspUri = (String) request.getAttribute(Constants.JSP_FILE);
        }
        if (jspUri == null) {
            /*
             * Check to see if the requested JSP has been the target of a RequestDispatcher.include()
             */
            jspUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH);
            if (jspUri != null) {
                /*
                 * Requested JSP has been target of RequestDispatcher.include(). Its path is assembled from the relevant
                 * javax.servlet.include.* request attributes
                 */
                String pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO);
                if (pathInfo != null) {
                    jspUri += pathInfo;
                }
            } else {
                /*
                 * Requested JSP has not been the target of a RequestDispatcher.include(). Reconstruct its path from the
                 * request's getServletPath() and getPathInfo()
                 */
                jspUri = request.getServletPath();
                String pathInfo = request.getPathInfo();
                if (pathInfo != null) {
                    jspUri += pathInfo;
                }
            }
        }

        if (debug) {
            log.debug("JspEngine --> " + jspUri);
            log.debug("\t     ServletPath: " + request.getServletPath());
            log.debug("\t        PathInfo: " + request.getPathInfo());
            log.debug("\t        RealPath: " + context.getRealPath(jspUri));
            log.debug("\t      RequestURI: " + request.getRequestURI());
            log.debug("\t     QueryString: " + request.getQueryString());
        }

        try {
            boolean isPreCompile = isPreCompile(request); //??JSPServlet

            serviceJspFile(request, response, jspUri, isPreCompile);
        } catch (RuntimeException e) {
            throw e;
        } catch (ServletException e) {
            throw e;
        } catch (IOException e) {
            throw e;
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(e);
        }

    }

    @Override
    public void destroy() {
        if (debug) {
            log.debug("JspServlet.destroy()");
        }

        rctxt.destroy();
    }

    @Override
    public void periodicEvent() {
        rctxt.checkUnload();
        rctxt.checkCompile();
    }

    // -------------------------------------------------------- Private Methods

    /**
     * jsp? JspServletWrapper  service() 
     * @param request
     * @param response
     * @param jspUri
     * @param isPreCompile
     * @throws ServletException
     * @throws IOException
     */
    private void serviceJspFile(HttpServletRequest request, HttpServletResponse response, String jspUri,
            boolean isPreCompile) throws ServletException, IOException {

        JspServletWrapper wrapper = rctxt.getWrapper(jspUri);
        if (wrapper == null) {
            synchronized (this) {
                wrapper = rctxt.getWrapper(jspUri);
                if (wrapper == null) {
                    // Check if the requested JSP page exists, to avoid
                    // creating unnecessary directories and files.
                    if (null == context.getResource(jspUri)) {
                        handleMissingResource(request, response, jspUri); //?jsp
                        return;
                    }
                    wrapper = new JspServletWrapper(config, options, jspUri, rctxt);
                    rctxt.addWrapper(jspUri, wrapper);
                }
            }
        }

        try {
            wrapper.service(request, response, isPreCompile);
        } catch (FileNotFoundException fnfe) {
            handleMissingResource(request, response, jspUri);
        }

    }

    /**
     * jsp
     * @param request
     * @param response
     * @param jspUri
     * @throws ServletException
     * @throws IOException
     */
    private void handleMissingResource(HttpServletRequest request, HttpServletResponse response, String jspUri)
            throws ServletException, IOException {

        String includeRequestUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI);

        if (includeRequestUri != null) {
            // This file was included. Throw an exception as
            // a response.sendError() will be ignored
            String msg = Localizer.getMessage("jsp.error.file.not.found", jspUri);
            // Strictly, filtering this is an application
            // responsibility but just in case...
            throw new ServletException(SecurityUtil.filter(msg));
        } else {
            try //?404?
            {
                response.sendError(HttpServletResponse.SC_NOT_FOUND, request.getRequestURI());
            } catch (IllegalStateException ise) {
                log.error(Localizer.getMessage("jsp.error.file.not.found", jspUri));
            }
        }
        return;
    }

}