首页 文章

如何使用嵌入式Jetty 9成功解析JSTL URI?

提问于
浏览
14

我正在打包Web应用程序存档(.war),以便通过在主类中使用此代码启动Jetty 9的嵌入副本,在shell中通过 java -jar webapp.war 启动它:

int port = Integer.parseInt(System.getProperty("port", "80")); // I know this has implications :)
String contextPath = System.getProperty("contextPath", "");
Server server = new Server(port);
ProtectionDomain domain = Deployer.class.getProtectionDomain();
URL location = domain.getCodeSource().getLocation();
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/" + contextPath);
webapp.setWar(location.toExternalForm());
server.setHandler(webapp);
server.start();
server.join();

但是,当第一个包含JSTL taglib声明的JSP被编译时,我遇到了这个错误:

org.apache.jasper.JasperException: /WEB-INF/html/user/login.jsp(2,62) PWC6188: The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
at org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:92)
at org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:378)
at org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:172)
at org.apache.jasper.compiler.TagLibraryInfoImpl.generateTLDLocation(TagLibraryInfoImpl.java:431)
at org.apache.jasper.compiler.TagLibraryInfoImpl.<init>(TagLibraryInfoImpl.java:240)
at org.apache.jasper.compiler.Parser.parseTaglibDirective(Parser.java:502)
at org.apache.jasper.compiler.Parser.parseDirective(Parser.java:582)
at org.apache.jasper.compiler.Parser.parseElements(Parser.java:1652)
at org.apache.jasper.compiler.Parser.parse(Parser.java:185)
at org.apache.jasper.compiler.ParserController.doParse(ParserController.java:244)
at org.apache.jasper.compiler.ParserController.parse(ParserController.java:145)
at org.apache.jasper.compiler.Compiler.generateJava(Compiler.java:212)
at org.apache.jasper.compiler.Compiler.compile(Compiler.java:451)
at org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:625)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:374)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:492)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:378)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:848)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:698)
etc...

该JSP的前几行如下:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

