alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Struts example source code file (JSPLoader.java)

This example Struts source code file (JSPLoader.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Struts tags/keywords

classloaderinterface, classloaderinterface, compiling, io, ioexception, jspc, jspc, memoryjavafileobject, net, network, regex, security, servlet, servlet, simplejavafileobject, string, string, uri, url, util

The Struts JSPLoader.java source code

/*
 * $Id$
 *
 * 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;

import com.opensymphony.xwork2.util.URLUtil;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate;
import com.opensymphony.xwork2.util.finder.UrlSet;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.struts2.compiler.MemoryClassLoader;
import org.apache.struts2.compiler.MemoryJavaFileObject;
import org.apache.struts2.jasper.JasperException;
import org.apache.struts2.jasper.JspC;

import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.jsp.JspPage;
import javax.tools.DiagnosticCollector;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Uses jasper to extract a JSP from the classpath to a file and compile it. The classpath used for
 * compilation is built by finding all the jar files using the current class loader (Thread), plus
 * directories.
 */
public class JSPLoader {
    private static final Logger LOG = LoggerFactory.getLogger(JSPLoader.class);

    private static MemoryClassLoader classLoader = new MemoryClassLoader();
    private static final String DEFAULT_PACKAGE = "org.apache.struts2.jsp";

    private static final Pattern PACKAGE_PATTERN = Pattern.compile("package (.*?);");
    private static final Pattern CLASS_PATTERN = Pattern.compile("public final class (.*?) ");

    public Servlet load(String location) throws Exception {
        location = StringUtils.substringBeforeLast(location, "?");

        if (LOG.isDebugEnabled()) {
            LOG.debug("Compiling JSP [#0]", location);
        }

        //use Jasper to compile the JSP into java code
        JspC jspC = compileJSP(location);
        String source = jspC.getSourceCode();

        //System.out.print(source);

        String className = extractClassName(source);

        //use Java Compiler API to compile the java code into a class
        //the tlds that were discovered are added (their jars) to the classpath
        compileJava(className, source, jspC.getTldAbsolutePaths());

        //load the class that was just built
        Class clazz = Class.forName(className, false, classLoader);
        return createServlet(clazz);
    }

    private String extractClassName(String source) {
        Matcher matcher = PACKAGE_PATTERN.matcher(source);
        matcher.find();
        String packageName = matcher.group(1);

        matcher = CLASS_PATTERN.matcher(source);
        matcher.find();
        String className = matcher.group(1);

        return packageName + "." + className;
    }

    /**
     * Creates and inits a servlet
     */
    private Servlet createServlet(Class clazz) throws IllegalAccessException, InstantiationException, ServletException {
        JSPServletConfig config = new JSPServletConfig(ServletActionContext.getServletContext());

        Servlet servlet = (Servlet) clazz.newInstance();
        servlet.init(config);

        /*
         there is no need to call JspPage.init explicitly because Jasper's
         JSP base classe HttpJspBase.init(ServletConfig) calls:
         jspInit();
         _jspInit();
         */

        return servlet;
    }

    /**
     * Compiles the given source code into java bytecode
     */
    private void compileJava(String className, final String source, Set<String> extraClassPath) throws IOException {
        if (LOG.isTraceEnabled())
            LOG.trace("Compiling [#0], source: [#1]", className, source);

        JavaCompiler compiler =
                ToolProvider.getSystemJavaCompiler();

        DiagnosticCollector<JavaFileObject> diagnostics =
                new DiagnosticCollector<JavaFileObject>();

        //the generated bytecode is fed to the class loader
        JavaFileManager jfm = new
                ForwardingJavaFileManager<StandardJavaFileManager>(
                        compiler.getStandardFileManager(diagnostics, null, null)) {

                    @Override
                    public JavaFileObject getJavaFileForOutput(Location location,
                                                               String name,
                                                               JavaFileObject.Kind kind,
                                                               FileObject sibling) throws IOException {
                        MemoryJavaFileObject fileObject = new MemoryJavaFileObject(name, kind);
                        classLoader.addMemoryJavaFileObject(name, fileObject);
                        return fileObject;
                    }
                };

        //read java source code from memory
        String fileName = className.replace('.', '/') + ".java";
        SimpleJavaFileObject sourceCodeObject = new SimpleJavaFileObject(toURI(fileName), JavaFileObject.Kind.SOURCE) {
            @Override
            public CharSequence getCharContent(boolean
                    ignoreEncodingErrors)
                    throws IOException, IllegalStateException,
                    UnsupportedOperationException {
                return source;
            }

        };

        //build classpath
        //some entries will be added multiple times, hence the set
        List<String> optionList = new ArrayList();
        Set<String> classPath = new HashSet();

        //find available jars
        ClassLoaderInterface classLoaderInterface = getClassLoaderInterface();
        UrlSet urlSet = new UrlSet(classLoaderInterface);

        //find jars
        List<URL> urls = urlSet.getUrls();

        for (URL url : urls) {
            URL normalizedUrl = URLUtil.normalizeToFileProtocol(url);
            File file = FileUtils.toFile((URL) ObjectUtils.defaultIfNull(normalizedUrl, url));
            if (file.exists())
                classPath.add(file.getAbsolutePath());
        }

        //these should be in the list already, but I am feeling paranoid
        //this jar
        classPath.add(getJarUrl(EmbeddedJSPResult.class));
        //servlet api
        classPath.add(getJarUrl(Servlet.class));
        //jsp api
        classPath.add(getJarUrl(JspPage.class));

        try {
            Class annotationsProcessor = Class.forName("org.apache.AnnotationProcessor");
            classPath.add(getJarUrl(annotationsProcessor));
        } catch (ClassNotFoundException e) {
            //ok ignore
        }

        //add extra classpath entries (jars where tlds were found will be here)
        for (Iterator<String> iterator = extraClassPath.iterator(); iterator.hasNext();) {
            String entry = iterator.next();
            classPath.add(entry);
        }

        String classPathString = StringUtils.join(classPath, File.pathSeparator);
        if (LOG.isDebugEnabled()) {
            LOG.debug("Compiling [#0] with classpath [#1]", className, classPathString);
        }

        optionList.addAll(Arrays.asList("-classpath", classPathString));

        //compile
        JavaCompiler.CompilationTask task = compiler.getTask(
                null, jfm, diagnostics, optionList, null,
                Arrays.asList(sourceCodeObject));

        if (!task.call()) {
            throw new StrutsException("Compilation failed:" + diagnostics.getDiagnostics().get(0).toString());
        }
    }

    protected String getJarUrl(Class clazz) {
        ProtectionDomain protectionDomain = clazz.getProtectionDomain();
        CodeSource codeSource = protectionDomain.getCodeSource();
        URL loc = codeSource.getLocation();
        File file = FileUtils.toFile(loc);
        return file.getAbsolutePath();
    }

    private JspC compileJSP(String location) throws JasperException {
        JspC jspC = new JspC();
        jspC.setClassLoaderInterface(getClassLoaderInterface());
        jspC.setCompile(false);
        jspC.setJspFiles(location);
        jspC.setPackage(DEFAULT_PACKAGE);
        jspC.execute();
        return jspC;
    }

    private ClassLoaderInterface getClassLoaderInterface() {
        ClassLoaderInterface classLoaderInterface = null;
        ServletContext ctx = ServletActionContext.getServletContext();
        if (ctx != null)
            classLoaderInterface = (ClassLoaderInterface) ctx.getAttribute(ClassLoaderInterface.CLASS_LOADER_INTERFACE);

        return (ClassLoaderInterface) ObjectUtils.defaultIfNull(classLoaderInterface, new ClassLoaderInterfaceDelegate(JSPLoader.class.getClassLoader()));
    }

    private static URI toURI(String name) {
        try {
            return new URI(name);
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }
}

Other Struts examples (source code examples)

Here is a short list of links related to this Struts JSPLoader.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.