Add support for JSPs in DevMode; this also enables annotations scanning. Bug: #9292 Bug-Link: https://github.com/gwtproject/gwt/issues/9292 Change-Id: Ibd8704a74292088857f344a25a073ba03771696c
diff --git a/dev/build.xml b/dev/build.xml index 1d8bf84..a864a2b 100755 --- a/dev/build.xml +++ b/dev/build.xml
@@ -75,12 +75,13 @@ <include name="jsr305/jsr305.jar"/> <include name="protobuf/protobuf-2.5.0/protobuf-java-rebased-2.5.0.jar"/> <!-- dependencies needed for JSP support in DevMode: BEGIN --> - <include name="tomcat/commons-el-1.0.jar"/> - <include name="tomcat/jasper-compiler-1.0.jar"/> - <include name="tomcat/jasper-runtime-1.0.jar"/> - <include name="tomcat/jsp-api-2.0.jar"/> + <include name="jetty/jetty-9.2.14.v20151106/mortbay-apache-jsp-8.0.9.M3.jar"/> + <include name="jetty/jetty-9.2.14.v20151106/mortbay-apache-el-8.0.9.M3.jar"/> + <include name="jetty/jetty-9.2.14.v20151106/jetty-apache-jsp-9.2.14.v20151106.jar"/> <!-- dependencies needed for JSP support in DevMode: END --> <include name="tomcat/tomcat-servlet-api-8.0.28.jar"/> + <include name="tomcat/tomcat-websocket-api-8.0.28.jar"/> + <include name="tomcat/tomcat-annotations-api-8.0.28.jar"/> <include name="apache/commons/commons-collections-3.2.2.jar"/> <!-- htmlunit dependencies not already included: BEGIN --> <include name="apache/http/httpclient-4.5.1.jar"/> @@ -111,6 +112,10 @@ </targetfiles> <sequential> <gwt.jar destfile="${alldeps.jar}" duplicate="preserve"> + <service type="javax.servlet.ServletContainerInitializer"> + <provider classname="org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer"/> + <provider classname="org.eclipse.jetty.apache.jsp.JettyJasperInitializer"/> + </service> <zipfileset src="${gwt.tools.lib}/objectweb/asm-5.0.3/lib/asm-all-5.0.3.jar"/> <zipfileset src="${gwt.tools.lib}/apache/tapestry-util-text-4.0.2.jar"/> <zipfileset src="${gwt.tools.lib}/apache/ant-1.6.5.jar"/> @@ -130,12 +135,13 @@ <zipfileset src="${gwt.tools.lib}/protobuf/protobuf-2.5.0/protobuf-java-rebased-2.5.0.jar"/> <!-- dependencies needed for JSP support in DevMode: BEGIN --> - <zipfileset src="${gwt.tools.lib}/tomcat/commons-el-1.0.jar"/> - <zipfileset src="${gwt.tools.lib}/tomcat/jasper-compiler-1.0.jar"/> - <zipfileset src="${gwt.tools.lib}/tomcat/jasper-runtime-1.0.jar"/> - <zipfileset src="${gwt.tools.lib}/tomcat/jsp-api-2.0.jar"/> + <zipfileset src="${gwt.tools.lib}/jetty/jetty-9.2.14.v20151106/mortbay-apache-jsp-8.0.9.M3.jar"/> + <zipfileset src="${gwt.tools.lib}/jetty/jetty-9.2.14.v20151106/mortbay-apache-el-8.0.9.M3.jar"/> + <zipfileset src="${gwt.tools.lib}/jetty/jetty-9.2.14.v20151106/jetty-apache-jsp-9.2.14.v20151106.jar"/> <!-- dependencies needed for JSP support in DevMode: END --> <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-servlet-api-8.0.28.jar"/> + <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-websocket-api-8.0.28.jar"/> + <zipfileset src="${gwt.tools.lib}/tomcat/tomcat-annotations-api-8.0.28.jar"/> <zipfileset src="${gwt.tools.lib}/apache/commons/commons-collections-3.2.2.jar"/> <!-- htmlunit dependencies not already included: BEGIN -->
diff --git a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java index 5a7b37c..317159c 100644 --- a/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java +++ b/dev/core/src/com/google/gwt/dev/shell/jetty/JettyLauncher.java
@@ -21,9 +21,9 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.dev.util.InstalledHelpInfo; import com.google.gwt.dev.util.Util; +import com.google.gwt.thirdparty.guava.common.collect.Iterators; +import com.google.gwt.thirdparty.guava.common.collect.Lists; -import org.apache.tools.ant.taskdefs.Javac; -import org.eclipse.jdt.core.JDTCompilerAdapter; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.server.HttpConfiguration; @@ -41,6 +41,7 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.webapp.ClasspathPattern; +import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; @@ -51,6 +52,10 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; import javax.imageio.ImageIO; import javax.xml.parsers.DocumentBuilderFactory; @@ -62,23 +67,6 @@ public class JettyLauncher extends ServletContainerLauncher { /** - * Ant compiler adapter for Eclipse Java compiler, but with default - * target and source compatibility set to Java 6. - */ - public static final class JDTCompiler16 extends JDTCompilerAdapter { - @Override - public void setJavac(Javac attributes) { - if (attributes.getTarget() == null) { - attributes.setTarget("1.6"); - } - if (attributes.getSource() == null) { - attributes.setSource("1.6"); - } - super.setJavac(attributes); - } - } - - /** * Log jetty requests/responses to TreeLogger. */ public static class JettyRequestLogger extends AbstractLifeCycle implements @@ -363,13 +351,17 @@ private final ClasspathPattern systemClassesFromWebappFirst = new ClasspathPattern(new String[] { "-javax.servlet.", + "-javax.el.", "javax.", }); private final ClasspathPattern allowedFromSystemClassLoader = new ClasspathPattern(new String[] { "org.eclipse.jetty.", + "javax.websocket.", // Jasper "org.apache.jasper.", - "org.apache.commons.logging.", + "org.apache.juli.logging.", + "org.apache.tomcat.", + "org.apache.el.", // Xerces "org.apache.xerces.", "javax.xml.", // Used by Jetty for jetty-web.xml parsing @@ -380,6 +372,18 @@ } @Override + public Enumeration<URL> getResources(String name) throws IOException { + // Logic copied from Jetty's WebAppClassLoader + List<URL> fromParent = isServerClass(name) + ? Collections.<URL>emptyList() + : Lists.newArrayList(Iterators.forEnumeration(systemClassLoader.getResources(name))); + Iterator<URL> fromWebapp = isSystemClass(name) && !fromParent.isEmpty() + ? Collections.<URL>emptyIterator() + : Iterators.forEnumeration(findResources(name)); + return Iterators.asEnumeration(Iterators.concat(fromWebapp, fromParent.iterator())); + } + + @Override public URL findResource(String name) { // Specifically for META-INF/services/javax.xml.parsers.SAXParserFactory String checkName = name; @@ -390,7 +394,7 @@ // For a system path, load from the outside world. // Note: bootstrap has already been searched, so javax. classes should be - // tried from the webapp first (except for javax.servlet). + // tried from the webapp first (except for javax.servlet and javax.el). URL found; if (isSystemClass(checkName) && !systemClassesFromWebappFirst.match(checkName)) { found = systemClassLoader.getResource(name); @@ -413,12 +417,8 @@ // Special-case Jetty/Jasper/etc. resources if (allowedFromSystemClassLoader.match(checkName) || - // Jasper uses Log4j (via Commons Logging), which will try - // to load those. - // We have a log4j.properties in user/test and don't want - // to add gwt-user when using a "Developer SDK" in Eclipse. - "log4j.xml".equals(name) || - "log4j.properties".equals(name)) { + // Jetty-plus reads jndi.properties + "jndi.properties".equals(name)) { return found; } @@ -460,6 +460,12 @@ return null; } + // Special-case JDBCUnloader; it should always be loaded in the webapp classloader + if (JDBCUnloader.class.getName().equals(name)) { + byte[] jdbcUnloader = Util.readURLAsBytes(found); + return defineClass(name, jdbcUnloader, 0, jdbcUnloader.length); + } + // Those classes are allowed to be loaded right from the systemClassLoader // Note: Jetty classes here are not "server classes", handled above. if (allowedFromSystemClassLoader.match(name)) { @@ -574,16 +580,6 @@ */ private static final String PROPERTY_NOWARN_WEBAPP_CLASSPATH = "gwt.nowarn.webapp.classpath"; - static { - /* - * Make JDT the default Ant compiler so that JSP compilation just works - * out-of-the-box. If we don't set this, it's very, very difficult to make - * JSP compilation work. - */ - String antJavaC = System.getProperty("build.compiler", JDTCompiler16.class.getName()); - System.setProperty("build.compiler", antJavaC); - } - /** * Setup a connector for the bind address/port. * @@ -735,8 +731,28 @@ setupConnector(connector, bindAddress, port); server.addConnector(connector); + Configuration.ClassList cl = Configuration.ClassList.setServerDefault(server); + try { + // from jetty-plus.xml + Thread.currentThread().getContextClassLoader().loadClass("org.eclipse.jetty.plus.webapp.PlusConfiguration"); + cl.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", + "org.eclipse.jetty.plus.webapp.EnvConfiguration", + "org.eclipse.jetty.plus.webapp.PlusConfiguration"); + } catch (ClassNotFoundException cnfe) { + logger.log(TreeLogger.Type.DEBUG, "jetty-plus isn't on the classpath, JNDI won't work. This might also affect annotations scanning and JSP."); + } + try { + // from jetty-annotations.xml + Thread.currentThread().getContextClassLoader() + .loadClass("org.eclipse.jetty.annotations.AnnotationConfiguration"); + cl.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", + "org.eclipse.jetty.annotations.AnnotationConfiguration"); + } catch (ClassNotFoundException cnfe) { + logger.log(TreeLogger.Type.DEBUG, "jetty-annotations isn't on the classpath, annotation scanning won't work. This might also affect annotations scanning."); + } + // Create a new web app in the war directory. - WebAppContext wac = createWebAppContext(logger, appRootDir); + WebAppContext wac = createWebAppContext(logger, appRootDir); RequestLogHandler logHandler = new RequestLogHandler(); logHandler.setRequestLog(new JettyRequestLogger(logger, getBaseLogLevel())); @@ -766,7 +782,18 @@ } protected WebAppContext createWebAppContext(TreeLogger logger, File appRootDir) { - return new WebAppContextWithReload(logger, appRootDir.getAbsolutePath(), "/"); + WebAppContext context = new WebAppContextWithReload(logger, appRootDir.getAbsolutePath(), "/"); + context.setConfigurationClasses(new String[] { + "org.eclipse.jetty.webapp.WebInfConfiguration", + "org.eclipse.jetty.webapp.WebXmlConfiguration", + "org.eclipse.jetty.webapp.MetaInfConfiguration", + "org.eclipse.jetty.webapp.FragmentConfiguration", + "org.eclipse.jetty.plus.webapp.EnvConfiguration", + "org.eclipse.jetty.plus.webapp.PlusConfiguration", + "org.eclipse.jetty.annotations.AnnotationConfiguration", + "org.eclipse.jetty.webapp.JettyWebXmlConfiguration" + }); + return context; } protected ServerConnector getConnector(Server server, TreeLogger logger) {
diff --git a/eclipse/dev/.classpath b/eclipse/dev/.classpath index 85c02a6..14118db 100644 --- a/eclipse/dev/.classpath +++ b/eclipse/dev/.classpath
@@ -15,10 +15,9 @@ <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/> <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-1.0.jar"/> <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-optional-1.0.jar"/> - <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/commons-el-1.0.jar"/> - <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/jasper-compiler-1.0.jar"/> - <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/jasper-runtime-1.0.jar"/> - <classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/jsp-api-2.0.jar"/> + <classpathentry kind="var" path="GWT_TOOLS/lib/jetty/jetty-9.2.14.v20151106/mortbay-apache-jsp-8.0.9.M3.jar"/> + <classpathentry kind="var" path="GWT_TOOLS/lib/jetty/jetty-9.2.14.v20151106/mortbay-apache-el-8.0.9.M3.jar"/> + <classpathentry kind="var" path="GWT_TOOLS/lib/jetty/jetty-9.2.14.v20151106/jetty-apache-jsp-9.2.14.v20151106.jar"/> <classpathentry kind="var" path="GWT_TOOLS/lib/htmlunit/htmlunit-2.19/htmlunit-2.19.jar"/> <classpathentry kind="var" path="GWT_TOOLS/lib/htmlunit/htmlunit-2.19/htmlunit-core-js-2.15.jar"/> <classpathentry kind="var" path="GWT_TOOLS/lib/protobuf/protobuf-2.5.0/protobuf-java-rebased-2.5.0.jar"/> @@ -29,6 +28,8 @@ <classpathentry kind="var" path="GWT_TOOLS/lib/json/android-sdk-19.1/json-android-rebased.jar"/> <classpathentry kind="var" path="GWT_TOOLS/lib/apache/commons/commons-collections-3.2.2.jar"/> <classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/tomcat/tomcat-servlet-api-8.0.28.jar"/> + <classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/tomcat/tomcat-websocket-api-8.0.28.jar"/> + <classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/tomcat/tomcat-annotations-api-8.0.28.jar"/> <classpathentry kind="var" path="GWT_TOOLS/lib/colt/colt-1.2.jar" sourcepath="/GWT_TOOLS/lib/colt/colt-1.2-src.jar"/> <classpathentry kind="output" path="bin"/> </classpath>
diff --git a/user/test/com/google/gwt/dev/shell/jetty/JettyLauncherSuite.java b/user/test/com/google/gwt/dev/shell/jetty/JettyLauncherSuite.java new file mode 100644 index 0000000..aebca32 --- /dev/null +++ b/user/test/com/google/gwt/dev/shell/jetty/JettyLauncherSuite.java
@@ -0,0 +1,34 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed 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 com.google.gwt.dev.shell.jetty; + +import com.google.gwt.dev.shell.jetty.client.JspTest; +import com.google.gwt.junit.tools.GWTTestSuite; + +import junit.framework.Test; + +/** + * All JettyLauncher tests that use GWTTestCase. + */ +public class JettyLauncherSuite { + public static Test suite() { + GWTTestSuite suite = new GWTTestSuite("All JettyLauncher tests"); + + suite.addTestSuite(JspTest.class); + + return suite; + } +}
diff --git a/user/test/com/google/gwt/dev/shell/jetty/Jsp.gwt.xml b/user/test/com/google/gwt/dev/shell/jetty/Jsp.gwt.xml new file mode 100644 index 0000000..684596f --- /dev/null +++ b/user/test/com/google/gwt/dev/shell/jetty/Jsp.gwt.xml
@@ -0,0 +1,17 @@ +<!-- --> +<!-- Copyright 2016 Google Inc. --> +<!-- Licensed under the Apache License, Version 2.0 (the "License"); you --> +<!-- may not use this file except in compliance with the License. You may --> +<!-- 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. License for the specific language governing permissions and --> +<!-- limitations under the License. --> + +<module> + <inherits name="com.google.gwt.http.HTTP" /> +</module>
diff --git a/user/test/com/google/gwt/dev/shell/jetty/client/JspTest.java b/user/test/com/google/gwt/dev/shell/jetty/client/JspTest.java new file mode 100644 index 0000000..d6d0027 --- /dev/null +++ b/user/test/com/google/gwt/dev/shell/jetty/client/JspTest.java
@@ -0,0 +1,51 @@ +/* + * Copyright 2016 Google Inc. + * + * Licensed 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 com.google.gwt.dev.shell.jetty.client; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.http.client.Request; +import com.google.gwt.http.client.RequestBuilder; +import com.google.gwt.http.client.RequestCallback; +import com.google.gwt.http.client.Response; +import com.google.gwt.junit.client.GWTTestCase; + +/** + * Checks that JSPs are supported in JettyLauncher (through JUnitShell) + */ +public class JspTest extends GWTTestCase { + @Override + public String getModuleName() { + return "com.google.gwt.dev.shell.jetty.Jsp"; + } + + public void testJsp() throws Exception { + delayTestFinish(5000); + new RequestBuilder(RequestBuilder.GET, GWT.getModuleBaseForStaticFiles() + "java7.jsp") + .sendRequest("", new RequestCallback() { + @Override + public void onResponseReceived(Request request, Response response) { + assertEquals(200, response.getStatusCode()); + assertEquals("OK", response.getText().trim()); + finishTest(); + } + + @Override + public void onError(Request request, Throwable exception) { + fail(); + } + }); + } +}
diff --git a/user/test/com/google/gwt/dev/shell/jetty/public/java7.jsp b/user/test/com/google/gwt/dev/shell/jetty/public/java7.jsp new file mode 100644 index 0000000..dbc65d3 --- /dev/null +++ b/user/test/com/google/gwt/dev/shell/jetty/public/java7.jsp
@@ -0,0 +1,11 @@ +<%@page language="java" contentType="text/plain; charset=UTF-8" session="false" %> +<% +// Use a switch-on-string to check whether the page compiles as Java 7 +switch (request.getMethod()) { +case "GET": + out.print("OK"); + break; +default: + out.print(request.getMethod()); +} +%>