From b327982f49172604c38ca3ca7af28b49b3407102 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Fri, 11 Jan 2013 17:10:24 +0100 Subject: o New feature: Ability to run web apps from nested classpath (foo.jar!/my-webapp/WEB-INF/web.xml). --- README.md | 25 ++++++++ .../trygvis/appsh/booter/jetty/JettyWebServer.java | 67 +++++++++++++++++++++- .../java/io/trygvis/appsh/booter/jetty/Main.java | 14 ++++- 3 files changed, 103 insertions(+), 3 deletions(-) mode change 100644 => 100755 README.md mode change 100644 => 100755 src/main/java/io/trygvis/appsh/booter/jetty/JettyWebServer.java mode change 100644 => 100755 src/main/java/io/trygvis/appsh/booter/jetty/Main.java diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 5d767f8..695755e --- a/README.md +++ b/README.md @@ -1,5 +1,30 @@ +# Usage + +This Jetty-based WAR/web application booter is configured through a properties files and environment properties. It +depends on a specific Jetty version. + +## Changing the version of Jetty used + +Feel free to use Maven's dependency management to override the version that this plugin depend on. Note that it has +some sets of dependencies. The major one is Jetty itself. This version is reflected in the booter's version itself. The +rest of the dependencies are based on what the Jetty version itself uses. + +If you change the Jetty version you should probably download the Jetty distribution for the new version and make sure +that the versions of the artifacts that the booter depend on match. + # Change Log +## 8.1.8.v20121106+2 + +### Support for running web apps from inside a JAR file + +Example `booter.properties`: + + context./=classpath:/webapp + +Note that because of the current JSP implementation the webapp has to be extracted for the JSP compiler to be able to +find the files. It also requires a temporary area to write the class files. + ## 8.1.8.v20121106+1 - 2012-12-20 Upgrading to 8.1.8. diff --git a/src/main/java/io/trygvis/appsh/booter/jetty/JettyWebServer.java b/src/main/java/io/trygvis/appsh/booter/jetty/JettyWebServer.java old mode 100644 new mode 100755 index 6a7b63a..03fb17c --- a/src/main/java/io/trygvis/appsh/booter/jetty/JettyWebServer.java +++ b/src/main/java/io/trygvis/appsh/booter/jetty/JettyWebServer.java @@ -4,14 +4,24 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.StdErrLog; +import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import java.io.File; import java.io.IOException; +import java.net.URL; import java.util.ArrayList; +import java.util.Enumeration; import java.util.List; +/** + * A web server that's expected to be used from main() methods. + * + * The main method is expected to know how to configure the server. It will use System.err and System.exit(-1) on + * errors. + */ public class JettyWebServer { private File basedir = new File("").getAbsoluteFile(); private File tmp, extraClasspath; @@ -35,6 +45,59 @@ public class JettyWebServer { return context; } + /** + * Adds a web application context from within the classpath. + * + * To get JSPs to work the app has to be extracted + * + * @param contextPath Where to "mount" the application, for example /myapp + * @param war Where there extracted files are places. Will be cleaned out before starting. + * @param prefix Where to start the classpath scan, for example "/my-awesome-webapp". + */ + public Context addClasspathContext(String contextPath, File war, String prefix) throws Exception { + war = war.getAbsoluteFile(); + + // Clean out any old cruft + if (war.isDirectory()) { + IO.delete(war); + } + + if (!war.mkdirs()) { + System.err.println("Could not create directory: " + war); + System.exit(-1); + } + + // Classloaders doesn't like slash prefixed searches. + if (prefix.startsWith("/")) { + prefix = prefix.substring(1); + } + + Enumeration enumeration = Main.class.getClassLoader().getResources(prefix); + + // TODO: just check for presence of WEB-INF/web.xml after extraction. + if (!enumeration.hasMoreElements()) { + System.err.println("Could not look up classpath resource: '" + prefix + "'."); + System.exit(-1); + } + + while (enumeration.hasMoreElements()) { + URL url = enumeration.nextElement(); + + try { + Resource resource = Resource.newResource(url); + + resource.copyTo(war); + } catch (IOException e) { + System.err.println("Unable to extract " + url.toExternalForm() + " to " + war); + } + } + + return addContext(contextPath, war); + } + + /** + * This should be moved to the main method. JettyWebServer should only be code to set up Jetty. + */ public void run() throws Exception { tmp = new File(basedir, "tmp"); @@ -65,7 +128,7 @@ public class JettyWebServer { ContextHandlerCollection handler = new ContextHandlerCollection(); server.setHandler(handler); for (Context context : contexts) { - handler.addHandler(context.toJetty()); + handler.addHandler(context.toJetty(tmp)); } server.start(); @@ -98,7 +161,7 @@ public class JettyWebServer { * By setting useFileMappedBuffer we prevent Jetty from memory-mapping the static resources * * The default servlet can be configured by setting parameters on the context in addition to the default - * servlet's init params, saving us from injecting the DefaultServlet programatically. + * servlet's init params, saving us from injecting the DefaultServlet programmatically. * * http://stackoverflow.com/questions/184312/how-to-make-jetty-dynamically-load-static-pages */ diff --git a/src/main/java/io/trygvis/appsh/booter/jetty/Main.java b/src/main/java/io/trygvis/appsh/booter/jetty/Main.java old mode 100644 new mode 100755 index d3920c7..1f2c58f --- a/src/main/java/io/trygvis/appsh/booter/jetty/Main.java +++ b/src/main/java/io/trygvis/appsh/booter/jetty/Main.java @@ -35,6 +35,7 @@ public class Main { IO.close(is); } + // TODO: This should copy the output to the old std out until we have started if possible. setStreams(basedir, properties); JettyWebServer server; @@ -47,7 +48,18 @@ public class Main { String value = entry.getValue().toString(); if (key.startsWith("context.")) { - server.addContext(key.substring(8), new File(basedir, value)); + String contextPath = key.substring(8); + + if (value.startsWith("classpath:")) { + value = value.substring(10); + + File war = new File("wat"); + + server.addClasspathContext(contextPath, war, value); + } + else { + server.addContext(contextPath, new File(basedir, value)); + } } } -- cgit v1.2.3