我正在打包Web应用程序存档(.war),以便可以通过shell中的java -jar webapp.war
启动它,方法是在主类中使用此代码启动Jetty 9的嵌入式副本:
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-server
,jetty-webapp
和jetty-jsp
,所有版本9.0.4.v20130625
)<taglib>
映射(通过阅读JSP规范获得此想法)addServerClass
和setParentLoaderPriority
根据Jetty's documentation,使用JSTL应该可行,但我认为嵌入式上下文可能正在改变JSTL加载的方式并导致它失败。
感谢任何想法或建议。此设置将替换在Windows上成功执行相同操作但在Linux上无法运行的旧设置,因为包含了引入this bug的旧依赖项。不幸的是,我无法找到一个快速替换该依赖项(groupId org.mortbay.jetty
artifactId jsp-2.1-glassfish
版本2.1.v20100127
),但没有引入上面提到的JSTL URI堆栈跟踪。 / p>
更新:我发现了一个次优的解决方案。由this thread启发的降级到Jetty 7现在让我起步并运行。这是一个好消息,但令人沮丧的是,如果我后来需要Jetty 8或Jetty 9独有的任何功能,我必须废弃这个部署基础架构。任何关于Jetty 9中JSTL taglib问题的见解仍然会受到赞赏。
答案 0 :(得分:8)
所以这是另一个解决方案。我正在努力解决非常类似的问题只有我有一个单独的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 <-- 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
我的dependecies只是通过程序集插件添加为
<dependencySet>
<outputDirectory>lib</outputDirectory>
<scope>runtime</scope>
</dependencySet>
我有一个shell启动脚本启动嵌入类,这就是我花了很多时间才弄清楚的。
我使用了jar中嵌入了类路径的清单,并设置了我的CLASSPATH=<PATH_TO_APP>\lib\EmbeddedApp.jar
,假设依赖项的重置是通过清单的类路径的一部分。而且我的gettign同样无法解析URI错误。
一旦我添加了更改我的脚本中的CLASSPATH
变量以明确包含所有jar,它就开始工作了。
for jar in ${APP_ROOT}/lib/*.jar; do CLASSPATH=$jar:${CLASSPATH}; done
希望这可以节省时间: - )
答案 1 :(得分:3)
我从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();
}
我的代码示例放在公共领域 - 您可以在自己的代码中使用它(归属归属但不是必需的。)
答案 2 :(得分:3)
在摆弄a1kmm的解决方案并最终使用NullPointers后,我注意到我没有在WebAppContext上设置类加载器。使用以下行我不再需要自定义类加载/清单扫描设置。
webAppContext.setClassLoader(new WebAppClassLoader(getClass().getClassLoader(), webAppContext));
答案 3 :(得分:2)
我遇到了这个问题。我以最不寻常的方式解决了它。
我使用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>
但是就像你自己一样,我得到了那个错误。两天后谷歌搜索死亡后我发现了这个 - 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>
一切都开始神奇地工作了!
到目前为止,我无法解释它为什么会起作用。但我很想找出
答案 4 :(得分:1)
我刚刚使用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();
}
答案 5 :(得分:1)
但是,如果我按照我在问题中描述的那样设置所有内容,直到JSTL无法解决,我可以采取一步使一切正常,就像魔术一样。
引起畏缩的步骤正在将.war
文件的扩展名更改为.jar
。一旦我这样做,JSTL解决得很好,一切正常。
所以现在我生成一个.war
,你可以将其粘贴到一个servlet容器中,或者你可以重命名为.jar
并独立运行。它适用于Unix和Windows,而之前我这样做的方式因为James Cook提到的jsp-2.1-glassfish
库中存在错误而无法在Unix上运行。
我朋友的相关信息:
<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>
答案 6 :(得分:0)
似乎嵌入式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 :(得分:0)
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文件。
参考: