web-dev-qa-db-ja.com

JSTL URIを正常に解決するために、埋め込みJetty 9をどのように取得しますか?

メインクラスでこのコードを使用してJetty9の埋め込みコピーを起動することにより、シェルのJava -jar webapp.warを介して開始できるように、Webアプリケーションアーカイブ(.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-webapp、およびjetty-jsp、すべてのバージョン9.0.4.v20130625にのみ依存しています)
  • JSTLを直接指すwebappのweb.xmlファイルで明示的な<taglib>マッピングを指定する(JSP仕様を読んでこのアイデアを得た)
  • この回答 に従ってサーバーのクラスパスを変更する
  • addServerClasssetParentLoaderPriorityなどのWebAppContextのメソッドを利用する

Jettyのドキュメント によると、JSTLの使用は問題なく機能するはずですが、埋め込まれたコンテキストによってJSTLの読み込み方法が変更され、失敗する可能性があると思います。

アイデアや提案をいただければ幸いです。このセットアップは、Windowsで同じことを正常に実行したが、 このバグ をもたらした古い依存関係が含まれているため、Linuxでは機能していなかった古いセットアップを置き換えます。残念ながら、上記のJSTL URIスタックトレースを導入しない依存関係(groupId org.mortbay.jetty ArtifactId jsp-2.1-glassfishバージョン2.1.v20100127)の迅速な代替を見つけることができませんでした。

UPDATE:次善の解決策を見つけました。 このスレッド に触発されたJetty 7へのダウングレードにより、私は稼働状態になりました。これは素晴らしいニュースですが、後でJetty8またはJetty9専用の機能が必要になった場合に、この展開インフラストラクチャを廃棄しなければならないのは気が進まないことです。 Jetty9のJSTLtaglibの問題に関する洞察をいただければ幸いです。

14

悲しいことに、これまでのところ、どの答えもうまくいきませんでした。しかし、私はついに私の問題の解決策を見つけました。これはハックのように聞こえるでしょう、そしてそれは間違いなく1つのように感じます。

しかし、質問で説明したとおりに、JSTLが解決されなくなるまですべてをセットアップできれば、魔法のようにすべてを機能させるための1つのステップを実行できます。

そのしわを誘発するステップは、.warファイルの拡張子を.jarに変更することです。これを行うと、JSTLは問題なく解決され、すべてが機能します。

そのため、現在、サーブレットコンテナに貼り付けるか、名前を.warに変更してスタンドアロンで実行できる.jarを作成しています。また、UnixとWindowsの両方で機能しますが、JamesCookが言及したjsp-2.1-glassfishライブラリにバグが存在するため、以前の方法ではUnixでは機能しませんでした。

私の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

だからここにさらに別の解決策があります。私は非常によく似た問題に苦しんでいましたが、別のwarファイルと単純なembedderクラスがあり、Jettyサーバーを作成して起動し、おそらく任意のwarファイルを指します。これがすべてどのように機能するかです。

  1. WarファイルにはWEB-INF/libにtldライブラリがなく、ローダーミニアプリケーションから完全に分離されています。

  2. サーバーを起動して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>
    
  3. ローディングクラス自体は次のようになります。

    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();
    } 
    
  4. 私の場合、結果のパッケージは次のようになります。

    + EmbedderApp
    |
    +-- lib
      - EmbeddedApp.jar <-- JAR with the embedder class
      - 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
    
  5. 私の依存関係は、アセンブリプラグインを介して単純に追加されました。

    <dependencySet>
        <outputDirectory>lib</outputDirectory>
        <scope>runtime</scope>
    </dependencySet>
    
  6. 埋め込みクラスを起動するShellstartスクリプトがありますが、これが私が何年もかけて理解したものです。

    jar内にクラスパスが埋め込まれたマニフェストを使用し、依存関係のリセットがマニフェストを介したクラスパスの一部であると想定してCLASSPATH=<PATH_TO_APP>\lib\EmbeddedApp.jarを設定しました。そして、同じ解決できないURIエラーが発生しました。

    追加すると、スクリプト内のCLASSPATH変数が変更され、すべてのjarが明示的に含まれるようになりました。

    for jar in ${APP_ROOT}/lib/*.jar; do CLASSPATH=$jar:${CLASSPATH}; done 
    

これが誰かの時間を節約できることを願っています:-)

8
Jan Zyka

SurefireテストからJettyを起動するときに同じ問題が発生しました。問題は、Jetty 9がWEB-INFを除いて、jarファイルのマニフェストを調べないことでした。これは、テストの作成方法と互換性がありません。

この問題を回避するために、マニフェストからjarファイルを見つけて新しい中間URLClassLoaderに配置するコードを少し作成しました。

これは、私の機能テストセットアップ機能がJetty9で動作するようになってしまったものです。

@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();
}

私のコードサンプルはパブリックドメインに置かれています-あなたはそれをあなた自身のコードで使うことができます(帰属はありがたいですが必須ではありません)。

3
a1kmm

A1kmmのソリューションをいじって、NullPointersになってしまった後、WebAppContextにクラスローダーを設定していないことに気付きました。次の行を使用すると、カスタムのクラスローディング/マニフェストスキャンの設定は不要になります。

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

私はこの正確な問題を抱えていました。そして、私はそれを最も珍しい方法で解決しました。

ビルドツールとして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>

それはすべて魔法のように働き始めました!

現時点では、なぜそれが機能するのか説明できません。しかし、私は知りたいと思っています

2

使用するように、tataglibを使用して単純な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
Valery-Sh

埋め込まれたjetty9は、メインの実行可能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);

これは、何らかの理由で機能しないembedded-jetty-jspの例を置き換える必要がありますが、クラスパスエントリは自動的にクラスパスに含まれる必要があるためです。ただし、JSPには非システムクラスローダーが必要であるとは言えません。したがって、JSPがシステムクラスパスローダーに到達すると、クラスパスのスキャンを停止すると想定できます。そのため、効果的に交換する必要があります。

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

Fat-jarをWEB-INFjarとして追加し、MetaInfConfigurationに* .tldファイルを見つけさせます。

参照:

0
zhongl