This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-jspc-maven-plugin.git
commit e1c0e69084597660c528a5466f0e0198797da091 Author: Carsten Ziegeler <[email protected]> AuthorDate: Fri Jun 2 04:39:26 2017 +0000 SLING-6923 : Update JSPC plugin to support java 1.8. Apply patch from Tobias Bocanegra git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1797317 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 31 +- .../sling/maven/jspc/JspCClassLoaderWriter.java | 82 ++++ .../apache/sling/maven/jspc/JspCIOProvider.java | 96 ++++ .../sling/maven/jspc/JspCServletContext.java | 189 ++++++- .../sling/maven/jspc/JspCTldLocationsCache.java | 545 +++++++++++++++++++++ .../java/org/apache/sling/maven/jspc/JspcMojo.java | 237 +++------ 6 files changed, 1000 insertions(+), 180 deletions(-) diff --git a/pom.xml b/pom.xml index 97e41a1..bb33853 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ <parent> <groupId>org.apache.sling</groupId> <artifactId>sling</artifactId> - <version>26</version> + <version>30</version> <relativePath/> </parent> @@ -89,13 +89,24 @@ <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.scripting.jsp</artifactId> - <version>2.0.18</version> + <version>2.3.0</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.compiler</artifactId> + <version>2.3.0</version> + <scope>compile</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.commons.classloader</artifactId> + <version>1.4.0</version> <scope>compile</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> - <artifactId>servlet-api</artifactId> - <version>2.5</version> + <artifactId>javax.servlet-api</artifactId> <scope>compile</scope> </dependency> <dependency> @@ -108,7 +119,7 @@ <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> - <version>1.4</version> + <version>2.5</version> <scope>compile</scope> </dependency> @@ -122,36 +133,34 @@ <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-plugin-api</artifactId> - <version>2.0</version> + <version>3.5.0</version> </dependency> <dependency> <groupId>org.apache.maven</groupId> <artifactId>maven-archiver</artifactId> - <version>2.0</version> + <version>3.1.1</version> </dependency> <dependency> <groupId>org.osgi</groupId> - <artifactId>org.osgi.core</artifactId> + <artifactId>osgi.core</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> - <version>1.5.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> - <version>1.5.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.apache.maven.plugin-tools</groupId> <artifactId>maven-plugin-annotations</artifactId> - <version>3.4</version> + <version>3.5</version> <scope>provided</scope> </dependency> </dependencies> diff --git a/src/main/java/org/apache/sling/maven/jspc/JspCClassLoaderWriter.java b/src/main/java/org/apache/sling/maven/jspc/JspCClassLoaderWriter.java new file mode 100644 index 0000000..231c896 --- /dev/null +++ b/src/main/java/org/apache/sling/maven/jspc/JspCClassLoaderWriter.java @@ -0,0 +1,82 @@ +/* + * 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.sling.maven.jspc; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.io.FileUtils; +import org.apache.sling.commons.classloader.ClassLoaderWriter; + +/** + * Implements the class loader writer + */ +public class JspCClassLoaderWriter implements ClassLoaderWriter { + + private final ClassLoader loader; + + private final File rootDirectory; + + JspCClassLoaderWriter(ClassLoader loader, File rootDirectory) { + this.loader = loader; + this.rootDirectory = rootDirectory; + } + + private File getFile(String fileName) { + return new File(rootDirectory, fileName); + } + + @Override + public OutputStream getOutputStream(String fileName) { + try { + return FileUtils.openOutputStream(getFile(fileName)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public InputStream getInputStream(String fileName) throws IOException { + return FileUtils.openInputStream(getFile(fileName)); + } + + @Override + public long getLastModified(String fileName) { + File file = getFile(fileName); + if (file.exists()) { + return file.lastModified(); + } + return -1L; + } + + @Override + public boolean delete(String fileName) { + return getFile(fileName).delete(); + } + + @Override + public boolean rename(String oldFileName, String newFileName) { + return getFile(oldFileName).renameTo(getFile(newFileName)); + } + + @Override + public ClassLoader getClassLoader() { + return loader; + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/maven/jspc/JspCIOProvider.java b/src/main/java/org/apache/sling/maven/jspc/JspCIOProvider.java new file mode 100644 index 0000000..748d25c --- /dev/null +++ b/src/main/java/org/apache/sling/maven/jspc/JspCIOProvider.java @@ -0,0 +1,96 @@ +/* + * 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.sling.maven.jspc; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.commons.io.FileUtils; +import org.apache.sling.commons.classloader.ClassLoaderWriter; +import org.apache.sling.commons.compiler.JavaCompiler; +import org.apache.sling.scripting.jsp.jasper.IOProvider; + +/** + * Implements the IOProvider for the JSPC plugin + */ +public class JspCIOProvider implements IOProvider { + + private final ClassLoader loader; + + private final JavaCompiler compiler; + + private final ClassLoaderWriter writer; + + JspCIOProvider(ClassLoader loader, JavaCompiler compiler, ClassLoaderWriter writer) { + this.loader = loader; + this.compiler = compiler; + this.writer = writer; + } + + private File getFile(String fileName) { + // TODO: sanity check to not write above project directory? + return new File(fileName); + } + + @Override + public OutputStream getOutputStream(String fileName) throws IOException { + return FileUtils.openOutputStream(getFile(fileName)); + } + + @Override + public InputStream getInputStream(String fileName) throws FileNotFoundException, IOException { + return FileUtils.openInputStream(getFile(fileName)); + } + + @Override + public boolean delete(String fileName) { + return getFile(fileName).delete(); + } + + @Override + public boolean rename(String oldFileName, String newFileName) { + return getFile(oldFileName).renameTo(getFile(newFileName)); + } + + @Override + public boolean mkdirs(String path) { + return getFile(path).mkdirs(); + } + + @Override + public long lastModified(String fileName) { + return getFile(fileName).lastModified(); + } + + @Override + public ClassLoader getClassLoader() { + return loader; + } + + @Override + public JavaCompiler getJavaCompiler() { + return compiler; + } + + @Override + public ClassLoaderWriter getClassLoaderWriter() { + return writer; + } +} \ No newline at end of file diff --git a/src/main/java/org/apache/sling/maven/jspc/JspCServletContext.java b/src/main/java/org/apache/sling/maven/jspc/JspCServletContext.java index bf4897e..84effd1 100644 --- a/src/main/java/org/apache/sling/maven/jspc/JspCServletContext.java +++ b/src/main/java/org/apache/sling/maven/jspc/JspCServletContext.java @@ -21,14 +21,24 @@ import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; +import java.util.EventListener; import java.util.HashSet; import java.util.Hashtable; +import java.util.Map; import java.util.Set; import java.util.Vector; +import javax.servlet.Filter; +import javax.servlet.FilterRegistration; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.ServletRegistration.Dynamic; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import javax.servlet.descriptor.JspConfigDescriptor; import org.apache.maven.plugin.logging.Log; @@ -75,7 +85,7 @@ public class JspCServletContext implements ServletContext { * @param resourceBaseURL Resource base URL */ public JspCServletContext(Log log, URL resourceBaseURL) { - attributes = new Hashtable<String, Object>(); + attributes = new Hashtable<>(); this.log = log; this.resourceBaseURL = resourceBaseURL; } @@ -87,6 +97,7 @@ public class JspCServletContext implements ServletContext { * * @param name Name of the requested attribute */ + @Override public Object getAttribute(String name) { return attributes.get(name); } @@ -94,6 +105,7 @@ public class JspCServletContext implements ServletContext { /** * Return an enumeration of context attribute names. */ + @Override public Enumeration<String> getAttributeNames() { return attributes.keys(); } @@ -103,6 +115,7 @@ public class JspCServletContext implements ServletContext { * * @param uripath Server-relative path starting with '/' */ + @Override public ServletContext getContext(String uripath) { return null; } @@ -112,6 +125,7 @@ public class JspCServletContext implements ServletContext { * * @param name Name of the requested parameter */ + @Override public String getInitParameter(String name) { return null; } @@ -119,6 +133,7 @@ public class JspCServletContext implements ServletContext { /** * Return an enumeration of the names of context initialization parameters. */ + @Override public Enumeration<String> getInitParameterNames() { return new Vector<String>().elements(); } @@ -126,8 +141,9 @@ public class JspCServletContext implements ServletContext { /** * Return the Servlet API major version number. */ + @Override public int getMajorVersion() { - return 2; + return 3; } /** @@ -135,6 +151,7 @@ public class JspCServletContext implements ServletContext { * * @param file Filename whose MIME type is requested */ + @Override public String getMimeType(String file) { return null; } @@ -142,8 +159,9 @@ public class JspCServletContext implements ServletContext { /** * Return the Servlet API minor version number. */ + @Override public int getMinorVersion() { - return 3; + return 1; } /** @@ -151,6 +169,7 @@ public class JspCServletContext implements ServletContext { * * @param name Name of the requested servlet */ + @Override public RequestDispatcher getNamedDispatcher(String name) { return null; } @@ -160,6 +179,7 @@ public class JspCServletContext implements ServletContext { * * @param path The context-relative virtual path to resolve */ + @Override public String getRealPath(String path) { if (!resourceBaseURL.getProtocol().equals("file")) { return null; @@ -181,6 +201,7 @@ public class JspCServletContext implements ServletContext { * * @param path Context-relative path for which to acquire a dispatcher */ + @Override public RequestDispatcher getRequestDispatcher(String path) { return null; } @@ -193,6 +214,7 @@ public class JspCServletContext implements ServletContext { * @exception MalformedURLException if the resource path is not properly * formed */ + @Override public URL getResource(String path) throws MalformedURLException { // catch for dummy web.xml @@ -229,6 +251,7 @@ public class JspCServletContext implements ServletContext { * * @param path Context-relative path of the desired resource */ + @Override public InputStream getResourceAsStream(String path) { try { return getResource(path).openStream(); @@ -243,8 +266,9 @@ public class JspCServletContext implements ServletContext { * * @param path Context-relative base path */ + @Override public Set<String> getResourcePaths(String path) { - Set<String> thePaths = new HashSet<String>(); + Set<String> thePaths = new HashSet<>(); if (!path.endsWith("/")) { path += "/"; @@ -276,6 +300,7 @@ public class JspCServletContext implements ServletContext { /** * Return descriptive information about this server. */ + @Override public String getServerInfo() { return "JspCServletContext/1.0"; } @@ -286,6 +311,7 @@ public class JspCServletContext implements ServletContext { * @param name Name of the requested servlet * @deprecated This method has been deprecated with no replacement */ + @Override @Deprecated public Servlet getServlet(String name) { return null; @@ -294,6 +320,7 @@ public class JspCServletContext implements ServletContext { /** * Return the name of this servlet context. */ + @Override public String getServletContextName() { return getServerInfo(); } @@ -301,6 +328,7 @@ public class JspCServletContext implements ServletContext { /** * Return "/" as the context path for compilation. */ + @Override public String getContextPath() { return "/"; } @@ -310,6 +338,7 @@ public class JspCServletContext implements ServletContext { * * @deprecated This method has been deprecated with no replacement */ + @Override @Deprecated public Enumeration<String> getServletNames() { return new Vector<String>().elements(); @@ -320,6 +349,7 @@ public class JspCServletContext implements ServletContext { * * @deprecated This method has been deprecated with no replacement */ + @Override @Deprecated public Enumeration<Servlet> getServlets() { return new Vector<Servlet>().elements(); @@ -330,6 +360,7 @@ public class JspCServletContext implements ServletContext { * * @param message The message to be logged */ + @Override public void log(String message) { log.info(message); } @@ -341,6 +372,7 @@ public class JspCServletContext implements ServletContext { * @param message The message to be logged * @deprecated Use log(String,Throwable) instead */ + @Override @Deprecated public void log(Exception exception, String message) { this.log(message, exception); @@ -352,6 +384,7 @@ public class JspCServletContext implements ServletContext { * @param message The message to be logged * @param exception The exception to be logged */ + @Override public void log(String message, Throwable exception) { log.error(message, exception); } @@ -361,6 +394,7 @@ public class JspCServletContext implements ServletContext { * * @param name Name of the attribute to remove */ + @Override public void removeAttribute(String name) { attributes.remove(name); } @@ -371,7 +405,154 @@ public class JspCServletContext implements ServletContext { * @param name Name of the context attribute to set * @param value Corresponding attribute value */ + @Override public void setAttribute(String name, Object value) { attributes.put(name, value); } + + @Override + public int getEffectiveMajorVersion() { + return this.getMinorVersion(); + } + + @Override + public int getEffectiveMinorVersion() { + return this.getMajorVersion(); + } + + @Override + public boolean setInitParameter(String name, String value) { + throw new IllegalStateException(); + } + + @Override + public Dynamic addServlet(String servletName, String className) { + throw new IllegalStateException(); + } + + @Override + public Dynamic addServlet(String servletName, Servlet servlet) { + throw new IllegalStateException(); + } + + @Override + public Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) { + throw new IllegalStateException(); + } + + @Override + public <T extends Servlet> T createServlet(Class<T> clazz) throws ServletException { + throw new IllegalStateException(); + } + + @Override + public ServletRegistration getServletRegistration(String servletName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map<String, ? extends ServletRegistration> getServletRegistrations() { + // TODO Auto-generated method stub + return null; + } + + @Override + public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, String className) { + throw new IllegalStateException(); + } + + @Override + public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, Filter filter) { + throw new IllegalStateException(); + } + + @Override + public javax.servlet.FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) { + throw new IllegalStateException(); + } + + @Override + public <T extends Filter> T createFilter(Class<T> clazz) throws ServletException { + throw new IllegalStateException(); + } + + @Override + public FilterRegistration getFilterRegistration(String filterName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map<String, ? extends FilterRegistration> getFilterRegistrations() { + // TODO Auto-generated method stub + return null; + } + + @Override + public SessionCookieConfig getSessionCookieConfig() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) { + // TODO Auto-generated method stub + + } + + @Override + public Set<SessionTrackingMode> getDefaultSessionTrackingModes() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void addListener(String className) { + throw new IllegalStateException(); + } + + @Override + public <T extends EventListener> void addListener(T t) { + throw new IllegalStateException(); + } + + @Override + public void addListener(Class<? extends EventListener> listenerClass) { + throw new IllegalStateException(); + } + + @Override + public <T extends EventListener> T createListener(Class<T> clazz) throws ServletException { + throw new IllegalStateException(); + } + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ClassLoader getClassLoader() { + // TODO Auto-generated method stub + return this.getClass().getClassLoader(); + } + + @Override + public void declareRoles(String... roleNames) { + throw new IllegalStateException(); + } + + @Override + public String getVirtualServerName() { + // TODO Auto-generated method stub + return null; + } } diff --git a/src/main/java/org/apache/sling/maven/jspc/JspCTldLocationsCache.java b/src/main/java/org/apache/sling/maven/jspc/JspCTldLocationsCache.java new file mode 100644 index 0000000..5bb5e87 --- /dev/null +++ b/src/main/java/org/apache/sling/maven/jspc/JspCTldLocationsCache.java @@ -0,0 +1,545 @@ +/* + * 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.sling.maven.jspc; + +import java.io.InputStream; +import java.net.JarURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLConnection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import javax.servlet.ServletContext; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.sling.scripting.jsp.jasper.Constants; +import org.apache.sling.scripting.jsp.jasper.JasperException; +import org.apache.sling.scripting.jsp.jasper.compiler.Localizer; +import org.apache.sling.scripting.jsp.jasper.compiler.TldLocationsCache; +import org.apache.sling.scripting.jsp.jasper.xmlparser.ParserUtils; +import org.apache.sling.scripting.jsp.jasper.xmlparser.TreeNode; +import org.xml.sax.InputSource; + +/** + * A container for all tag libraries that are defined "globally" + * for the web application. + * + * Tag Libraries can be defined globally in one of two ways: + * 1. Via <taglib> elements in web.xml: + * the uri and location of the tag-library are specified in + * the <taglib> element. + * 2. Via packaged jar files that contain .tld files + * within the META-INF directory, or some subdirectory + * of it. The taglib is 'global' if it has the <uri> + * element defined. + * + * A mapping between the taglib URI and its associated TaglibraryInfoImpl + * is maintained in this container. + * Actually, that's what we'd like to do. However, because of the + * way the classes TagLibraryInfo and TagInfo have been defined, + * it is not currently possible to share an instance of TagLibraryInfo + * across page invocations. A bug has been submitted to the spec lead. + * In the mean time, all we do is save the 'location' where the + * TLD associated with a taglib URI can be found. + * + * When a JSP page has a taglib directive, the mappings in this container + * are first searched (see method getLocation()). + * If a mapping is found, then the location of the TLD is returned. + * If no mapping is found, then the uri specified + * in the taglib directive is to be interpreted as the location for + * the TLD of this tag library. + * + * @author Pierre Delisle + * @author Jan Luehe + */ + +public class JspCTldLocationsCache extends TldLocationsCache { + + // Logger + private Log log = LogFactory.getLog(TldLocationsCache.class); + + /** + * The types of URI one may specify for a tag library + */ + public static final int ABS_URI = 0; + public static final int ROOT_REL_URI = 1; + public static final int NOROOT_REL_URI = 2; + + private static final String WEB_XML = "/WEB-INF/web.xml"; + private static final String FILE_PROTOCOL = "file:"; + private static final String JAR_FILE_SUFFIX = ".jar"; + + // Names of JARs that are known not to contain any TLDs + private static HashSet<String> noTldJars; + + /** + * The mapping of the 'global' tag library URI to the location (resource + * path) of the TLD associated with that tag library. The location is + * returned as a String array: + * [0] The location + * [1] If the location is a jar file, this is the location of the tld. + */ + private Map<String, String[]> mappings; + + private boolean initialized; + private ServletContext ctxt; + private boolean redeployMode; + + private final ClassLoader webappLoader; + + //********************************************************************* + // Constructor and Initilizations + + /* + * Initializes the set of JARs that are known not to contain any TLDs + */ + static { + noTldJars = new HashSet<String>(); + // Bootstrap JARs + noTldJars.add("bootstrap.jar"); + noTldJars.add("commons-daemon.jar"); + noTldJars.add("tomcat-juli.jar"); + // Main JARs + noTldJars.add("annotations-api.jar"); + noTldJars.add("catalina.jar"); + noTldJars.add("catalina-ant.jar"); + noTldJars.add("catalina-ha.jar"); + noTldJars.add("catalina-tribes.jar"); + noTldJars.add("el-api.jar"); + noTldJars.add("jasper.jar"); + noTldJars.add("jasper-el.jar"); + noTldJars.add("jasper-jdt.jar"); + noTldJars.add("jsp-api.jar"); + noTldJars.add("servlet-api.jar"); + noTldJars.add("tomcat-coyote.jar"); + noTldJars.add("tomcat-dbcp.jar"); + // i18n JARs + noTldJars.add("tomcat-i18n-en.jar"); + noTldJars.add("tomcat-i18n-es.jar"); + noTldJars.add("tomcat-i18n-fr.jar"); + noTldJars.add("tomcat-i18n-ja.jar"); + // Misc JARs not included with Tomcat + noTldJars.add("ant.jar"); + noTldJars.add("commons-dbcp.jar"); + noTldJars.add("commons-beanutils.jar"); + noTldJars.add("commons-fileupload-1.0.jar"); + noTldJars.add("commons-pool.jar"); + noTldJars.add("commons-digester.jar"); + noTldJars.add("commons-logging.jar"); + noTldJars.add("commons-collections.jar"); + noTldJars.add("jmx.jar"); + noTldJars.add("jmx-tools.jar"); + noTldJars.add("xercesImpl.jar"); + noTldJars.add("xmlParserAPIs.jar"); + noTldJars.add("xml-apis.jar"); + // JARs from J2SE runtime + noTldJars.add("sunjce_provider.jar"); + noTldJars.add("ldapsec.jar"); + noTldJars.add("localedata.jar"); + noTldJars.add("dnsns.jar"); + noTldJars.add("tools.jar"); + noTldJars.add("sunpkcs11.jar"); + } + + /** Constructor. + * + * @param ctxt the servlet context of the web application in which Jasper + * is running + * @param redeployMode if true, then the compiler will allow redeploying + * a tag library from the same jar, at the expense of slowing down the + * server a bit. Note that this may only work on JDK 1.3.1_01a and later, + * because of JDK bug 4211817 fixed in this release. + * If redeployMode is false, a faster but less capable mode will be used. + */ + public JspCTldLocationsCache(ServletContext ctxt, boolean redeployMode, ClassLoader loader) { + this.ctxt = ctxt; + this.redeployMode = redeployMode; + mappings = new HashMap<String, String[]>(); + initialized = false; + this.webappLoader = loader; + } + + /** + * Sets the list of JARs that are known not to contain any TLDs. + * + * @param jarNames List of comma-separated names of JAR files that are + * known not to contain any TLDs + */ + public static void setNoTldJars(String jarNames) { + if (jarNames != null) { + noTldJars.clear(); + StringTokenizer tokenizer = new StringTokenizer(jarNames, ","); + while (tokenizer.hasMoreElements()) { + noTldJars.add(tokenizer.nextToken()); + } + } + } + + /** + * Gets the 'location' of the TLD associated with the given taglib 'uri'. + * + * Returns null if the uri is not associated with any tag library 'exposed' + * in the web application. A tag library is 'exposed' either explicitly in + * web.xml or implicitly via the uri tag in the TLD of a taglib deployed + * in a jar file (WEB-INF/lib). + * + * @param uri The taglib uri + * + * @return An array of two Strings: The first element denotes the real + * path to the TLD. If the path to the TLD points to a jar file, then the + * second element denotes the name of the TLD entry in the jar file. + * Returns null if the uri is not associated with any tag library 'exposed' + * in the web application. + */ + public String[] getLocation(String uri) throws JasperException { + if (!initialized) { + init(); + } + return mappings.get(uri); + } + + /** + * Returns the type of a URI: + * ABS_URI + * ROOT_REL_URI + * NOROOT_REL_URI + */ + public static int uriType(String uri) { + if (uri.indexOf(':') != -1) { + return ABS_URI; + } else if (uri.startsWith("/")) { + return ROOT_REL_URI; + } else { + return NOROOT_REL_URI; + } + } + + private void init() throws JasperException { + if (initialized) return; + try { + processWebDotXml(); + scanJars(); + processTldsInFileSystem("/WEB-INF/"); + initialized = true; + } catch (Exception ex) { + throw new JasperException(Localizer.getMessage( + "jsp.error.internal.tldinit", ex.getMessage())); + } + } + + /* + * Populates taglib map described in web.xml. + */ + private void processWebDotXml() throws Exception { + + InputStream is = null; + + try { + // Acquire input stream to web application deployment descriptor + String altDDName = (String)ctxt.getAttribute( + Constants.ALT_DD_ATTR); + URL uri = null; + if (altDDName != null) { + try { + uri = new URL(FILE_PROTOCOL+altDDName.replace('\\', '/')); + } catch (MalformedURLException e) { + if (log.isWarnEnabled()) { + log.warn(Localizer.getMessage( + "jsp.error.internal.filenotfound", + altDDName)); + } + } + } else { + uri = ctxt.getResource(WEB_XML); + if (uri == null && log.isWarnEnabled()) { + log.warn(Localizer.getMessage( + "jsp.error.internal.filenotfound", + WEB_XML)); + } + } + + if (uri == null) { + return; + } + is = uri.openStream(); + InputSource ip = new InputSource(is); + ip.setSystemId(uri.toExternalForm()); + + // Parse the web application deployment descriptor + TreeNode webtld; + // altDDName is the absolute path of the DD + if (altDDName != null) { + webtld = new ParserUtils().parseXMLDocument(altDDName, ip); + } else { + webtld = new ParserUtils().parseXMLDocument(WEB_XML, ip); + } + + // Allow taglib to be an element of the root or jsp-config (JSP2.0) + TreeNode jspConfig = webtld.findChild("jsp-config"); + if (jspConfig != null) { + webtld = jspConfig; + } + Iterator taglibs = webtld.findChildren("taglib"); + while (taglibs.hasNext()) { + + // Parse the next <taglib> element + TreeNode taglib = (TreeNode) taglibs.next(); + String tagUri = null; + String tagLoc = null; + TreeNode child = taglib.findChild("taglib-uri"); + if (child != null) + tagUri = child.getBody(); + child = taglib.findChild("taglib-location"); + if (child != null) + tagLoc = child.getBody(); + + // Save this location if appropriate + if (tagLoc == null) + continue; + if (uriType(tagLoc) == NOROOT_REL_URI) + tagLoc = "/WEB-INF/" + tagLoc; + String tagLoc2 = null; + if (tagLoc.endsWith(JAR_FILE_SUFFIX)) { + tagLoc = ctxt.getResource(tagLoc).toString(); + tagLoc2 = "META-INF/taglib.tld"; + } + mappings.put(tagUri, new String[] { tagLoc, tagLoc2 }); + } + } finally { + if (is != null) { + try { + is.close(); + } catch (Throwable t) {} + } + } + } + + /** + * Scans the given JarURLConnection for TLD files located in META-INF + * (or a subdirectory of it), adding an implicit map entry to the taglib + * map for any TLD that has a <uri> element. + * + * @param conn The JarURLConnection to the JAR file to scan + * @param ignore true if any exceptions raised when processing the given + * JAR should be ignored, false otherwise + */ + private void scanJar(JarURLConnection conn, boolean ignore) + throws JasperException { + + JarFile jarFile = null; + String resourcePath = conn.getJarFileURL().toString(); + try { + if (redeployMode) { + conn.setUseCaches(false); + } + jarFile = conn.getJarFile(); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = (JarEntry) entries.nextElement(); + String name = entry.getName(); + if (!name.startsWith("META-INF/")) continue; + if (!name.endsWith(".tld")) continue; + InputStream stream = jarFile.getInputStream(entry); + try { + String uri = getUriFromTld(resourcePath, stream); + // Add implicit map entry only if its uri is not already + // present in the map + if (uri != null && mappings.get(uri) == null) { + mappings.put(uri, new String[]{ resourcePath, name }); + } + } finally { + if (stream != null) { + try { + stream.close(); + } catch (Throwable t) { + // do nothing + } + } + } + } + } catch (Exception ex) { + if (!redeployMode) { + // if not in redeploy mode, close the jar in case of an error + if (jarFile != null) { + try { + jarFile.close(); + } catch (Throwable t) { + // ignore + } + } + } + if (!ignore) { + throw new JasperException(ex); + } + } finally { + if (redeployMode) { + // if in redeploy mode, always close the jar + if (jarFile != null) { + try { + jarFile.close(); + } catch (Throwable t) { + // ignore + } + } + } + } + } + + /* + * Searches the filesystem under /WEB-INF for any TLD files, and adds + * an implicit map entry to the taglib map for any TLD that has a <uri> + * element. + */ + private void processTldsInFileSystem(String startPath) + throws Exception { + + Set dirList = ctxt.getResourcePaths(startPath); + if (dirList != null) { + for (Object aDirList : dirList) { + String path = (String) aDirList; + if (path.endsWith("/")) { + processTldsInFileSystem(path); + } + if (!path.endsWith(".tld")) { + continue; + } + InputStream stream = ctxt.getResourceAsStream(path); + String uri; + try { + uri = getUriFromTld(path, stream); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (Throwable t) { + // do nothing + } + } + } + // Add implicit map entry only if its uri is not already + // present in the map + if (uri != null && mappings.get(uri) == null) { + mappings.put(uri, new String[]{path, null}); + } + } + } + } + + /* + * Returns the value of the uri element of the given TLD, or null if the + * given TLD does not contain any such element. + */ + private String getUriFromTld(String resourcePath, InputStream in) + throws JasperException + { + // Parse the tag library descriptor at the specified resource path + TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath, in); + TreeNode uri = tld.findChild("uri"); + if (uri != null) { + String body = uri.getBody(); + if (body != null) + return body; + } + + return null; + } + + /* + * Scans all JARs accessible to the webapp's classloader and its + * parent classloaders for TLDs. + * + * The list of JARs always includes the JARs under WEB-INF/lib, as well as + * all shared JARs in the classloader delegation chain of the webapp's + * classloader. + * + * Considering JARs in the classloader delegation chain constitutes a + * Tomcat-specific extension to the TLD search + * order defined in the JSP spec. It allows tag libraries packaged as JAR + * files to be shared by web applications by simply dropping them in a + * location that all web applications have access to (e.g., + * <CATALINA_HOME>/common/lib). + * + * The set of shared JARs to be scanned for TLDs is narrowed down by + * the <tt>noTldJars</tt> class variable, which contains the names of JARs + * that are known not to contain any TLDs. + */ + private void scanJars() throws Exception { + ClassLoader loader = webappLoader; + while (loader != null) { + if (loader instanceof URLClassLoader) { + URL[] urls = ((URLClassLoader) loader).getURLs(); + for (URL url : urls) { + URLConnection conn = url.openConnection(); + if (conn instanceof JarURLConnection) { + if (needScanJar(loader, webappLoader, ((JarURLConnection) conn).getJarFile().getName())) { + scanJar((JarURLConnection) conn, true); + } + } else { + String urlStr = url.toString(); + if (urlStr.startsWith(FILE_PROTOCOL) + && urlStr.endsWith(JAR_FILE_SUFFIX) + && needScanJar(loader, webappLoader, urlStr)) { + URL jarURL = new URL("jar:" + urlStr + "!/"); + scanJar((JarURLConnection) jarURL.openConnection(), + true); + } + } + } + } + + loader = loader.getParent(); + } + } + + /* + * Determines if the JAR file with the given <tt>jarPath</tt> needs to be + * scanned for TLDs. + * + * @param loader The current classloader in the parent chain + * @param webappLoader The webapp classloader + * @param jarPath The JAR file path + * + * @return TRUE if the JAR file identified by <tt>jarPath</tt> needs to be + * scanned for TLDs, FALSE otherwise + */ + private boolean needScanJar(ClassLoader loader, ClassLoader webappLoader, + String jarPath) { + if (loader == webappLoader) { + // JARs under WEB-INF/lib must be scanned unconditionally according + // to the spec. + return true; + } else { + String jarName = jarPath; + int slash = jarPath.lastIndexOf('/'); + if (slash >= 0) { + jarName = jarPath.substring(slash + 1); + } + return (!noTldJars.contains(jarName)); + } + } +} diff --git a/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java b/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java index 1483b4d..043c205 100644 --- a/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java +++ b/src/main/java/org/apache/sling/maven/jspc/JspcMojo.java @@ -17,19 +17,14 @@ package org.apache.sling.maven.jspc; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; +import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.StringTokenizer; +import java.util.jar.JarFile; import javax.servlet.ServletContext; @@ -44,17 +39,17 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; +import org.apache.sling.commons.classloader.ClassLoaderWriter; +import org.apache.sling.commons.compiler.JavaCompiler; +import org.apache.sling.commons.compiler.impl.EclipseJavaCompiler; +import org.apache.sling.scripting.jsp.jasper.IOProvider; import org.apache.sling.scripting.jsp.jasper.JasperException; import org.apache.sling.scripting.jsp.jasper.JspCompilationContext; import org.apache.sling.scripting.jsp.jasper.Options; -import org.apache.sling.scripting.jsp.jasper.compiler.Compiler; import org.apache.sling.scripting.jsp.jasper.compiler.JspConfig; import org.apache.sling.scripting.jsp.jasper.compiler.JspRuntimeContext; -import org.apache.sling.scripting.jsp.jasper.compiler.OriginalTldLocationsCache; import org.apache.sling.scripting.jsp.jasper.compiler.TagPluginManager; import org.apache.sling.scripting.jsp.jasper.compiler.TldLocationsCache; -import org.apache.sling.scripting.jsp.jasper.servlet.JspServletWrapper; -import org.apache.sling.scripting.jsp.jasper.xmlparser.TreeNode; import org.codehaus.plexus.util.DirectoryScanner; /** @@ -63,7 +58,7 @@ import org.codehaus.plexus.util.DirectoryScanner; * descriptor for Declarative Services to use the JSP with the help of the * appropriate adapter as component. */ -@Mojo( name = "jspc", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE) +@Mojo(name = "jspc", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.COMPILE) public class JspcMojo extends AbstractMojo implements Options { /** @@ -119,10 +114,16 @@ public class JspcMojo extends AbstractMojo implements Options { /** * Comma separated list of extensions of files to be compiled by the plugin. + * @deprecated Use the {@link #includes} filter instead. */ + @Deprecated @Parameter ( property = "jspc.jspFileExtensions", defaultValue = "jsp,jspx") private String jspFileExtensions; + /** + * @deprecated Due to internal refactoring, this is not longer supported. + */ + @Deprecated @Parameter ( property = "jspc.servletPackage", defaultValue = "org.apache.jsp") private String servletPackage; @@ -138,10 +139,6 @@ public class JspcMojo extends AbstractMojo implements Options { @Parameter private String[] excludes; - private Set<String> jspFileExtensionSet; - - private boolean compile = true; - private String uriSourceRoot; private List<String> pages = new ArrayList<String>(); @@ -152,8 +149,6 @@ public class JspcMojo extends AbstractMojo implements Options { private URLClassLoader loader = null; - private Map<String, TreeNode> tldCache; - /** * Cache for the TLD locations */ @@ -216,7 +211,7 @@ public class JspcMojo extends AbstractMojo implements Options { * Locate all jsp files in the webapp. Used if no explicit jsps are * specified. */ - public void scanFiles(File base) { + private void scanFiles(File base) { DirectoryScanner scanner = new DirectoryScanner(); scanner.setBasedir(base); @@ -224,9 +219,7 @@ public class JspcMojo extends AbstractMojo implements Options { scanner.setExcludes(excludes); scanner.scan(); - for (String included : scanner.getIncludedFiles()) { - pages.add(included); - } + Collections.addAll(pages, scanner.getIncludedFiles()); } /** @@ -298,39 +291,16 @@ public class JspcMojo extends AbstractMojo implements Options { } private void processFile(final String file) throws JasperException { - ClassLoader originalClassLoader = null; - try { final String jspUri = file.replace('\\', '/'); - final JspServletWrapper wrapper = new JspServletWrapper(new JspCServletConfig(context), this, jspUri, - false, rctxt); - - final JspCompilationContext clctxt = new JspCompilationContext(jspUri, - false, this, context, wrapper, rctxt); - - // write to a specific servlet package - clctxt.setServletPackageName(servletPackage); + final JspCompilationContext clctxt = new JspCompilationContext(jspUri, false, this, context, rctxt, false); - originalClassLoader = Thread.currentThread().getContextClassLoader(); - if (loader == null) { - initClassLoader(); + JasperException error = clctxt.compile(); + if (error != null) { + throw error; } - Thread.currentThread().setContextClassLoader(loader); - - Compiler clc = clctxt.createCompiler(); - - // If compile is set, generate both .java and .class, if - // .jsp file is newer than .class file; - // Otherwise only generate .java, if .jsp file is newer than - // the .java file - if (clc.isOutDated(compile)) { - clc.compile(compile, true); - - if (showSuccess) { - getLog().info("Built File: " + file); - } - } else if (showSuccess) { - getLog().info("File up to date: " + file); + if (showSuccess) { + getLog().info("Built File: " + file); } } catch (final JasperException je) { @@ -352,117 +322,91 @@ public class JspcMojo extends AbstractMojo implements Options { getLog().error(je.getMessage()); } catch (Exception e) { - if ((e instanceof FileNotFoundException) - && getLog().isWarnEnabled()) { - getLog().warn("Missing file: " + e.getMessage()); - } throw new JasperException(e); - } finally { - if (originalClassLoader != null) { - Thread.currentThread().setContextClassLoader( - originalClassLoader); - } } } // ---------- Additional Settings ------------------------------------------ - private Set<String> getExtensions() { - if (jspFileExtensionSet == null) { - jspFileExtensionSet = new HashSet<String>(); - - // fallback default value, should actually be set by Maven - if (jspFileExtensions == null) { - jspFileExtensions = "jsp,jspx"; - } - - StringTokenizer st = new StringTokenizer(jspFileExtensions, ","); - while (st.hasMoreTokens()) { - String ext = st.nextToken().trim(); - if (ext.length() > 0) { - jspFileExtensionSet.add(ext); - } - } + private void initServletContext() throws IOException, DependencyResolutionRequiredException { + if (loader == null) { + initClassLoader(); } - return jspFileExtensionSet; - } + context = new JspCServletContext(getLog(), new URL("file:" + uriSourceRoot.replace('\\', '/') + '/')); + tldLocationsCache = new JspCTldLocationsCache(context, true, loader); - private void initServletContext() { - try { - context = new JspCServletContext(getLog(), new URL("file:" - + uriSourceRoot.replace('\\', '/') + '/')); - tldLocationsCache = new OriginalTldLocationsCache(context, true); - } catch (MalformedURLException me) { - getLog().error("Cannot setup ServletContext", me); - } - - rctxt = new JspRuntimeContext(context, this); + JavaCompiler compiler = new EclipseJavaCompiler(); + ClassLoaderWriter writer = new JspCClassLoaderWriter(loader, new File(outputDirectory)); + IOProvider ioProvider = new JspCIOProvider(loader, compiler, writer); + rctxt = new JspRuntimeContext(context, this, ioProvider); jspConfig = new JspConfig(context); tagPluginManager = new TagPluginManager(context); } + /** * Initializes the classloader as/if needed for the given compilation * context. * - * @param clctxt The compilation context * @throws IOException If an error occurs */ private void initClassLoader() throws IOException, DependencyResolutionRequiredException { - List artifacts = project.getCompileArtifacts(); - URL[] path = new URL[artifacts.size() + 1]; - int i = 0; - for (Iterator ai=artifacts.iterator(); ai.hasNext(); ) { - Artifact a = (Artifact) ai.next(); - path[i++] = a.getFile().toURI().toURL(); - } + List<URL> classPath = new ArrayList<URL>(); + + // add output directory to classpath final String targetDirectory = project.getBuild().getOutputDirectory(); - path[path.length - 1] = new File(targetDirectory).toURI().toURL(); - loader = new URLClassLoader(path, this.getClass().getClassLoader()); - } + classPath.add(new File(targetDirectory).toURI().toURL()); + + // add artifacts from project + Set<Artifact> artifacts = project.getDependencyArtifacts(); + for (Artifact a: artifacts) { + final String scope = a.getScope(); + if ("provided".equals(scope) || "runtime".equals(scope) || "compile".equals(scope)) { + // we need to exclude the javax.servlet.jsp API, otherwise the taglib parser causes problems (see note below) + if (containsProblematicPackage(a.getFile())) { + continue; + } + classPath.add(a.getFile().toURI().toURL()); + } + } - // ---------- Options interface -------------------------------------------- + if (getLog().isDebugEnabled()) { + getLog().debug("Compiler classpath:"); + for (URL u: classPath) { + getLog().debug(" " + u); + } + } - /* - * (non-Javadoc) - * - * @see org.apache.jasper.Options#genStringAsCharArray() - */ - public boolean genStringAsCharArray() { - return jasperGenStringAsCharArray; + // this is dangerous to use this classloader as parent as the compilation will depend on the classes provided + // in the plugin dependencies. but if we omit this, we get errors by not being able to load the TagExtraInfo classes. + // this is because this plugin uses classes from the javax.servlet.jsp that are also loaded via the TLDs. + loader = new URLClassLoader(classPath.toArray(new URL[classPath.size()]), this.getClass().getClassLoader()); } - /* - * (non-Javadoc) - * - * @see org.apache.jasper.Options#isCaching() + /** + * Checks if the given jar file contains a problematic java API that should be excluded from the classloader. + * @param file the file to check + * @return {@code true} if it contains a problematic package + * @throws IOException if an error occurrs. */ - public boolean isCaching() { - return true; + private boolean containsProblematicPackage(File file) throws IOException { + JarFile jar = new JarFile(file); + boolean isJSPApi = jar.getEntry("/javax/servlet/jsp/JspPage.class") != null; + jar.close(); + return isJSPApi; } - /* - * (non-Javadoc) - * - * @see org.apache.jasper.Options#getCache() - */ - public Map<String, TreeNode> getCache() { - if (tldCache == null) { - tldCache = new HashMap<String, TreeNode>(); - } - - return tldCache; - } + // ---------- Options interface -------------------------------------------- /* * (non-Javadoc) * - * @see org.apache.jasper.Options#getCheckInterval() + * @see org.apache.jasper.Options#genStringAsCharArray() */ - public int getCheckInterval() { - return 0; + public boolean genStringAsCharArray() { + return jasperGenStringAsCharArray; } /* @@ -477,16 +421,6 @@ public class JspcMojo extends AbstractMojo implements Options { /* * (non-Javadoc) * - * @see org.apache.jasper.Options#getClassPath() - */ - public String getClassPath() { - // no extra classpath - return null; - } - - /* - * (non-Javadoc) - * * @see org.apache.jasper.Options#getCompiler() */ public String getCompiler() { @@ -520,15 +454,6 @@ public class JspcMojo extends AbstractMojo implements Options { /* * (non-Javadoc) * - * @see org.apache.jasper.Options#getDevelopment() - */ - public boolean getDevelopment() { - return false; - } - - /* - * (non-Javadoc) - * * @see org.apache.jasper.Options#getErrorOnUseBeanInvalidClassAttribute() */ public boolean getErrorOnUseBeanInvalidClassAttribute() { @@ -567,15 +492,6 @@ public class JspcMojo extends AbstractMojo implements Options { /* * (non-Javadoc) * - * @see org.apache.jasper.Options#getJspClassLoader() - */ - public ClassLoader getJspClassLoader() { - return this.loader; - } - - /* - * (non-Javadoc) - * * @see org.apache.jasper.Options#getJspConfig() */ public JspConfig getJspConfig() { @@ -603,15 +519,6 @@ public class JspcMojo extends AbstractMojo implements Options { /* * (non-Javadoc) * - * @see org.apache.jasper.Options#getModificationTestInterval() - */ - public int getModificationTestInterval() { - return 0; - } - - /* - * (non-Javadoc) - * * @see org.apache.jasper.Options#getScratchDir() */ public String getScratchDir() { -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
