You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/09/28 03:55:35 UTC

svn commit: r819444 [23/27] - in /struts/struts2/trunk/plugins/embeddedjsp: ./ src/main/java/org/apache/struts2/el/ src/main/java/org/apache/struts2/el/lang/ src/main/java/org/apache/struts2/el/parser/ src/main/java/org/apache/struts2/el/util/ src/main...

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServlet.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServlet.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServlet.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServlet.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,346 @@
+/*
+ * 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.apache.struts2.jasper.servlet;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.Enumeration;
+
+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.struts2.jasper.Constants;
+import org.apache.struts2.jasper.EmbeddedServletOptions;
+import org.apache.struts2.jasper.Options;
+import org.apache.struts2.jasper.compiler.JspRuntimeContext;
+import org.apache.struts2.jasper.compiler.Localizer;
+import org.apache.struts2.jasper.security.SecurityUtil;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.PeriodicEventListener;
+
+/**
+ * The JSP engine (a.k.a Jasper).
+ *
+ * The servlet container is responsible for providing a
+ * URLClassLoader for the web application context Jasper
+ * is being used in. Jasper will try get the Tomcat
+ * ServletContext attribute for its ServletContext class
+ * loader, if that fails, it uses the parent class loader.
+ * In either case, it must be a URLClassLoader.
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Remy Maucherat
+ * @author Kin-man Chung
+ * @author Glenn Nielsen
+ */
+public class JspServlet extends HttpServlet implements PeriodicEventListener {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspServlet.class);
+
+    private ServletContext context;
+    private ServletConfig config;
+    private Options options;
+    private JspRuntimeContext rctxt;
+
+
+    /*
+     * Initializes this JspServlet.
+     */
+    public void init(ServletConfig config) throws ServletException {
+        
+        super.init(config);
+        this.config = config;
+        this.context = config.getServletContext();
+        
+        // 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) {
+                // 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 (log.isDebugEnabled()) {
+            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();
+    }
+
+
+    /**
+     * <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 requset we are processing
+     *
+     * @exception ServletException if an invalid parameter value for the
+     *  <code>jsp_precompile</code> parameter name is specified
+     */
+    boolean preCompile(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);
+        }
+
+    }
+    
+
+    public void service (HttpServletRequest request, 
+    			 HttpServletResponse response)
+                throws ServletException, IOException {
+
+        String jspUri = null;
+
+        String jspFile = (String) request.getAttribute(Constants.JSP_FILE);
+        if (jspFile != null) {
+            // JSP is specified via <jsp-file> in <servlet> declaration
+            jspUri = jspFile;
+        } else {
+            /*
+             * Check to see if the requested JSP has been the target of a
+             * RequestDispatcher.include()
+             */
+            jspUri = (String) request.getAttribute(Constants.INC_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(
+                                    "javax.servlet.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 (log.isDebugEnabled()) {	    
+            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());
+            log.debug("\t  Request Params: ");
+            Enumeration e = request.getParameterNames();
+            while (e.hasMoreElements()) {
+                String name = (String) e.nextElement();
+                log.debug("\t\t " + name + " = " 
+                          + request.getParameter(name));
+            }
+        }
+
+        try {
+            boolean precompile = preCompile(request);
+            serviceJspFile(request, response, jspUri, null, precompile);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (ServletException e) {
+            throw e;
+        } catch (IOException e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new ServletException(e);
+        }
+
+    }
+
+    public void destroy() {
+        if (log.isDebugEnabled()) {
+            log.debug("JspServlet.destroy()");
+        }
+
+        rctxt.destroy();
+    }
+
+
+    public void periodicEvent() {
+        rctxt.checkCompile();
+    }
+
+    // -------------------------------------------------------- Private Methods
+
+    private void serviceJspFile(HttpServletRequest request,
+                                HttpServletResponse response, String jspUri,
+                                Throwable exception, boolean precompile)
+        throws ServletException, IOException {
+
+        JspServletWrapper wrapper =
+            (JspServletWrapper) rctxt.getWrapper(jspUri);
+        if (wrapper == null) {
+            synchronized(this) {
+                wrapper = (JspServletWrapper) 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)) {
+                        String includeRequestUri = (String)
+                        request.getAttribute(
+                                "javax.servlet.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 {
+                                response.sendError(
+                                        HttpServletResponse.SC_NOT_FOUND,
+                                        request.getRequestURI());
+                            } catch (IllegalStateException ise) {
+                                log.error(Localizer.getMessage(
+                                        "jsp.error.file.not.found",
+                                        jspUri));
+                            }
+                        }
+                        return;
+                    }
+                    boolean isErrorPage = exception != null;
+                    wrapper = new JspServletWrapper(config, options, jspUri,
+                                                    isErrorPage, rctxt);
+                    rctxt.addWrapper(jspUri,wrapper);
+                }
+            }
+        }
+
+        wrapper.service(request, response, precompile);
+
+    }
+
+
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServletWrapper.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServletWrapper.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServletWrapper.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspServletWrapper.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,527 @@
+/*
+ * 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.apache.struts2.jasper.servlet;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.SingleThreadModel;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.jsp.tagext.TagInfo;
+
+import org.apache.AnnotationProcessor;
+import org.apache.struts2.jasper.JasperException;
+import org.apache.struts2.jasper.JspCompilationContext;
+import org.apache.struts2.jasper.Options;
+import org.apache.struts2.jasper.compiler.ErrorDispatcher;
+import org.apache.struts2.jasper.compiler.JavacErrorDetail;
+import org.apache.struts2.jasper.compiler.JspRuntimeContext;
+import org.apache.struts2.jasper.compiler.Localizer;
+import org.apache.struts2.jasper.runtime.JspSourceDependent;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * The JSP engine (a.k.a Jasper).
+ *
+ * The servlet container is responsible for providing a
+ * URLClassLoader for the web application context Jasper
+ * is being used in. Jasper will try get the Tomcat
+ * ServletContext attribute for its ServletContext class
+ * loader, if that fails, it uses the parent class loader.
+ * In either case, it must be a URLClassLoader.
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Remy Maucherat
+ * @author Kin-man Chung
+ * @author Glenn Nielsen
+ * @author Tim Fennell
+ */
+
+public class JspServletWrapper {
+
+    // Logger
+    private Log log = LogFactory.getLog(JspServletWrapper.class);
+
+    private Servlet theServlet;
+    private String jspUri;
+    private Class servletClass;
+    private Class tagHandlerClass;
+    private JspCompilationContext ctxt;
+    private long available = 0L;
+    private ServletConfig config;
+    private Options options;
+    private boolean firstTime = true;
+    private boolean reload = true;
+    private boolean isTagFile;
+    private int tripCount;
+    private JasperException compileException;
+    private long servletClassLastModifiedTime;
+    private long lastModificationTest = 0L;
+
+    /*
+     * JspServletWrapper for JSP pages.
+     */
+    public JspServletWrapper(ServletConfig config, Options options, String jspUri,
+                      boolean isErrorPage, JspRuntimeContext rctxt)
+            throws JasperException {
+
+	this.isTagFile = false;
+        this.config = config;
+        this.options = options;
+        this.jspUri = jspUri;
+        ctxt = new JspCompilationContext(jspUri, isErrorPage, options,
+					 config.getServletContext(),
+					 this, rctxt, null);
+    }
+
+    /*
+     * JspServletWrapper for tag files.
+     */
+    public JspServletWrapper(ServletContext servletContext,
+			     Options options,
+			     String tagFilePath,
+			     TagInfo tagInfo,
+			     JspRuntimeContext rctxt,
+			     URL tagFileJarUrl)
+	    throws JasperException {
+
+	this.isTagFile = true;
+        this.config = null;	// not used
+        this.options = options;
+	this.jspUri = tagFilePath;
+	this.tripCount = 0;
+        ctxt = new JspCompilationContext(jspUri, tagInfo, options,
+					 servletContext, this, rctxt,
+					 tagFileJarUrl);
+    }
+
+    public JspCompilationContext getJspEngineContext() {
+        return ctxt;
+    }
+
+    public void setReload(boolean reload) {
+        this.reload = reload;
+    }
+
+    public Servlet getServlet()
+        throws ServletException, IOException, FileNotFoundException
+    {
+        if (reload) {
+            synchronized (this) {
+                // Synchronizing on jsw enables simultaneous loading
+                // of different pages, but not the same page.
+                if (reload) {
+                    // This is to maintain the original protocol.
+                    destroy();
+                    
+                    Servlet servlet = null;
+                    
+                    try {
+                        servletClass = ctxt.load();
+                        servlet = (Servlet) servletClass.newInstance();
+                        AnnotationProcessor annotationProcessor = (AnnotationProcessor) config.getServletContext().getAttribute(AnnotationProcessor.class.getName());
+                        if (annotationProcessor != null) {
+                           annotationProcessor.processAnnotations(servlet);
+                           annotationProcessor.postConstruct(servlet);
+                        }
+                    } catch (IllegalAccessException e) {
+                        throw new JasperException(e);
+                    } catch (InstantiationException e) {
+                        throw new JasperException(e);
+                    } catch (Exception e) {
+                        throw new JasperException(e);
+                    }
+                    
+                    servlet.init(config);
+
+                    if (!firstTime) {
+                        ctxt.getRuntimeContext().incrementJspReloadCount();
+                    }
+
+                    theServlet = servlet;
+                    reload = false;
+                }
+            }    
+        }
+        return theServlet;
+    }
+
+    public ServletContext getServletContext() {
+        return config.getServletContext();
+    }
+
+    /**
+     * Sets the compilation exception for this JspServletWrapper.
+     *
+     * @param je The compilation exception
+     */
+    public void setCompilationException(JasperException je) {
+        this.compileException = je;
+    }
+
+    /**
+     * Sets the last-modified time of the servlet class file associated with
+     * this JspServletWrapper.
+     *
+     * @param lastModified Last-modified time of servlet class
+     */
+    public void setServletClassLastModifiedTime(long lastModified) {
+        if (this.servletClassLastModifiedTime < lastModified) {
+            synchronized (this) {
+                if (this.servletClassLastModifiedTime < lastModified) {
+                    this.servletClassLastModifiedTime = lastModified;
+                    reload = true;
+                }
+            }
+        }
+    }
+
+    /**
+     * Compile (if needed) and load a tag file
+     */
+    public Class loadTagFile() throws JasperException {
+
+        try {
+            if (ctxt.isRemoved()) {
+                throw new FileNotFoundException(jspUri);
+            }
+            if (options.getDevelopment() || firstTime ) {
+                synchronized (this) {
+                    firstTime = false;
+                    ctxt.compile();
+                }
+            } else {
+                if (compileException != null) {
+                    throw compileException;
+                }
+            }
+
+            if (reload) {
+                tagHandlerClass = ctxt.load();
+                reload = false;
+            }
+        } catch (FileNotFoundException ex) {
+            throw new JasperException(ex);
+	}
+
+	return tagHandlerClass;
+    }
+
+    /**
+     * Compile and load a prototype for the Tag file.  This is needed
+     * when compiling tag files with circular dependencies.  A prototpe
+     * (skeleton) with no dependencies on other other tag files is
+     * generated and compiled.
+     */
+    public Class loadTagFilePrototype() throws JasperException {
+
+	ctxt.setPrototypeMode(true);
+	try {
+	    return loadTagFile();
+	} finally {
+	    ctxt.setPrototypeMode(false);
+	}
+    }
+
+    /**
+     * Get a list of files that the current page has source dependency on.
+     */
+    public java.util.List getDependants() {
+        try {
+            Object target;
+            if (isTagFile) {
+                if (reload) {
+                    tagHandlerClass = ctxt.load();
+                    reload = false;
+                }
+                target = tagHandlerClass.newInstance();
+            } else {
+                target = getServlet();
+            }
+            if (target != null && target instanceof JspSourceDependent) {
+                return ((java.util.List) ((JspSourceDependent) target).getDependants());
+            }
+        } catch (Throwable ex) {
+        }
+        return null;
+    }
+
+    public boolean isTagFile() {
+	return this.isTagFile;
+    }
+
+    public int incTripCount() {
+	return tripCount++;
+    }
+
+    public int decTripCount() {
+	return tripCount--;
+    }
+
+    public void service(HttpServletRequest request, 
+                        HttpServletResponse response,
+                        boolean precompile)
+	    throws ServletException, IOException, FileNotFoundException {
+        
+        try {
+
+            if (ctxt.isRemoved()) {
+                throw new FileNotFoundException(jspUri);
+            }
+
+            if ((available > 0L) && (available < Long.MAX_VALUE)) {
+                if (available > System.currentTimeMillis()) {
+                    response.setDateHeader("Retry-After", available);
+                    response.sendError
+                        (HttpServletResponse.SC_SERVICE_UNAVAILABLE,
+                         Localizer.getMessage("jsp.error.unavailable"));
+                    return;
+                } else {
+                    // Wait period has expired. Reset.
+                    available = 0;
+                }
+            }
+
+            /*
+             * (1) Compile
+             */
+            if (options.getDevelopment() || firstTime ) {
+                synchronized (this) {
+                    firstTime = false;
+
+                    // The following sets reload to true, if necessary
+                    ctxt.compile();
+                }
+            } else {
+                if (compileException != null) {
+                    // Throw cached compilation exception
+                    throw compileException;
+                }
+            }
+
+            /*
+             * (2) (Re)load servlet class file
+             */
+            getServlet();
+
+            // If a page is to be precompiled only, return.
+            if (precompile) {
+                return;
+            }
+
+        } catch (ServletException ex) {
+            if (options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw ex;
+            }
+        } catch (IOException ex) {
+            if (options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw ex;
+            }
+        } catch (IllegalStateException ex) {
+            if (options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw ex;
+            }
+        } catch (Exception ex) {
+            if (options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw new JasperException(ex);
+            }
+        }
+
+        try {
+            
+            /*
+             * (3) Service request
+             */
+            if (theServlet instanceof SingleThreadModel) {
+               // sync on the wrapper so that the freshness
+               // of the page is determined right before servicing
+               synchronized (this) {
+                   theServlet.service(request, response);
+                }
+            } else {
+                theServlet.service(request, response);
+            }
+
+        } catch (UnavailableException ex) {
+            String includeRequestUri = (String)
+                request.getAttribute("javax.servlet.include.request_uri");
+            if (includeRequestUri != null) {
+                // This file was included. Throw an exception as
+                // a response.sendError() will be ignored by the
+                // servlet engine.
+                throw ex;
+            } else {
+                int unavailableSeconds = ex.getUnavailableSeconds();
+                if (unavailableSeconds <= 0) {
+                    unavailableSeconds = 60;        // Arbitrary default
+                }
+                available = System.currentTimeMillis() +
+                    (unavailableSeconds * 1000L);
+                response.sendError
+                    (HttpServletResponse.SC_SERVICE_UNAVAILABLE, 
+                     ex.getMessage());
+            }
+        } catch (ServletException ex) {
+            if(options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw ex;
+            }
+        } catch (IOException ex) {
+            if(options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw ex;
+            }
+        } catch (IllegalStateException ex) {
+            if(options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw ex;
+            }
+        } catch (Exception ex) {
+            if(options.getDevelopment()) {
+                throw handleJspException(ex);
+            } else {
+                throw new JasperException(ex);
+            }
+        }
+    }
+
+    public void destroy() {
+        if (theServlet != null) {
+            theServlet.destroy();
+            AnnotationProcessor annotationProcessor = (AnnotationProcessor) config.getServletContext().getAttribute(AnnotationProcessor.class.getName());
+            if (annotationProcessor != null) {
+                try {
+                    annotationProcessor.preDestroy(theServlet);
+                } catch (Exception e) {
+                    // Log any exception, since it can't be passed along
+                    log.error(Localizer.getMessage("jsp.error.file.not.found",
+                           e.getMessage()), e);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return Returns the lastModificationTest.
+     */
+    public long getLastModificationTest() {
+        return lastModificationTest;
+    }
+    /**
+     * @param lastModificationTest The lastModificationTest to set.
+     */
+    public void setLastModificationTest(long lastModificationTest) {
+        this.lastModificationTest = lastModificationTest;
+    }
+
+    /**
+     * <p>Attempts to construct a JasperException that contains helpful information
+     * about what went wrong. Uses the JSP compiler system to translate the line
+     * number in the generated servlet that originated the exception to a line
+     * number in the JSP.  Then constructs an exception containing that
+     * information, and a snippet of the JSP to help debugging.
+     * Please see http://issues.apache.org/bugzilla/show_bug.cgi?id=37062 and
+     * http://www.tfenne.com/jasper/ for more details.
+     *</p>
+     *
+     * @param ex the exception that was the cause of the problem.
+     * @return a JasperException with more detailed information
+     */
+    protected JasperException handleJspException(Exception ex) {
+        try {
+            Throwable realException = ex;
+            if (ex instanceof ServletException) {
+                realException = ((ServletException) ex).getRootCause();
+            }
+
+            // First identify the stack frame in the trace that represents the JSP
+            StackTraceElement[] frames = realException.getStackTrace();
+            StackTraceElement jspFrame = null;
+
+            for (int i=0; i<frames.length; ++i) {
+                if ( frames[i].getClassName().equals(this.getServlet().getClass().getName()) ) {
+                    jspFrame = frames[i];
+                    break;
+                }
+            }
+
+            if (jspFrame == null) {
+                // If we couldn't find a frame in the stack trace corresponding
+                // to the generated servlet class, we can't really add anything
+                return new JasperException(ex);
+            }
+            else {
+                int javaLineNumber = jspFrame.getLineNumber();
+                JavacErrorDetail detail = ErrorDispatcher.createJavacError(
+                        jspFrame.getMethodName(),
+                        this.ctxt.getCompiler().getPageNodes(),
+                        null,
+                        javaLineNumber,
+                        ctxt);
+
+                // If the line number is less than one we couldn't find out
+                // where in the JSP things went wrong
+                int jspLineNumber = detail.getJspBeginLineNumber();
+                if (jspLineNumber < 1) {
+                    throw new JasperException(ex);
+                }
+
+                if (options.getDisplaySourceFragment()) {
+                    return new JasperException(Localizer.getMessage
+                            ("jsp.exception", detail.getJspFileName(),
+                                    "" + jspLineNumber) +
+                                    "\n\n" + detail.getJspExtract() +
+                                    "\n\nStacktrace:", ex);
+                    
+                } else {
+                    return new JasperException(Localizer.getMessage
+                            ("jsp.exception", detail.getJspFileName(),
+                                    "" + jspLineNumber), ex);
+                }
+            }
+        } catch (Exception je) {
+            // If anything goes wrong, just revert to the original behaviour
+            if (ex instanceof JasperException) {
+                return (JasperException) ex;
+            } else {
+                return new JasperException(ex);
+            }
+        }
+    }
+
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/mbeans-descriptors.xml
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/mbeans-descriptors.xml?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/mbeans-descriptors.xml (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/mbeans-descriptors.xml Mon Sep 28 01:55:26 2009
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<mbeans-descriptors>
+
+  <mbean         name="JspMonitor"
+          description="JSP Monitoring"
+               domain="Catalina"
+                group="Monitoring"
+                 type="org.apache.struts2.jasper.servlet.JspServlet">
+
+    <attribute   name="jspCount"
+          description="The number of JSPs that have been loaded into a webapp"
+                 type="int"/>
+
+    <attribute   name="jspReloadCount"
+          description="The number of JSPs that have been reloaded"
+                 type="int"/>
+
+  </mbean>
+
+</mbeans-descriptors>
\ No newline at end of file

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/Util.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/Util.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/Util.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/Util.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,328 @@
+/*
+ * 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.apache.struts2.jasper.tagplugins.jstl;
+
+import org.apache.struts2.jasper.Constants;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspTagException;
+import javax.servlet.jsp.PageContext;
+
+/**
+ * Util contains some often used consts, static methods and embedded class
+ * to support the JSTL tag plugin.
+ */
+
+public class Util {
+    
+    public static final String VALID_SCHEME_CHAR = 
+        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-";
+    
+    public static final String DEFAULT_ENCODING = 
+        "ISO-8859-1";
+    
+    public static final int HIGHEST_SPECIAL = '>';
+    
+    public static char[][] specialCharactersRepresentation = new char[HIGHEST_SPECIAL + 1][];
+    
+    static {
+        specialCharactersRepresentation['&'] = "&amp;".toCharArray();
+        specialCharactersRepresentation['<'] = "&lt;".toCharArray();
+        specialCharactersRepresentation['>'] = "&gt;".toCharArray();
+        specialCharactersRepresentation['"'] = "&#034;".toCharArray();
+        specialCharactersRepresentation['\''] = "&#039;".toCharArray();
+    }
+    
+    /**
+     * Converts the given string description of a scope to the corresponding
+     * PageContext constant.
+     *
+     * The validity of the given scope has already been checked by the
+     * appropriate TLV.
+     *
+     * @param scope String description of scope
+     *
+     * @return PageContext constant corresponding to given scope description
+     * 
+     * taken from org.apache.taglibs.standard.tag.common.core.Util  
+     */
+    public static int getScope(String scope){
+        int ret = PageContext.PAGE_SCOPE;
+        
+        if("request".equalsIgnoreCase(scope)){
+            ret = PageContext.REQUEST_SCOPE;
+        }else if("session".equalsIgnoreCase(scope)){
+            ret = PageContext.SESSION_SCOPE;
+        }else if("application".equalsIgnoreCase(scope)){
+            ret = PageContext.APPLICATION_SCOPE;
+        }
+        
+        return ret;
+    }
+    
+    /**
+     * Returns <tt>true</tt> if our current URL is absolute,
+     * <tt>false</tt> otherwise.
+     * taken from org.apache.taglibs.standard.tag.common.core.ImportSupport
+     */
+    public static boolean isAbsoluteUrl(String url){
+        if(url == null){
+            return false;
+        }
+        
+        int colonPos = url.indexOf(":");
+        if(colonPos == -1){
+            return false;
+        }
+        
+        for(int i=0;i<colonPos;i++){
+            if(VALID_SCHEME_CHAR.indexOf(url.charAt(i)) == -1){
+                return false;
+            }
+        }
+        
+        return true;
+    }
+    
+    /**
+     * Get the value associated with a content-type attribute.
+     * Syntax defined in RFC 2045, section 5.1.
+     * taken from org.apache.taglibs.standard.tag.common.core.Util
+     */
+    public static String getContentTypeAttribute(String input, String name) {
+        int begin;
+        int end;
+        int index = input.toUpperCase().indexOf(name.toUpperCase());
+        if (index == -1) return null;
+        index = index + name.length(); // positioned after the attribute name
+        index = input.indexOf('=', index); // positioned at the '='
+        if (index == -1) return null;
+        index += 1; // positioned after the '='
+        input = input.substring(index).trim();
+        
+        if (input.charAt(0) == '"') {
+            // attribute value is a quoted string
+            begin = 1;
+            end = input.indexOf('"', begin);
+            if (end == -1) return null;
+        } else {
+            begin = 0;
+            end = input.indexOf(';');
+            if (end == -1) end = input.indexOf(' ');
+            if (end == -1) end = input.length();
+        }
+        return input.substring(begin, end).trim();
+    }
+    
+    /**
+     * Strips a servlet session ID from <tt>url</tt>.  The session ID
+     * is encoded as a URL "path parameter" beginning with "jsessionid=".
+     * We thus remove anything we find between ";jsessionid=" (inclusive)
+     * and either EOS or a subsequent ';' (exclusive).
+     * 
+     * taken from org.apache.taglibs.standard.tag.common.core.ImportSupport
+     */
+    public static String stripSession(String url) {
+        StringBuffer u = new StringBuffer(url);
+        int sessionStart;
+        while ((sessionStart = u.toString().indexOf(";" + Constants.SESSION_PARAMETER_NAME + "=")) != -1) {
+            int sessionEnd = u.toString().indexOf(";", sessionStart + 1);
+            if (sessionEnd == -1)
+                sessionEnd = u.toString().indexOf("?", sessionStart + 1);
+            if (sessionEnd == -1) 				// still
+                sessionEnd = u.length();
+            u.delete(sessionStart, sessionEnd);
+        }
+        return u.toString();
+    }
+    
+    
+    /**
+     * Performs the following substring replacements
+     * (to facilitate output to XML/HTML pages):
+     *
+     *    & -> &amp;
+     *    < -> &lt;
+     *    > -> &gt;
+     *    " -> &#034;
+     *    ' -> &#039;
+     *
+     * See also OutSupport.writeEscapedXml().
+     * 
+     * taken from org.apache.taglibs.standard.tag.common.core.Util
+     */
+    public static String escapeXml(String buffer) {
+        int start = 0;
+        int length = buffer.length();
+        char[] arrayBuffer = buffer.toCharArray();
+        StringBuffer escapedBuffer = null;
+        
+        for (int i = 0; i < length; i++) {
+            char c = arrayBuffer[i];
+            if (c <= HIGHEST_SPECIAL) {
+                char[] escaped = specialCharactersRepresentation[c];
+                if (escaped != null) {
+                    // create StringBuffer to hold escaped xml string
+                    if (start == 0) {
+                        escapedBuffer = new StringBuffer(length + 5);
+                    }
+                    // add unescaped portion
+                    if (start < i) {
+                        escapedBuffer.append(arrayBuffer,start,i-start);
+                    }
+                    start = i + 1;
+                    // add escaped xml
+                    escapedBuffer.append(escaped);
+                }
+            }
+        }
+        // no xml escaping was necessary
+        if (start == 0) {
+            return buffer;
+        }
+        // add rest of unescaped portion
+        if (start < length) {
+            escapedBuffer.append(arrayBuffer,start,length-start);
+        }
+        return escapedBuffer.toString();
+    }
+    
+    /** Utility methods
+     * taken from org.apache.taglibs.standard.tag.common.core.UrlSupport
+     */
+    public static String resolveUrl(
+            String url, String context, PageContext pageContext)
+    throws JspException {
+        // don't touch absolute URLs
+        if (isAbsoluteUrl(url))
+            return url;
+        
+        // normalize relative URLs against a context root
+        HttpServletRequest request =
+            (HttpServletRequest) pageContext.getRequest();
+        if (context == null) {
+            if (url.startsWith("/"))
+                return (request.getContextPath() + url);
+            else
+                return url;
+        } else {
+            if (!context.startsWith("/") || !url.startsWith("/")) {
+                throw new JspTagException(
+                "In URL tags, when the \"context\" attribute is specified, values of both \"context\" and \"url\" must start with \"/\".");
+            }
+            if (context.equals("/")) {
+                // Don't produce string starting with '//', many
+                // browsers interpret this as host name, not as
+                // path on same host.
+                return url;
+            } else {
+                return (context + url);
+            }
+        }
+    }
+    
+    /** Wraps responses to allow us to retrieve results as Strings. 
+     * mainly taken from org.apache.taglibs.standard.tag.common.core.importSupport 
+     */
+    public static class ImportResponseWrapper extends HttpServletResponseWrapper{
+        
+        private StringWriter sw = new StringWriter();
+        private ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        private ServletOutputStream sos = new ServletOutputStream() {
+            public void write(int b) throws IOException {
+                bos.write(b);
+            }
+        };
+        private boolean isWriterUsed;
+        private boolean isStreamUsed;
+        private int status = 200;
+        private String charEncoding;
+        
+        public ImportResponseWrapper(HttpServletResponse arg0) {
+            super(arg0);
+            // TODO Auto-generated constructor stub
+        }
+        
+        public PrintWriter getWriter() {
+            if (isStreamUsed)
+                throw new IllegalStateException("Unexpected internal error during &lt;import&gt: " +
+                "Target servlet called getWriter(), then getOutputStream()");
+            isWriterUsed = true;
+            return new PrintWriter(sw);
+        }
+        
+        public ServletOutputStream getOutputStream() {
+            if (isWriterUsed)
+                throw new IllegalStateException("Unexpected internal error during &lt;import&gt: " +
+                "Target servlet called getOutputStream(), then getWriter()");
+            isStreamUsed = true;
+            return sos;
+        }
+        
+        /** Has no effect. */
+        public void setContentType(String x) {
+            // ignore
+        }
+        
+        /** Has no effect. */
+        public void setLocale(Locale x) {
+            // ignore
+        }
+        
+        public void setStatus(int status) {
+            this.status = status;
+        }
+        
+        public int getStatus() {
+            return status;
+        }
+        
+        public String getCharEncoding(){
+            return this.charEncoding;
+        }
+        
+        public void setCharEncoding(String ce){
+            this.charEncoding = ce;
+        }
+        
+        public String getString() throws UnsupportedEncodingException {
+            if (isWriterUsed)
+                return sw.toString();
+            else if (isStreamUsed) {
+                if (this.charEncoding != null && !this.charEncoding.equals(""))
+                    return bos.toString(charEncoding);
+                else
+                    return bos.toString("ISO-8859-1");
+            } else
+                return "";		// target didn't write anything
+        }
+    }
+    
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Catch.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Catch.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Catch.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Catch.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,72 @@
+/*
+ * 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.apache.struts2.jasper.tagplugins.jstl.core;
+
+import org.apache.struts2.jasper.compiler.tagplugin.TagPlugin;
+import org.apache.struts2.jasper.compiler.tagplugin.TagPluginContext;
+
+public class Catch implements TagPlugin {
+    
+    public void doTag(TagPluginContext ctxt) {
+        
+        //flag for the existence of the var attribute
+        boolean hasVar = ctxt.isAttributeSpecified("var");
+        
+        //temp name for exception and caught
+        String exceptionName = ctxt.getTemporaryVariableName();
+        String caughtName = ctxt.getTemporaryVariableName();
+        
+        //main part to generate code
+        ctxt.generateJavaSource("boolean " + caughtName + " = false;");
+        ctxt.generateJavaSource("try{");
+        ctxt.generateBody();
+        ctxt.generateJavaSource("}");
+        
+        //do catch
+        ctxt.generateJavaSource("catch(Throwable " + exceptionName + "){");
+        
+        //if the var specified, the exception object should 
+        //be set to the attribute "var" defines in page scope 
+        if(hasVar){
+            String strVar = ctxt.getConstantAttribute("var");
+            ctxt.generateJavaSource("    pageContext.setAttribute(\"" + strVar + "\", " 
+                    + exceptionName + ", PageContext.PAGE_SCOPE);");
+        }
+        
+        //whenever there's exception caught, 
+        //the flag caught should be set true;
+        ctxt.generateJavaSource("    " + caughtName + " = true;");
+        ctxt.generateJavaSource("}");
+        
+        //do finally
+        ctxt.generateJavaSource("finally{");
+        
+        //if var specified, the attribute it defines 
+        //in page scope should be removed
+        if(hasVar){
+            String strVar = ctxt.getConstantAttribute("var");
+            ctxt.generateJavaSource("    if(!" + caughtName + "){");
+            ctxt.generateJavaSource("        pageContext.removeAttribute(\"" + strVar + "\", PageContext.PAGE_SCOPE);");
+            ctxt.generateJavaSource("    }");
+        }
+        
+        ctxt.generateJavaSource("}");
+    }
+    
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Choose.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Choose.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Choose.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/Choose.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,34 @@
+/*
+ * 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.apache.struts2.jasper.tagplugins.jstl.core;
+
+import org.apache.struts2.jasper.compiler.tagplugin.*;
+
+public final class Choose implements TagPlugin {
+    
+    public void doTag(TagPluginContext ctxt) {
+        
+        // Not much to do here, much of the work will be done in the
+        // containing tags, <c:when> and <c:otherwise>.
+        
+        ctxt.generateBody();
+        // See comments in When.java for the reason "}" is generated here.
+        ctxt.generateJavaSource("}");
+    }
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForEach.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForEach.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForEach.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForEach.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,344 @@
+/*
+ * 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.apache.struts2.jasper.tagplugins.jstl.core;
+
+import org.apache.struts2.jasper.compiler.tagplugin.*;
+
+public final class ForEach implements TagPlugin {
+    
+    private boolean hasVar, hasBegin, hasEnd, hasStep;
+    
+    public void doTag(TagPluginContext ctxt) {
+        
+        String index = null;
+        
+        boolean hasVarStatus = ctxt.isAttributeSpecified("varStatus");
+        if (hasVarStatus) {
+            ctxt.dontUseTagPlugin();
+            return;
+        }
+        
+        hasVar = ctxt.isAttributeSpecified("var");
+        hasBegin = ctxt.isAttributeSpecified("begin");
+        hasEnd = ctxt.isAttributeSpecified("end");
+        hasStep = ctxt.isAttributeSpecified("step");
+        
+        boolean hasItems = ctxt.isAttributeSpecified("items");
+        if (hasItems) {
+            doCollection(ctxt);
+            return;
+        }
+        
+        // We must have a begin and end attributes
+        index = ctxt.getTemporaryVariableName();
+        ctxt.generateJavaSource("for (int " + index + " = ");
+        ctxt.generateAttribute("begin");
+        ctxt.generateJavaSource("; " + index + " <= ");
+        ctxt.generateAttribute("end");
+        if (hasStep) {
+            ctxt.generateJavaSource("; " + index + "+=");
+            ctxt.generateAttribute("step");
+            ctxt.generateJavaSource(") {");
+        }
+        else {
+            ctxt.generateJavaSource("; " + index + "++) {");
+        }
+        
+        // If var is specified and the body contains an EL, then sycn
+        // the var attribute
+        if (hasVar /* && ctxt.hasEL() */) {
+            ctxt.generateJavaSource("_jspx_page_context.setAttribute(");
+            ctxt.generateAttribute("var");
+            ctxt.generateJavaSource(", String.valueOf(" + index + "));");
+        }
+        ctxt.generateBody();
+        ctxt.generateJavaSource("}");
+    }
+    
+    /**
+     * Generate codes for Collections
+     * The pseudo code is:
+     */
+    private void doCollection(TagPluginContext ctxt) {
+        
+        ctxt.generateImport("java.util.*");
+        generateIterators(ctxt);
+        
+        String itemsV = ctxt.getTemporaryVariableName();
+        ctxt.generateJavaSource("Object " + itemsV + "= ");
+        ctxt.generateAttribute("items");
+        ctxt.generateJavaSource(";");
+        
+        String indexV=null, beginV=null, endV=null, stepV=null;
+        if (hasBegin) {
+            beginV = ctxt.getTemporaryVariableName();
+            ctxt.generateJavaSource("int " + beginV + " = ");
+            ctxt.generateAttribute("begin");
+            ctxt.generateJavaSource(";");
+        }
+        if (hasEnd) {
+            indexV = ctxt.getTemporaryVariableName();
+            ctxt.generateJavaSource("int " + indexV + " = 0;");
+            endV = ctxt.getTemporaryVariableName();
+            ctxt.generateJavaSource("int " + endV + " = ");
+            ctxt.generateAttribute("end");
+            ctxt.generateJavaSource(";");
+        }
+        if (hasStep) {
+            stepV = ctxt.getTemporaryVariableName();
+            ctxt.generateJavaSource("int " + stepV + " = ");
+            ctxt.generateAttribute("step");
+            ctxt.generateJavaSource(";");
+        }
+        
+        String iterV = ctxt.getTemporaryVariableName();
+        ctxt.generateJavaSource("Iterator " + iterV + " = null;");
+        // Object[]
+        ctxt.generateJavaSource("if (" + itemsV + " instanceof Object[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((Object[])" + itemsV + ");");
+        // boolean[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof boolean[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((boolean[])" + itemsV + ");");
+        // byte[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof byte[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((byte[])" + itemsV + ");");
+        // char[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof char[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((char[])" + itemsV + ");");
+        // short[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof short[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((short[])" + itemsV + ");");
+        // int[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof int[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((int[])" + itemsV + ");");
+        // long[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof long[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((long[])" + itemsV + ");");
+        // float[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof float[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((float[])" + itemsV + ");");
+        // double[]
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof double[])");
+        ctxt.generateJavaSource(iterV + "=toIterator((double[])" + itemsV + ");");
+        
+        // Collection
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof Collection)");
+        ctxt.generateJavaSource(iterV + "=((Collection)" + itemsV + ").iterator();");
+        
+        // Iterator
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof Iterator)");
+        ctxt.generateJavaSource(iterV + "=(Iterator)" + itemsV + ";");
+        
+        // Enumeration
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof Enumeration)");
+        ctxt.generateJavaSource(iterV + "=toIterator((Enumeration)" + itemsV + ");");
+        
+        // Map
+        ctxt.generateJavaSource("else if (" + itemsV + " instanceof Map)");
+        ctxt.generateJavaSource(iterV + "=((Map)" + itemsV + ").entrySet().iterator();");
+        
+        if (hasBegin) {
+            String tV = ctxt.getTemporaryVariableName();
+            ctxt.generateJavaSource("for (int " + tV + "=" + beginV + ";" +
+                    tV + ">0 && " + iterV + ".hasNext(); " +
+                    tV + "--)");
+            ctxt.generateJavaSource(iterV + ".next();");
+        }
+        
+        ctxt.generateJavaSource("while (" + iterV + ".hasNext()){");
+        if (hasVar) {
+            ctxt.generateJavaSource("_jspx_page_context.setAttribute(");
+            ctxt.generateAttribute("var");
+            ctxt.generateJavaSource(", " + iterV + ".next());");
+        }
+        
+        ctxt.generateBody();
+        
+        if (hasStep) {
+            String tV = ctxt.getTemporaryVariableName();
+            ctxt.generateJavaSource("for (int " + tV + "=" + stepV + "-1;" +
+                    tV + ">0 && " + iterV + ".hasNext(); " +
+                    tV + "--)");
+            ctxt.generateJavaSource(iterV + ".next();");
+        }
+        if (hasEnd) {
+            if (hasStep) {
+                ctxt.generateJavaSource(indexV + "+=" + stepV + ";");
+            }
+            else {
+                ctxt.generateJavaSource(indexV + "++;");
+            }
+            if (hasBegin) {
+                ctxt.generateJavaSource("if(" + beginV + "+" + indexV +
+                        ">"+ endV + ")");
+            }
+            else {
+                ctxt.generateJavaSource("if(" + indexV + ">" + endV + ")");
+            }
+            ctxt.generateJavaSource("break;");
+        }
+        ctxt.generateJavaSource("}");	// while
+    }
+    
+    /**
+     * Generate iterators for data types supported in items
+     */
+    private void generateIterators(TagPluginContext ctxt) {
+        
+        // Object[]
+        ctxt.generateDeclaration("ObjectArrayIterator", 
+                "private Iterator toIterator(final Object[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return a[index++];}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // boolean[]
+        ctxt.generateDeclaration("booleanArrayIterator", 
+                "private Iterator toIterator(final boolean[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Boolean(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // byte[]
+        ctxt.generateDeclaration("byteArrayIterator", 
+                "private Iterator toIterator(final byte[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Byte(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // char[]
+        ctxt.generateDeclaration("charArrayIterator", 
+                "private Iterator toIterator(final char[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Character(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // short[]
+        ctxt.generateDeclaration("shortArrayIterator", 
+                "private Iterator toIterator(final short[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Short(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // int[]
+        ctxt.generateDeclaration("intArrayIterator", 
+                "private Iterator toIterator(final int[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Integer(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // long[]
+        ctxt.generateDeclaration("longArrayIterator", 
+                "private Iterator toIterator(final long[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Long(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // float[]
+        ctxt.generateDeclaration("floatArrayIterator",
+                "private Iterator toIterator(final float[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Float(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // double[]
+        ctxt.generateDeclaration("doubleArrayIterator",
+                "private Iterator toIterator(final double[] a){\n" +
+                "  return (new Iterator() {\n" +
+                "    int index=0;\n" +
+                "    public boolean hasNext() {\n" +
+                "      return index < a.length;}\n" +
+                "    public Object next() {\n" +
+                "      return new Double(a[index++]);}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+        // Enumeration
+        ctxt.generateDeclaration("enumIterator",
+                "private Iterator toIterator(final Enumeration e){\n" +
+                "  return (new Iterator() {\n" +
+                "    public boolean hasNext() {\n" +
+                "      return e.hasMoreElements();}\n" +
+                "    public Object next() {\n" +
+                "      return e.nextElement();}\n" +
+                "    public void remove() {}\n" +
+                "  });\n" +
+                "}"
+        );
+        
+    }
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForTokens.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForTokens.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForTokens.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/ForTokens.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,119 @@
+/*
+ * 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.apache.struts2.jasper.tagplugins.jstl.core;
+
+import org.apache.struts2.jasper.compiler.tagplugin.TagPlugin;
+import org.apache.struts2.jasper.compiler.tagplugin.TagPluginContext;
+
+public class ForTokens implements TagPlugin {
+    
+    public void doTag(TagPluginContext ctxt) {
+        boolean hasVar, hasVarStatus, hasBegin, hasEnd, hasStep;
+        
+        //init the flags
+        hasVar = ctxt.isAttributeSpecified("var");
+        hasVarStatus = ctxt.isAttributeSpecified("varStatus");
+        hasBegin = ctxt.isAttributeSpecified("begin");
+        hasEnd = ctxt.isAttributeSpecified("end");
+        hasStep = ctxt.isAttributeSpecified("step");
+        
+        if(hasVarStatus){
+            ctxt.dontUseTagPlugin();
+            return;
+        }
+        
+        //define all the temp variables' names
+        String itemsName = ctxt.getTemporaryVariableName();
+        String delimsName = ctxt.getTemporaryVariableName();
+        String stName = ctxt.getTemporaryVariableName();
+        String beginName = ctxt.getTemporaryVariableName();
+        String endName  = ctxt.getTemporaryVariableName();
+        String stepName = ctxt.getTemporaryVariableName();
+        String index = ctxt.getTemporaryVariableName();
+        String temp  = ctxt.getTemporaryVariableName();
+        String tokensCountName = ctxt.getTemporaryVariableName();
+        
+        //get the value of the "items" attribute 
+        ctxt.generateJavaSource("String " + itemsName + " = (String)");
+        ctxt.generateAttribute("items");
+        ctxt.generateJavaSource(";");
+        
+        //get the value of the "delim" attribute
+        ctxt.generateJavaSource("String " + delimsName + " = (String)");
+        ctxt.generateAttribute("delims");
+        ctxt.generateJavaSource(";");
+        
+        //new a StringTokenizer Object according to the "items" and the "delim"
+        ctxt.generateJavaSource("java.util.StringTokenizer " + stName + " = " +
+                "new java.util.StringTokenizer(" + itemsName + ", " + delimsName + ");");
+        
+        //if "begin" specified, move the token to the "begin" place
+        //and record the begin index. default begin place is 0.
+        ctxt.generateJavaSource("int " + tokensCountName + " = " + stName + ".countTokens();");
+        if(hasBegin){
+            ctxt.generateJavaSource("int " + beginName + " = "  );
+            ctxt.generateAttribute("begin");
+            ctxt.generateJavaSource(";");
+            ctxt.generateJavaSource("for(int " + index + " = 0; " + index + " < " + beginName + " && " + stName + ".hasMoreTokens(); " + index + "++, " + stName + ".nextToken()){}");
+        }else{
+            ctxt.generateJavaSource("int " + beginName + " = 0;");
+        }
+        
+        //when "end" is specified, if the "end" is more than the last index,
+        //record the end place as the last index, otherwise, record it as "end";
+        //default end place is the last index 
+        if(hasEnd){
+            ctxt.generateJavaSource("int " + endName + " = 0;"  );
+            ctxt.generateJavaSource("if((" + tokensCountName + " - 1) < ");
+            ctxt.generateAttribute("end");
+            ctxt.generateJavaSource("){");
+            ctxt.generateJavaSource("    " + endName + " = " + tokensCountName + " - 1;");
+            ctxt.generateJavaSource("}else{");
+            ctxt.generateJavaSource("    " + endName + " = ");
+            ctxt.generateAttribute("end");
+            ctxt.generateJavaSource(";}");
+        }else{
+            ctxt.generateJavaSource("int " + endName + " = " + tokensCountName + " - 1;");
+        }
+        
+        //get the step value from "step" if specified.
+        //default step value is 1.
+        if(hasStep){
+            ctxt.generateJavaSource("int " + stepName + " = "  );
+            ctxt.generateAttribute("step");
+            ctxt.generateJavaSource(";");
+        }else{
+            ctxt.generateJavaSource("int " + stepName + " = 1;");
+        }
+        
+        //the loop
+        ctxt.generateJavaSource("for(int " + index + " = " + beginName + "; " + index + " <= " + endName + "; " + index + "++){");
+        ctxt.generateJavaSource("    String " + temp + " = " + stName + ".nextToken();");
+        ctxt.generateJavaSource("    if(((" + index + " - " + beginName + ") % " + stepName + ") == 0){");
+        //if var specified, put the current token into the attribute "var" defines.
+        if(hasVar){
+            String strVar = ctxt.getConstantAttribute("var");
+            ctxt.generateJavaSource("        pageContext.setAttribute(\"" + strVar + "\", " + temp + ");");
+        }
+        ctxt.generateBody();
+        ctxt.generateJavaSource("    }");
+        ctxt.generateJavaSource("}");
+    }
+    
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/If.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/If.java?rev=819444&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/If.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/tagplugins/jstl/core/If.java Mon Sep 28 01:55:26 2009
@@ -0,0 +1,50 @@
+/*
+ * 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.apache.struts2.jasper.tagplugins.jstl.core;
+
+import org.apache.struts2.jasper.compiler.tagplugin.*;
+
+public final class If implements TagPlugin {
+    
+    public void doTag(TagPluginContext ctxt) {
+        String condV = ctxt.getTemporaryVariableName();
+        ctxt.generateJavaSource("boolean " + condV + "=");
+        ctxt.generateAttribute("test");
+        ctxt.generateJavaSource(";");
+        if (ctxt.isAttributeSpecified("var")) {
+            String scope = "PageContext.PAGE_SCOPE";
+            if (ctxt.isAttributeSpecified("scope")) {
+                String scopeStr = ctxt.getConstantAttribute("scope");
+                if ("request".equals(scopeStr)) {
+                    scope = "PageContext.REQUEST_SCOPE";
+                } else if ("session".equals(scopeStr)) {
+                    scope = "PageContext.SESSION_SCOPE";
+                } else if ("application".equals(scopeStr)) {
+                    scope = "PageContext.APPLICATION_SCOPE";
+                }
+            }
+            ctxt.generateJavaSource("_jspx_page_context.setAttribute(");
+            ctxt.generateAttribute("var");
+            ctxt.generateJavaSource(", new Boolean(" + condV + ")," + scope + ");");
+        }
+        ctxt.generateJavaSource("if (" + condV + "){");
+        ctxt.generateBody();
+        ctxt.generateJavaSource("}");
+    }
+}