Create a runnable war file using the WarMain from warbler
References #1
This commit is contained in:
parent
e71f3db21f
commit
f15e081468
|
@ -3,3 +3,4 @@
|
|||
.gemcache/
|
||||
vendor/
|
||||
.jarcache
|
||||
build/
|
||||
|
|
19
build.gradle
19
build.gradle
|
@ -1,5 +1,6 @@
|
|||
apply plugin: 'maven'
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'war'
|
||||
|
||||
repositories {
|
||||
// Pull in Maven Central for all our non-ruby dependencies
|
||||
|
@ -14,8 +15,10 @@ repositories {
|
|||
dependencies {
|
||||
runtime group: 'rubygems', name: 'sinatra', version: '1.4.5'
|
||||
runtime group: 'rubygems', name: 'rake', version: '10.3.+'
|
||||
runtime group: 'org.apache.kafka', name: 'kafka_2.9.2', version: '0.8.+'
|
||||
runtime group: 'rubygems', name: 'jruby-rack', version: '1.1.+'
|
||||
|
||||
runtime group: 'org.jruby', name: 'jruby-complete', version: '1.7.+'
|
||||
runtime group: 'org.apache.kafka', name: 'kafka_2.9.2', version: '0.8.+'
|
||||
// Needed otherwise we get `"Artifact 'com.sun.jdmk:jmxtools:1.2.1:jmxtools.jar' not found."`
|
||||
runtime group: 'log4j', name: 'log4j', version: '1.2.+', transitive: true
|
||||
}
|
||||
|
@ -59,4 +62,18 @@ task prepare {
|
|||
dependsOn cachejars, preparegems
|
||||
}
|
||||
|
||||
war {
|
||||
dependsOn prepare
|
||||
baseName 'rubygradle'
|
||||
from "$buildDir/classes/main"
|
||||
manifest { attributes 'Main-Class': 'WarMain' }
|
||||
|
||||
webInf {
|
||||
from 'vendor/gems', 'vendor/specifications', 'vendor/bin'
|
||||
into 'gems'
|
||||
}
|
||||
|
||||
metaInf { from "init.rb" }
|
||||
}
|
||||
|
||||
// vim: et ts=2 sw=2 autoindent ft=groovy
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
WARBLER_CONFIG = {"public.root"=>"/", "rack.env"=>"production", "jruby.compat.version"=>"1.9"}
|
||||
|
||||
if $servlet_context.nil?
|
||||
ENV['GEM_HOME'] ||= File.expand_path('../../WEB-INF', __FILE__)
|
||||
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../WEB-INF/Gemfile', __FILE__)
|
||||
|
||||
else
|
||||
ENV['GEM_HOME'] ||= $servlet_context.getRealPath('/WEB-INF/gems')
|
||||
|
||||
ENV['BUNDLE_GEMFILE'] ||= $servlet_context.getRealPath('/WEB-INF/Gemfile')
|
||||
|
||||
end
|
||||
ENV['BUNDLE_WITHOUT'] = 'development:test:assets'
|
||||
|
||||
ENV['RACK_ENV'] = 'production'
|
||||
|
||||
$LOAD_PATH.unshift $servlet_context.getRealPath('/WEB-INF') if $servlet_context
|
||||
|
2
run.sh
2
run.sh
|
@ -4,4 +4,4 @@
|
|||
export GEM_HOME=''
|
||||
export GEM_PATH=''
|
||||
|
||||
exec ./gradlew prepare pkg
|
||||
exec ./gradlew prepare war
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2012 Engine Yard, Inc.
|
||||
* Copyright (c) 2007-2009 Sun Microsystems, Inc.
|
||||
* This source code is available under the MIT license.
|
||||
* See the file LICENSE.txt for details.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class JarMain implements Runnable {
|
||||
|
||||
static final String MAIN = "/" + JarMain.class.getName().replace('.', '/') + ".class";
|
||||
|
||||
final boolean debug = isDebug();
|
||||
|
||||
protected final String[] args;
|
||||
protected final String archive;
|
||||
private final String path;
|
||||
|
||||
protected File extractRoot;
|
||||
|
||||
protected URLClassLoader classLoader;
|
||||
|
||||
JarMain(String[] args) {
|
||||
this.args = args;
|
||||
URL mainClass = getClass().getResource(MAIN);
|
||||
try {
|
||||
this.path = mainClass.toURI().getSchemeSpecificPart();
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
archive = this.path.replace("!" + MAIN, "").replace("file:", "");
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this));
|
||||
}
|
||||
|
||||
protected URL[] extractArchive() throws Exception {
|
||||
final JarFile jarFile = new JarFile(archive);
|
||||
try {
|
||||
Map<String, JarEntry> jarNames = new HashMap<String, JarEntry>();
|
||||
for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
|
||||
JarEntry entry = e.nextElement();
|
||||
String extractPath = getExtractEntryPath(entry);
|
||||
if ( extractPath != null ) jarNames.put(extractPath, entry);
|
||||
}
|
||||
|
||||
extractRoot = File.createTempFile("jruby", "extract");
|
||||
extractRoot.delete(); extractRoot.mkdirs();
|
||||
|
||||
final List<URL> urls = new ArrayList<URL>();
|
||||
for (Map.Entry<String, JarEntry> e : jarNames.entrySet()) {
|
||||
URL entryURL = extractEntry(e.getValue(), e.getKey());
|
||||
if (entryURL != null) urls.add( entryURL );
|
||||
}
|
||||
return (URL[]) urls.toArray(new URL[urls.size()]);
|
||||
}
|
||||
finally {
|
||||
jarFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected String getExtractEntryPath(final JarEntry entry) {
|
||||
final String name = entry.getName();
|
||||
if ( name.startsWith("META-INF/lib") && name.endsWith(".jar") ) {
|
||||
return name.substring(name.lastIndexOf("/") + 1);
|
||||
}
|
||||
return null; // do not extract entry
|
||||
}
|
||||
|
||||
protected URL extractEntry(final JarEntry entry, final String path) throws Exception {
|
||||
final File file = new File(extractRoot, path);
|
||||
if ( entry.isDirectory() ) {
|
||||
file.mkdirs();
|
||||
return null;
|
||||
}
|
||||
final String entryPath = entryPath(entry.getName());
|
||||
final InputStream entryStream;
|
||||
try {
|
||||
entryStream = new URI("jar", entryPath, null).toURL().openStream();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// TODO gems '%' file name "encoding" ?!
|
||||
debug("failed to open jar:" + entryPath + " skipping entry: " + entry.getName(), e);
|
||||
return null;
|
||||
}
|
||||
final File parent = file.getParentFile();
|
||||
if ( parent != null ) parent.mkdirs();
|
||||
FileOutputStream outStream = new FileOutputStream(file);
|
||||
final byte[] buf = new byte[65536];
|
||||
try {
|
||||
int bytesRead = 0;
|
||||
while ((bytesRead = entryStream.read(buf)) != -1) {
|
||||
outStream.write(buf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
entryStream.close();
|
||||
outStream.close();
|
||||
file.deleteOnExit();
|
||||
}
|
||||
if (false) debug(entry.getName() + " extracted to " + file.getPath());
|
||||
return file.toURI().toURL();
|
||||
}
|
||||
|
||||
protected String entryPath(String name) {
|
||||
if ( ! name.startsWith("/") ) name = "/" + name;
|
||||
return path.replace(MAIN, name);
|
||||
}
|
||||
|
||||
protected Object newScriptingContainer(final URL[] jars) throws Exception {
|
||||
System.setProperty("org.jruby.embed.class.path", "");
|
||||
classLoader = new URLClassLoader(jars);
|
||||
Class scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, classLoader);
|
||||
Object scriptingContainer = scriptingContainerClass.newInstance();
|
||||
debug("scripting container class loader urls: " + Arrays.toString(jars));
|
||||
invokeMethod(scriptingContainer, "setArgv", (Object) args);
|
||||
invokeMethod(scriptingContainer, "setClassLoader", new Class[] { ClassLoader.class }, classLoader);
|
||||
return scriptingContainer;
|
||||
}
|
||||
|
||||
protected int launchJRuby(final URL[] jars) throws Exception {
|
||||
final Object scriptingContainer = newScriptingContainer(jars);
|
||||
debug("invoking " + archive + " with: " + Arrays.deepToString(args));
|
||||
Object outcome = invokeMethod(scriptingContainer, "runScriptlet", launchScript());
|
||||
return ( outcome instanceof Number ) ? ( (Number) outcome ).intValue() : 0;
|
||||
}
|
||||
|
||||
protected String launchScript() {
|
||||
return
|
||||
"begin\n" +
|
||||
" require 'META-INF/init.rb'\n" +
|
||||
" require 'META-INF/main.rb'\n" +
|
||||
" 0\n" +
|
||||
"rescue SystemExit => e\n" +
|
||||
" e.status\n" +
|
||||
"end";
|
||||
}
|
||||
|
||||
protected int start() throws Exception {
|
||||
final URL[] jars = extractArchive();
|
||||
return launchJRuby(jars);
|
||||
}
|
||||
|
||||
protected void debug(String msg) {
|
||||
debug(msg, null);
|
||||
}
|
||||
|
||||
protected void debug(String msg, Throwable t) {
|
||||
if (debug) System.out.println(msg);
|
||||
if (debug && t != null) t.printStackTrace(System.out);
|
||||
}
|
||||
|
||||
protected void warn(String msg) {
|
||||
System.out.println("WARNING: " + msg);
|
||||
}
|
||||
|
||||
protected void delete(File f) {
|
||||
try {
|
||||
if (f.isDirectory() && !isSymlink(f)) {
|
||||
File[] children = f.listFiles();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
delete(children[i]);
|
||||
}
|
||||
}
|
||||
f.delete();
|
||||
} catch (IOException e) {
|
||||
System.err.println("error: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isSymlink(File file) throws IOException {
|
||||
if (file == null)
|
||||
throw new NullPointerException("File must not be null");
|
||||
File canon;
|
||||
if (file.getParent() == null) {
|
||||
canon = file;
|
||||
} else {
|
||||
File canonDir = file.getParentFile().getCanonicalFile();
|
||||
canon = new File(canonDir, file.getName());
|
||||
}
|
||||
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// If the URLClassLoader isn't closed, on Windows, temp JARs won't be cleaned up
|
||||
try {
|
||||
invokeMethod(classLoader, "close");
|
||||
}
|
||||
catch(NoSuchMethodException e) { } // We're not being run on Java >= 7
|
||||
catch(Exception e) {
|
||||
System.err.println("error: " + e.toString());
|
||||
}
|
||||
|
||||
if ( extractRoot != null ) delete(extractRoot);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
doStart(new JarMain(args));
|
||||
}
|
||||
|
||||
protected static void doStart(final JarMain main) {
|
||||
try {
|
||||
int exit = main.start();
|
||||
if(isSystemExitEnabled()) System.exit(exit);
|
||||
} catch (Exception e) {
|
||||
System.err.println("error: " + e.toString());
|
||||
Throwable t = e;
|
||||
while (t.getCause() != null && t.getCause() != t) {
|
||||
t = t.getCause();
|
||||
}
|
||||
if (isDebug()) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
protected static Object invokeMethod(final Object self, final String name, final Object... args)
|
||||
throws NoSuchMethodException, IllegalAccessException, Exception {
|
||||
|
||||
final Class[] signature = new Class[args.length];
|
||||
for ( int i = 0; i < args.length; i++ ) signature[i] = args[i].getClass();
|
||||
return invokeMethod(self, name, signature, args);
|
||||
}
|
||||
|
||||
protected static Object invokeMethod(final Object self, final String name, final Class[] signature, final Object... args)
|
||||
throws NoSuchMethodException, IllegalAccessException, Exception {
|
||||
Method method = self.getClass().getDeclaredMethod(name, signature);
|
||||
try {
|
||||
return method.invoke(self, args);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Throwable target = e.getTargetException();
|
||||
if (target instanceof Exception) {
|
||||
throw (Exception) target;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isDebug() {
|
||||
return Boolean.getBoolean("warbler.debug");
|
||||
}
|
||||
|
||||
/**
|
||||
* if warbler.skip_system_exit system property is defined, we will not
|
||||
* call System.exit in the normal flow. System.exit can cause problems
|
||||
* for wrappers like procrun
|
||||
*/
|
||||
private static boolean isSystemExitEnabled(){
|
||||
return System.getProperty("warbler.skip_system_exit") == null; //omission enables System.exit use
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,372 @@
|
|||
/**
|
||||
* Copyright (c) 2010-2012 Engine Yard, Inc.
|
||||
* Copyright (c) 2007-2009 Sun Microsystems, Inc.
|
||||
* This source code is available under the MIT license.
|
||||
* See the file LICENSE.txt for details.
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.io.InputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.SequenceInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URLClassLoader;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used as a Main-Class in the manifest for a .war file, so that you can run
|
||||
* a .war file with <tt>java -jar</tt>.
|
||||
*
|
||||
* WarMain can be used with different web server libraries. WarMain expects
|
||||
* to have two files present in the .war file,
|
||||
* <tt>WEB-INF/webserver.properties</tt> and <tt>WEB-INF/webserver.jar</tt>.
|
||||
*
|
||||
* When WarMain starts up, it extracts the webserver jar to a temporary
|
||||
* directory, and creates a temporary work directory for the webapp. Both
|
||||
* are deleted on exit.
|
||||
*
|
||||
* It then reads webserver.properties into a java.util.Properties object,
|
||||
* creates a URL classloader holding the jar, and loads and invokes the
|
||||
* <tt>main</tt> method of the main class mentioned in the properties.
|
||||
*
|
||||
* An example webserver.properties follows for Winstone. The <tt>args</tt>
|
||||
* property indicates the names and ordering of other properties to be used
|
||||
* as command-line arguments. The special tokens <tt>{{warfile}}</tt> and
|
||||
* <tt>{{webroot}}</tt> are substituted with the location of the .war file
|
||||
* being run and the temporary work directory, respectively.
|
||||
* <pre>
|
||||
* mainclass = winstone.Launcher
|
||||
* args = args0,args1,args2
|
||||
* args0 = --warfile={{warfile}}
|
||||
* args1 = --webroot={{webroot}}
|
||||
* args2 = --directoryListings=false
|
||||
* </pre>
|
||||
*
|
||||
* System properties can also be set via webserver.properties. For example,
|
||||
* the following entries set <tt>jetty.home</tt> before launching the server.
|
||||
* <pre>
|
||||
* props = jetty.home
|
||||
* jetty.home = {{webroot}}
|
||||
* </pre>
|
||||
*/
|
||||
public class WarMain extends JarMain {
|
||||
|
||||
static final String MAIN = "/" + WarMain.class.getName().replace('.', '/') + ".class";
|
||||
static final String WEBSERVER_PROPERTIES = "/WEB-INF/webserver.properties";
|
||||
static final String WEBSERVER_JAR = "/WEB-INF/webserver.jar";
|
||||
|
||||
/**
|
||||
* jruby arguments, consider the following command :
|
||||
* `java -jar rails.war --1.9 -S rake db:migrate`
|
||||
* arguments == [ "--1.9" ]
|
||||
* executable == "rake"
|
||||
* executableArgv == [ "db:migrate" ]
|
||||
*/
|
||||
private final String[] arguments;
|
||||
|
||||
/**
|
||||
* null to launch webserver or != null to run a executable e.g. rake
|
||||
*/
|
||||
private final String executable;
|
||||
private final String[] executableArgv;
|
||||
|
||||
private File webroot;
|
||||
|
||||
WarMain(final String[] args) {
|
||||
super(args);
|
||||
final List<String> argsList = Arrays.asList(args);
|
||||
final int sIndex = argsList.indexOf("-S");
|
||||
if ( sIndex == -1 ) {
|
||||
executable = null; executableArgv = null; arguments = null;
|
||||
}
|
||||
else {
|
||||
if ( args.length == sIndex + 1 || args[sIndex + 1].isEmpty() ) {
|
||||
throw new IllegalArgumentException("missing executable after -S");
|
||||
}
|
||||
arguments = argsList.subList(0, sIndex).toArray(new String[0]);
|
||||
String execArg = argsList.get(sIndex + 1);
|
||||
executableArgv = argsList.subList(sIndex + 2, argsList.size()).toArray(new String[0]);
|
||||
|
||||
if (execArg.equals("bundle") && executableArgv.length > 0 && executableArgv[0].equals("exec")) {
|
||||
warn("`bundle exec' may drop out of the Warbler environment and into the system environment");
|
||||
} else if (execArg.equals("rails")) {
|
||||
// The rails executable doesn't play well with ScriptingContainer, so we've packaged the
|
||||
// same script that would have been generated by `rake rails:update:bin`
|
||||
execArg = "./META-INF/rails.rb";
|
||||
}
|
||||
|
||||
executable = execArg;
|
||||
}
|
||||
}
|
||||
|
||||
private URL extractWebserver() throws Exception {
|
||||
this.webroot = File.createTempFile("warbler", "webroot");
|
||||
this.webroot.delete();
|
||||
this.webroot.mkdirs();
|
||||
this.webroot = new File(this.webroot, new File(archive).getName());
|
||||
debug("webroot directory is " + this.webroot.getPath());
|
||||
InputStream jarStream = new URI("jar", entryPath(WEBSERVER_JAR), null).toURL().openStream();
|
||||
File jarFile = File.createTempFile("webserver", ".jar");
|
||||
jarFile.deleteOnExit();
|
||||
FileOutputStream outStream = new FileOutputStream(jarFile);
|
||||
try {
|
||||
byte[] buf = new byte[4096];
|
||||
int bytesRead = 0;
|
||||
while ((bytesRead = jarStream.read(buf)) != -1) {
|
||||
outStream.write(buf, 0, bytesRead);
|
||||
}
|
||||
} finally {
|
||||
jarStream.close();
|
||||
outStream.close();
|
||||
}
|
||||
debug("webserver.jar extracted to " + jarFile.getPath());
|
||||
return jarFile.toURI().toURL();
|
||||
}
|
||||
|
||||
private Properties getWebserverProperties() throws Exception {
|
||||
Properties props = new Properties();
|
||||
try {
|
||||
InputStream is = getClass().getResourceAsStream(WEBSERVER_PROPERTIES);
|
||||
if ( is != null ) props.load(is);
|
||||
} catch (Exception e) { }
|
||||
|
||||
for (Map.Entry entry : props.entrySet()) {
|
||||
String val = (String) entry.getValue();
|
||||
val = val.replace("{{warfile}}", archive).replace("{{webroot}}", webroot.getAbsolutePath());
|
||||
entry.setValue(val);
|
||||
}
|
||||
|
||||
if (props.getProperty("props") != null) {
|
||||
String[] propsToSet = props.getProperty("props").split(",");
|
||||
for (String key : propsToSet) {
|
||||
System.setProperty(key, props.getProperty(key));
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
private void launchWebServer(URL jar) throws Exception {
|
||||
URLClassLoader loader = new URLClassLoader(new URL[] {jar});
|
||||
Thread.currentThread().setContextClassLoader(loader);
|
||||
Properties props = getWebserverProperties();
|
||||
String mainClass = props.getProperty("mainclass");
|
||||
if (mainClass == null) {
|
||||
throw new IllegalArgumentException("unknown webserver main class ("
|
||||
+ WEBSERVER_PROPERTIES
|
||||
+ " is missing 'mainclass' property)");
|
||||
}
|
||||
Class klass = Class.forName(mainClass, true, loader);
|
||||
Method main = klass.getDeclaredMethod("main", new Class[] { String[].class });
|
||||
String[] newArgs = launchWebServerArguments(props);
|
||||
debug("invoking webserver with: " + Arrays.deepToString(newArgs));
|
||||
main.invoke(null, new Object[] { newArgs });
|
||||
|
||||
// the following code is specific to winstone. but a whole winstone module like the jetty module seemed
|
||||
// excessive. if running under jetty (or anything other than wintstone) this will effectively do nothing.
|
||||
Set<Thread> threads = Thread.getAllStackTraces().keySet();
|
||||
for (Thread thread : threads) {
|
||||
String name = thread.getName();
|
||||
if (name.startsWith("LauncherControlThread")) {
|
||||
debug("joining thread: " + name);
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String[] launchWebServerArguments(Properties props) {
|
||||
String[] newArgs = args;
|
||||
|
||||
if (props.getProperty("args") != null) {
|
||||
String[] insertArgs = props.getProperty("args").split(",");
|
||||
newArgs = new String[args.length + insertArgs.length];
|
||||
for (int i = 0; i < insertArgs.length; i++) {
|
||||
newArgs[i] = props.getProperty(insertArgs[i], "");
|
||||
}
|
||||
System.arraycopy(args, 0, newArgs, insertArgs.length, args.length);
|
||||
}
|
||||
|
||||
return newArgs;
|
||||
}
|
||||
|
||||
// JarMain overrides to make WarMain "launchable"
|
||||
// e.g. java -jar rails.war -S rake db:migrate
|
||||
|
||||
@Override
|
||||
protected String getExtractEntryPath(final JarEntry entry) {
|
||||
final String name = entry.getName();
|
||||
final String start = "WEB-INF";
|
||||
if ( name.startsWith(start) ) {
|
||||
// WEB-INF/app/controllers/application_controller.rb ->
|
||||
// app/controllers/application_controller.rb
|
||||
return name.substring(start.length());
|
||||
}
|
||||
if ( name.indexOf('/') == -1 ) {
|
||||
// 404.html -> public/404.html
|
||||
return "/public/" + name;
|
||||
}
|
||||
return "/" + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URL extractEntry(final JarEntry entry, final String path) throws Exception {
|
||||
// always extract but only return class-path entry URLs :
|
||||
final URL entryURL = super.extractEntry(entry, path);
|
||||
return path.endsWith(".jar") ? entryURL : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int launchJRuby(final URL[] jars) throws Exception {
|
||||
final Object scriptingContainer = newScriptingContainer(jars);
|
||||
|
||||
invokeMethod(scriptingContainer, "setArgv", (Object) executableArgv);
|
||||
invokeMethod(scriptingContainer, "setCurrentDirectory", extractRoot.getAbsolutePath());
|
||||
initJRubyScriptingEnv(scriptingContainer, jars);
|
||||
|
||||
final Object provider = invokeMethod(scriptingContainer, "getProvider");
|
||||
final Object rubyInstanceConfig = invokeMethod(provider, "getRubyInstanceConfig");
|
||||
|
||||
invokeMethod(rubyInstanceConfig, "setUpdateNativeENVEnabled", new Class[] { Boolean.TYPE }, false);
|
||||
|
||||
final String executablePath = locateExecutable(scriptingContainer);
|
||||
if ( executablePath == null ) {
|
||||
throw new IllegalStateException("failed to locate gem executable: '" + executable + "'");
|
||||
}
|
||||
invokeMethod(scriptingContainer, "setScriptFilename", executablePath);
|
||||
|
||||
invokeMethod(rubyInstanceConfig, "processArguments", (Object) arguments);
|
||||
|
||||
Object runtime = invokeMethod(scriptingContainer, "getRuntime");
|
||||
Object executableInput =
|
||||
new SequenceInputStream(new ByteArrayInputStream(executableScriptEnvPrefix().getBytes()),
|
||||
(InputStream) invokeMethod(rubyInstanceConfig, "getScriptSource"));
|
||||
|
||||
debug("invoking " + executablePath + " with: " + Arrays.toString(executableArgv));
|
||||
|
||||
Object outcome = invokeMethod(runtime, "runFromMain",
|
||||
new Class[] { InputStream.class, String.class },
|
||||
executableInput, executablePath
|
||||
);
|
||||
return ( outcome instanceof Number ) ? ( (Number) outcome ).intValue() : 0;
|
||||
}
|
||||
|
||||
protected String locateExecutable(final Object scriptingContainer) throws Exception {
|
||||
if ( executable == null ) {
|
||||
throw new IllegalStateException("no executable");
|
||||
}
|
||||
final File exec = new File(extractRoot, executable);
|
||||
if ( exec.exists() ) {
|
||||
return exec.getAbsolutePath();
|
||||
}
|
||||
else {
|
||||
final String script = locateExecutableScript(executable);
|
||||
return (String) invokeMethod(scriptingContainer, "runScriptlet", script);
|
||||
}
|
||||
}
|
||||
protected String executableScriptEnvPrefix() {
|
||||
final String gemsDir = new File(extractRoot, "gems").getAbsolutePath();
|
||||
final String gemfile = new File(extractRoot, "Gemfile").getAbsolutePath();
|
||||
debug("setting GEM_HOME to " + gemsDir);
|
||||
debug("... and BUNDLE_GEMFILE to " + gemfile);
|
||||
|
||||
// ideally this would look up the config.override_gem_home setting
|
||||
return "ENV['GEM_HOME'] = ENV['GEM_PATH'] = '"+ gemsDir +"' \n" +
|
||||
"ENV['BUNDLE_GEMFILE'] ||= '"+ gemfile +"' \n" +
|
||||
"require 'META-INF/init.rb' \n";
|
||||
}
|
||||
|
||||
protected String locateExecutableScript(final String executable) {
|
||||
return executableScriptEnvPrefix() +
|
||||
"begin\n" +
|
||||
// locate the executable within gemspecs :
|
||||
" require 'rubygems' \n" +
|
||||
" begin\n" +
|
||||
// add bundler gems to load path:
|
||||
" require 'bundler' \n" +
|
||||
// TODO: environment from web.xml. Any others?
|
||||
" Bundler.setup(:default, *ENV.values_at('RACK_ENV', 'RAILS_ENV').compact)\n" +
|
||||
" rescue LoadError\n" +
|
||||
// bundler not used
|
||||
" end\n" +
|
||||
" exec = '"+ executable +"' \n" +
|
||||
" spec = Gem::Specification.find { |s| s.executables.include?(exec) } \n" +
|
||||
" spec ? spec.bin_file(exec) : nil \n" +
|
||||
// returns the full path to the executable
|
||||
"rescue SystemExit => e\n" +
|
||||
" e.status\n" +
|
||||
"end";
|
||||
}
|
||||
|
||||
protected void initJRubyScriptingEnv(Object scriptingContainer, final URL[] jars) throws Exception {
|
||||
String jrubyStdlibJar = "";
|
||||
String bcpkixJar = "";
|
||||
String bcprovJar = "";
|
||||
for (URL url : jars) {
|
||||
if (url.toString().matches("file:/.*jruby-stdlib-.*jar")) {
|
||||
jrubyStdlibJar = url.toString();
|
||||
debug("using jruby-stdlib: " + jrubyStdlibJar);
|
||||
} else if (url.toString().matches("file:/.*bcpkix-jdk15on-.*jar")) {
|
||||
bcpkixJar = url.toString();
|
||||
debug("using bcpkix: " + bcpkixJar);
|
||||
} else if (url.toString().matches("file:/.*bcprov-jdk15on-.*jar")) {
|
||||
bcprovJar = url.toString();
|
||||
debug("using bcprov: " + bcprovJar);
|
||||
}
|
||||
}
|
||||
|
||||
invokeMethod(scriptingContainer, "runScriptlet", "" +
|
||||
"ruby = RUBY_VERSION.match(/^\\d\\.\\d/)[0] \n" +
|
||||
"jruby_major_version = JRUBY_VERSION.match(/^\\d\\.\\d/)[0].to_f \n" +
|
||||
"jruby_minor_version = JRUBY_VERSION.split('.')[2].to_i\n" +
|
||||
"$: << \"" + jrubyStdlibJar + "!/META-INF/jruby.home/lib/ruby/#{ruby}/site_ruby\"\n" +
|
||||
"$: << \"" + jrubyStdlibJar + "!/META-INF/jruby.home/lib/ruby/shared\"\n" +
|
||||
"$: << \"" + jrubyStdlibJar + "!/META-INF/jruby.home/lib/ruby/#{ruby}\"\n" +
|
||||
"if jruby_major_version >= 1.7 and jruby_minor_version < 13\n" +
|
||||
" require \"" + bcpkixJar + "\".gsub('file:', '') unless \"" + bcpkixJar + "\".empty?\n" +
|
||||
" require \"" + bcprovJar + "\".gsub('file:', '') unless \"" + bcprovJar + "\".empty?\n" +
|
||||
"end");
|
||||
|
||||
invokeMethod(scriptingContainer, "setHomeDirectory", "classpath:/META-INF/jruby.home");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int start() throws Exception {
|
||||
if ( executable == null ) {
|
||||
try {
|
||||
URL server = extractWebserver();
|
||||
launchWebServer(server);
|
||||
}
|
||||
catch (FileNotFoundException e) {
|
||||
if ( e.getMessage().indexOf("WEB-INF/webserver.jar") > -1 ) {
|
||||
System.out.println("specify the -S argument followed by the bin file to run e.g. `java -jar rails.war -S rake -T` ...");
|
||||
System.out.println("(or if you'd like your .war file to start a web server package it using `warbler executable war`)");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return super.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
if ( webroot != null ) delete(webroot.getParentFile());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
doStart(new WarMain(args));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue