Deploying existing WAR to embedded Jetty

2019-08-02 03:34发布

问题:

My intention is to deploy an existing WAR to embedded Jetty 9.4.5.

Unfortunately I get the following error when trying to open a page (JSP):

An error occurred at line: [52] in the generated java file: [/tmp/embedded-jetty-jsp/jsp/org/apache/jsp/WEB_002dINF/jsp/MainLayout_jsp.java]
Type mismatch: cannot convert from HashSet<?> to Set<String>

An error occurred at line: [52] in the generated java file: [/tmp/embedded-jetty-jsp/jsp/org/apache/jsp/WEB_002dINF/jsp/MainLayout_jsp.java]
Cannot instantiate the type HashSet<?>

An error occurred at line: [52] in the generated java file: [/tmp/embedded-jetty-jsp/jsp/org/apache/jsp/WEB_002dINF/jsp/MainLayout_jsp.java]
Syntax error on token "<", ? expected after this token

The line in question in Java is as follows:

private static final java.util.Set<java.lang.String> _jspx_imports_packages = new java.util.HashSet<>();

It seems that Jasper tried to compile the code as Java 1.6 or below so the diamond operator cannot be interpreted (I am having Java 1.8.0_141).

I tried to set the version but no success:

ServletHolder holderJsp = new ServletHolder("jsp",JspServlet.class);
holderJsp.setInitOrder(0);
holderJsp.setInitParameter("logVerbosityLevel","DEBUG");
holderJsp.setInitParameter("compilerTargetVM","1.7");
holderJsp.setInitParameter("compilerSourceVM","1.7");
holderJsp.setInitParameter("keepgenerated","true");
webAppContext.addServlet(holderJsp,"*.jsp");

The code that starts Jetty is

public class JettyRunner {

  private static File getScratchDir() throws IOException {
     File tempDir = new File(System.getProperty("java.io.tmpdir"));
     File scratchDir = new File(tempDir.toString(), "embedded-jetty-jsp");

     if (!scratchDir.exists()) {
        if (!scratchDir.mkdirs()) {
           throw new IOException("Unable to create scratch directory: " + scratchDir);
        }
     }
     return scratchDir;
  }

  private static List<ContainerInitializer> jspInitializers() {
     JettyJasperInitializer sci = new JettyJasperInitializer();
     ContainerInitializer initializer = new ContainerInitializer(sci, null);
     List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
     initializers.add(initializer);
     return initializers;
  } 


  public static void main(String[] args) {
     Server server = new Server(8080);

     WebAppContext webAppContext = new WebAppContext();
     File warFile = new File("existing.war");
     webAppContext.setWar(warFile.getAbsolutePath());
     webAppContext.setContextPath("/acme");

     webAppContext.setConfigurations(new Configuration[] {
              new AnnotationConfiguration(),
              new WebInfConfiguration(),
              new WebXmlConfiguration(),
              new MetaInfConfiguration(),
              new FragmentConfiguration(),
              new EnvConfiguration(),
              new PlusConfiguration(),
              new JettyWebXmlConfiguration()
     });

     webAppContext.setAttribute("javax.servlet.context.tempdir", getScratchDir());
     webAppContext.setAttribute("org.eclipse.jetty.containerInitializers", jspInitializers());
     webAppContext.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager());
     webAppContext.addBean(new ServletContainerInitializersStarter(webAppContext), true);

     ServletHolder holderJsp = new ServletHolder("jsp",JspServlet.class);
     holderJsp.setInitOrder(0);
     holderJsp.setInitParameter("logVerbosityLevel","DEBUG");
     holderJsp.setInitParameter("compilerTargetVM","1.7");
     holderJsp.setInitParameter("compilerSourceVM","1.7");
     holderJsp.setInitParameter("keepgenerated","true");
     webAppContext.addServlet(holderJsp,"*.jsp");

     webAppContext.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*/[^/]*jstl.*\\.jar$");
     org.eclipse.jetty.webapp.Configuration.ClassList classlist = org.eclipse.jetty.webapp.Configuration.ClassList.setServerDefault(server);
     classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration");
     classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration");

     HashLoginService hashLoginService = new HashLoginService();
     hashLoginService.setName("Test Realm");
     hashLoginService.setConfig("jetty-realm.properties");

     webAppContext.getSecurityHandler().setLoginService(hashLoginService);

     server.setHandler(webAppContext);          

     // Start Jetty
     server.start();
     server.join();
  }

}

Any hint would be highly appreciated!
Thanks, V.

--------------------------- UPDATE 1 ---------------------------
I set the server.setDumpAfterStart(true); (thanks @JoakimErdfelt for the hint!) and commented out the code where I set the compilerTargetVM etc. (so I don't add the JspServlet to webAppContext!) and I can see that

|   += org.eclipse.jetty.server.session.SessionHandler483422889==dftMaxIdleSec=1800 - STARTED
|   |   += org.eclipse.jetty.security.ConstraintSecurityHandler@7c75222b - STARTED
|   |   |   +- org.eclipse.jetty.security.DefaultAuthenticatorFactory@4c203ea1
|   |   |   += org.eclipse.jetty.servlet.ServletHandler@27f674d - STARTED
|   |   |   |   += jsp@19c47==org.eclipse.jetty.jsp.JettyJspServlet,jsp=null,order=0,inst=true - STARTED
|   |   |   |   |   +- fork=false
|   |   |   |   |   +- compilerSourceVM=1.7
|   |   |   |   |   +- logVerbosityLevel=DEBUG
|   |   |   |   |   +- compilerTargetVM=1.7
|   |   |   |   |   +- scratchdir=/tmp/embedded-jetty-jsp/jsp
|   |   |   |   |   +- xpoweredBy=false

So the Java source is set to 1.7 but still the diamond operator cannot be interpreted by the JVM!
Bummer... Any idea?
Thank you very much!

回答1:

Your WAR has WEB-INF/lib/ entries that are conflicting with the updated version of JSP.

Remove the following entries from your WAR.

WEB-INF/lib/jstl-1.1.2.jar
WEB-INF/lib/standard-1.1.2.jar

Those jars should never have been included in your WAR file in the first place.

Those are provided by the JSP Container.

Also, get rid of this ...

webAppContext.setConfigurations(new Configuration[] {
          new AnnotationConfiguration(),
          new WebInfConfiguration(),
          new WebXmlConfiguration(),
          new MetaInfConfiguration(),
          new FragmentConfiguration(),
          new EnvConfiguration(),
          new PlusConfiguration(),
          new JettyWebXmlConfiguration()
 });

... and add this instead (before you create the WebAppContext) ...

    Configuration.ClassList classlist = Configuration.ClassList
            .setServerDefault( server );
    classlist.addAfter(
            "org.eclipse.jetty.webapp.FragmentConfiguration",
            "org.eclipse.jetty.plus.webapp.EnvConfiguration",
            "org.eclipse.jetty.plus.webapp.PlusConfiguration");

    classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
                        "org.eclipse.jetty.annotations.AnnotationConfiguration");

As its inappropriate to "set" the entire configuration list, use the modification routines instead.

Example taken from https://github.com/eclipse/jetty.project/blob/jetty-9.4.8.v20171121/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java#L186-L195