我看了很多(这似乎不是一个新问题),并尝试了以下解决方案:

  • 减少我的依赖关系并寻找冲突(目前我只依赖于 jetty-serverjetty-webappjetty-jsp ,所有版本 9.0.4.v20130625

  • 在webapp的web.xml文件中指定一个直接指向JSTL的显式 <taglib> 映射(通过阅读JSP规范获得此想法)

  • 根据this answer修改服务器类路径

  • 利用WebAppContext的方法,例如 addServerClasssetParentLoaderPriority

根据Jetty's documentation,使用JSTL应该可行,但我认为嵌入式上下文可能正在改变JSTL加载的方式并导致它失败 .

将不胜感激任何想法或建议 . 此设置将替换在Windows上成功执行相同操作但在Linux上无法运行的旧设置,因为包含了引入this bug的旧依赖项 . 不幸的是,我还没有找到一个快速替换该依赖项(groupId org.mortbay.jetty artifactId jsp-2.1-glassfish version 2.1.v20100127 ),它没有引入上面提到的JSTL URI堆栈跟踪 .

UPDATE: 我发现了一个次优的解决方案 . 由this thread启发的降级到Jetty 7现在已经启动并运行 . 这是个好消息,但它必须废弃这个部署基础架构 . 任何关于Jetty 9中JSTL taglib问题的见解仍然会受到赞赏 .

8 回答

  • 2

    所以这是另一个解决方案 . 我正在努力解决非常类似的问题只有我有一个单独的war文件和一个简单的嵌入类,它为我创建一个Jetty服务器并启动它指向可能的任何war文件 . 以下是这一切的工作原理 .

    • war文件在 WEB-INF/lib 中没有任何tld库,并且与加载器miniapplication完全分开 .

    • Loader应用程序启动服务器并将其指向任何war文件的主类具有以下依赖项(maven):

    <!-- Jetty webapp -->
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>${jettyVersion}</version>
        </dependency>
    
        <!-- With JSP support -->
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-jsp</artifactId>
            <version>${jettyVersion}</version>
        </dependency>
    
    • 加载类本身看起来像:
    Server server = new Server(cncPort);
    
    WebAppContext webApp = new WebAppContext();
    
    webApp.setContextPath(applicationContext);
    webApp.setWar(jettyHome + "/" + warFile);
    server.setHandler(webApp);
    
    try {
        server.start();
        server.join();
    } catch (Exception ex) {
        System.out.println("Failed to start server.");
        ex.printStackTrace();
    }
    
    • 我案例中的结果包如下所示:

    EmbedderApp
    |

    • lib

    • EmbeddedApp.jar < - 使用embedder类的JAR

    • com.sun.el-2.2.0.v201303151357.jar

    • javax.el-2.2.0.v201303151357.jar

    • javax.servlet-3.0.0.v201112011016.jar

    • javax.servlet.jsp-2.2.0.v201112011158.jar

    • javax.servlet.jsp.jstl-1.2.0.v201105211821.jar

    • jetty-http-9.0.6.v20130930.jar

    • jetty-io-9.0.6.v20130930.jar

    • jetty-jsp-9.0.6.v20130930.jar

    • jetty-security-9.0.6.v20130930.jar

    • jetty-server-9.0.6.v20130930.jar

    • jetty-servlet-9.0.6.v20130930.jar

    • jetty-util-9.0.6.v20130930.jar

    • jetty-webapp-9.0.6.v20130930.jar

    • jetty-xml-9.0.6.v20130930.jar

    • org.apache.jasper.glassfish-2.2.2.v201112011158.jar

    • org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar

    • org.eclipse.jdt.core-3.8.2.v20130121.jar

    • 我的dependecies只是通过程序集插件添加的

    <dependencySet>
        <outputDirectory>lib</outputDirectory>
        <scope>runtime</scope>
    </dependencySet>
    
    • 我有一个shell启动脚本启动嵌入类,这就是我花了很多时间才弄清楚的 .

    I used a manifest with classpath embedded within the jar and setting my CLASSPATH=<PATH_TO_APP>\lib\EmbeddedApp.jar assuming the reset of the dependencies are part of my classpath through the manifest. 我遇到了同样无法解决的URI错误 .

    一旦我添加了更改我的脚本中的 CLASSPATH 变量以明确包含所有 jar 它开始工作 .

    for $ in $ / lib / * .jar;做CLASSPATH = $ jar:$ ; DONE

    希望这可以节省一些时间:-)

  • 0

    我从Surefire测试中启动Jetty时遇到了同样的问题;问题是Jetty 9没有查看任何jar文件的清单,除了在WEB-INF中,这与我编写测试的方式不兼容 .

    为了解决这个问题,我编写了一些代码来从清单中找到jar文件,并将它们放入一个新的中间URLClassLoader中 .

    这就是我的功能测试设置功能最终看起来是为了让它与Jetty 9一起工作:

    @Before @SuppressWarnings("unchecked")
    public void setUp() throws Exception {
    
        WebAppContext ctx = new WebAppContext("src/main/webapp", "/");
    
        Server server = new Server(9000);
    
        ctx.setServer(server);
        server.setHandler(ctx);
    
        ctx.preConfigure();
    
        ctx.addOverrideDescriptor("src/main/webapp/WEB-INF/tests-web.xml");      
    
        // Replace classloader with a new classloader with all URLs in manifests 
        // from the parent loader bubbled up so Jasper looks at them.
        ClassLoader contextClassLoader = ctx.getClassLoader();
        ClassLoader parentLoader = contextClassLoader.getParent();
        if (contextClassLoader instanceof WebAppClassLoader &&
            parentLoader instanceof URLClassLoader) {
          LinkedList<URL> allURLs =
              new LinkedList<URL>(Arrays.asList(((URLClassLoader)parentLoader).getURLs()));
          for (URL url : ((LinkedList<URL>)allURLs.clone())) {
            try {
              URLConnection conn = new URL("jar:" + url.toString() + "!/").openConnection();
              if (!(conn instanceof JarURLConnection))
                continue;
              JarURLConnection jconn = (JarURLConnection)conn;
              Manifest jarManifest = jconn.getManifest();
              String[] classPath = ((String)jarManifest.getMainAttributes().getValue("Class-Path")).split(" ");
    
              for (String cpurl : classPath)
                allURLs.add(new URL(url, cpurl));
            } catch (IOException e) {} catch (NullPointerException e) {}
          }
    
          ctx.setClassLoader(
              new WebAppClassLoader(
                  new URLClassLoader(allURLs.toArray(new URL[]{}), parentLoader),
                  ((WebAppClassLoader)contextClassLoader).getContext()));
        }
    
        server.start();
    }
    

    我的代码示例放在公共域中 - 您可以在自己的代码中使用它(归属归属但不是必需的) .

  • 0

    在使用a1kmm的解决方案摆弄并最终使用NullPointers后,我注意到了我没有在WebAppContext上设置类加载器 . 使用以下行我不再需要自定义类加载/清单扫描设置 .

    webAppContext.setClassLoader(new WebAppClassLoader(getClass().getClassLoader(), webAppContext));
    
  • 3

    我遇到了这个问题 . 我以最不寻常的方式解决了这个问题 .

    我使用Maven作为构建工具,但这是我如何构建自我执行WAR .

    <profile>
            <id>Jetty_9</id>
            <properties>
                <jetty9.version>9.0.4.v20130625</jetty9.version>
            </properties>
            <dependencies>
                <dependency>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                    <version>${logback.version}</version>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-access</artifactId>
                    <version>${logback.version}</version>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-core</artifactId>
                    <version>${logback.version}</version>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                    <version>${slf4j.version}</version>
                    <scope>provided</scope>
                </dependency>
    
                <dependency>
                    <groupId>org.eclipse.jetty.orbit</groupId>
                    <artifactId>javax.servlet</artifactId>
                    <version>3.0.0.v201112011016</version>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-webapp</artifactId>
                    <version>${jetty9.version}</version>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-plus</artifactId>
                    <version>${jetty9.version}</version>
                    <scope>provided</scope>
                </dependency>
                <dependency>
                    <groupId>org.eclipse.jetty</groupId>
                    <artifactId>jetty-jsp</artifactId>
                    <version>${jetty9.version}</version>
                </dependency>            
            </dependencies>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <version>2.3.2</version>
                        <configuration>
                            <source>${compileSource}</source>
                            <target>${compileSource}</target>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-antrun-plugin</artifactId>
                        <version>1.7</version>
                        <executions>
                            <execution>
                                <id>main-class-placement</id>
                                <phase>prepare-package</phase>
                                <configuration>
                                    <target>
                                        <move todir="${project.build.directory}/${project.build.finalName}/">
                                            <fileset dir="${project.build.directory}/classes/">
                                                <include name="Main.class"/>
                                            </fileset>
                                        </move>
                                    </target>
                                </configuration>
                                <goals>
                                    <goal>run</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-dependency-plugin</artifactId>
                        <version>2.6</version>
                        <executions>
                            <execution>
                                <id>jetty-classpath</id>
                                <phase>prepare-package</phase>
                                <goals>
                                    <goal>unpack-dependencies</goal>
                                </goals>
                                <configuration>
                                    <includeGroupIds>
                                        org.eclipse.jetty,org.slf4j,ch.qos
                                    </includeGroupIds>                                    
                                    <includeScope>provided</includeScope>
                                    <excludes>META-INF/*.SF,META-INF/*.RSA,about.html, about_files/**, readme.txt,
                                        plugin.properties, jetty-dir.css
                                    </excludes>
                                    <outputDirectory>
                                        ${project.build.directory}/${project.build.finalName}
                                    </outputDirectory>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.maven.plugins</groupId>
                        <artifactId>maven-war-plugin</artifactId>
                        <version>2.3</version>
                        <configuration>
                            <archive>
                                <manifest>
                                    <mainClass>Main</mainClass>
                                </manifest>
                            </archive>
                        </configuration>
                        <executions>
                            <execution>
                                <id>default-war</id>
                                <phase>package</phase>
                                <goals>
                                    <goal>war</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    

    但就像你自己一样,我得到了那个错误 . 谷歌搜索了2天后的死亡,我发现了这个 - http://internna.blogspot.co.uk/2011/08/step-by-step-executable-war-files.html

    通过为这一个切换jetty-jsp依赖:

    <dependency>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>jsp-2.1-glassfish</artifactId>
                <version>2.1.v20100127</version>
     </dependency>
    

    这一切都开始神奇地工作!

    到目前为止,我无法解释它为什么会起作用 . 但我很想知道

  • 3

    我刚刚使用ta taglib创建了简单的jsp . 然后创建服务器应用程序,它工作 . 所以,我认为它不是导致问题的taglib .

    public static void main(String[] args) throws Exception {
        Server server = new Server(8680);
        HandlerCollection handlers = new HandlerCollection();
        server.setHandler(handlers);
        ContextHandlerCollection chc = new ContextHandlerCollection();
        handlers.addHandler(chc);
        WebAppContext webapp = new WebAppContext();
        webapp.getInitParams().put("org.eclipse.jetty.servlet.Default.useFileMappedBuffer",    
        "false");
        webapp.setContextPath("/WebAppMng01");
        //URL url = ManagedServer.class.getResource("/WebAppMng01/build/web");
        URL url = ManagedServer.class.getResource("/WebAppMng01/dist/WebAppMng01.war");
        if (url != null) {
            webapp.setWar(url.toExternalForm());
            chc.addHandler(webapp);
        }
        server.start();
        server.join();
    }
    
  • 1

    可悲的是,到目前为止,没有一个答案对我有用 . 但我终于找到了解决问题的方法 . 这听起来像一个黑客,它肯定感觉像一个 .

    但是,如果我按照我在问题中描述的那样设置所有内容,直到JSTL无法解决,我可以采取一个步骤,使一切正常,就像魔术一样 .

    引起畏缩的步骤是将 .war 文件的扩展名更改为 .jar . 一旦我这样做,JSTL解决得很好,一切正常 .

    所以现在我生成一个 .war ,你可以将其放入servlet容器中,或者你可以重命名为 .jar 并独立运行 . 它可以在Unix和Windows上运行,而我以前这样做的方式在Unix上不起作用,因为詹姆斯库克提到的 jsp-2.1-glassfish 库中存在一个错误 .

    来自我的pom的相关部分:

    <properties>
        <jetty.version>9.0.5.v20130815</jetty.version>
        <war.class>com.domain.package.DeployWebapp</war.class>
        <war.class.path>com/domain/package</war.class.path>
    </properties>    
    
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-server</artifactId>
        <version>${jetty.version}</version>
        <scope>provided</scope>
    </dependency>
    
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-webapp</artifactId>
        <version>${jetty.version}</version>
        <scope>provided</scope>
    </dependency>
    
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-jsp</artifactId>
        <version>${jetty.version}</version>
        <scope>provided</scope>
    </dependency>
    
    <plugin>
        <artifactId>maven-antrun-plugin</artifactId>
        <version>1.7</version>
        <executions>
            <execution>
                <id>main-class-placement</id>
                <phase>prepare-package</phase>
                <configuration>
                    <tasks>
                        <move todir="${project.build.directory}/${project.artifactId}-${project.version}/${war.class.path}">
                        <fileset dir="${project.build.directory}/classes/${war.class.path}">
                            <include name="DeployWebapp.class" />
                        </fileset>
                    </move>
                            </tasks>
                    </configuration>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
        </executions>
    </plugin>
    
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.3</version>
        <executions>
            <execution>
                <id>jetty-classpath</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>unpack-dependencies</goal>
                </goals>
                <configuration>
                    <includeGroupIds>org.eclipse.jetty,javax.servlet,javax.el,org.glassfish.web,org.eclipse.jetty.orbit,org.ow2.asm,javax.annotation</includeGroupIds>
                    <outputDirectory>
                        ${project.build.directory}/${project.artifactId}-${project.version}
                    </outputDirectory>
                </configuration>
            </execution>
        </executions>
    </plugin>
    
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.4</version>
        <configuration>
            <attachClasses>true</attachClasses>
            <archiveClasses>true</archiveClasses>
            <archive>
                <manifest>
                    <mainClass>${war.class}</mainClass>
                </manifest>
            </archive>
            <packagingExcludes>META-INF/*.SF</packagingExcludes>
            <packagingExcludes>META-INF/*.DSA</packagingExcludes>
            <packagingExcludes>META-INF/*.RSA</packagingExcludes>
        </configuration>
    </plugin>
    
  • 1

    似乎嵌入式jetty 9不喜欢自动使用主可执行jar文件中的任何类路径条目 . 这包括taglib库 . 直接将类路径条目添加到webappclassloader似乎也不起作用 . 无论出于何种原因,类路径条目必须添加到webappclassloader的父级 .

    简单的解决方案对我不起作用:

    webAppContext.setClassLoader(new WebAppClassLoader(getClass().getClassLoader(), webAppContext));
    

    但手工扫描清单呢 . 我重新编写了上面的扫描示例,只是我可以看到发生了什么,并尝试不同的东西,我将在这里包括 .

    //========== create WebAppClassloader with a new parent classloader with all URLs in manifests=================== 
        //search for any secondary class path entries
        Vector<URL> secondayClassPathURLVector = new Vector<>();
        ClassLoader parentClassLoader = this.getClass().getClassLoader();
        if(parentClassLoader instanceof URLClassLoader)
        {
            URL[] existingURLs = ((URLClassLoader)parentClassLoader).getURLs(); 
            for (URL parentURL : existingURLs)
            {
                //if it doesn't end in .jar, then it's probably not going to have Manifest with a Class-Path entry 
                if(parentURL.toString().endsWith(".jar"))
                {
                    JarURLConnection jarURLConnection = (JarURLConnection) new URL("jar:" + parentURL.toString() + "!/").openConnection();
                    Manifest jarManifest = jarURLConnection.getManifest();
                    String classPath = jarManifest.getMainAttributes().getValue("Class-Path");
                    if(classPath != null)
                    {
                        //Iterate through all of the class path entries and create URLs out of them                                                
                        for (String part : classPath.split(" "))
                        {
                            //add our full path to the jar file to our classpath list
                            secondayClassPathURLVector.add(new URL(parentURL,part));                     
                        }
                    }
                }                
            }
        }
        //use our class path entries to create a parent for the webappclassloader that knows how to use or referenced class paths
        URLClassLoader internalClassPathUrlClassLoader = new URLClassLoader(secondayClassPathURLVector.toArray(new URL[secondayClassPathURLVector.size()]), parentClassLoader);
        //create a new webapp class loader as a child of our  internalClassPathUrlClassLoader. For whatever reason Jetty needs to have a WebAppClassLoader be it's main class loader,
        //while all of our classpath entries need to be added to the parent class loader   
        WebAppClassLoader webAppClassLoader = new WebAppClassLoader(internalClassPathUrlClassLoader,context);
        context.setClassLoader(webAppClassLoader);
    

    这应该替换嵌入式jetty-jsp示例,该示例因任何原因无效,但应该,因为任何类路径条目都应自动包含在类路径中 . 但它确实说,JSP需要一个NON系统类加载器 . 所以我只能假设JSP一旦到达System类路径加载器就停止扫描类路径 . 这就是为什么我们有效地必须替换它 .

  • 7
    WebAppContext context = new WebAppContext();      
    final URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
    if (url != null) {
        context.getMetaData().addWebInfJar(JarResource.newResource(url));
    }
    

    添加fat-jar作为WEB-INF jar,让 MetaInfConfiguration 找到* .tld文件 .

    参考:

相关问题