Skip to content

Commit ff10266

Browse files
committed
Merge remote-tracking branch 'kares/runnable' into gh-130
Conflicts: .gitignore Gemfile ext/JarMain.java spec/sample_war/Rakefile
2 parents e5e41e8 + 509b71c commit ff10266

17 files changed

+391
-181
lines changed

Gemfile

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
source "http://rubygems.org/"
22

3-
gem "rake"
4-
gem "rubyzip"
5-
gem "jruby-jars"
6-
gem "jruby-rack"
3+
gemspec
74

85
group :development do
96
gem "jruby-openssl", :platform => :jruby

Rakefile

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
begin
99
require 'bundler/setup'
10-
rescue LoadError
11-
puts $!
12-
puts "Please install Bundler and run 'bundle install' to ensure you have all dependencies"
10+
rescue LoadError => e
11+
require('rubygems') && retry
12+
puts "Please `gem install bundler' and run `bundle install' to ensure you have all dependencies"
13+
raise e
1314
end
1415

1516
require 'bundler/gem_helper'

ext/JarMain.java

+152-71
Original file line numberDiff line numberDiff line change
@@ -8,111 +8,164 @@
88
import java.io.File;
99
import java.io.FileOutputStream;
1010
import java.io.InputStream;
11+
import java.lang.reflect.InvocationTargetException;
1112
import java.lang.reflect.Method;
1213
import java.net.URI;
14+
import java.net.URISyntaxException;
1315
import java.net.URL;
1416
import java.net.URLClassLoader;
1517
import java.util.ArrayList;
1618
import java.util.Arrays;
1719
import java.util.Enumeration;
20+
import java.util.HashMap;
1821
import java.util.List;
22+
import java.util.Map;
1923
import java.util.jar.JarEntry;
2024
import java.util.jar.JarFile;
2125

2226
public class JarMain implements Runnable {
23-
public static final String MAIN = "/" + JarMain.class.getName().replace('.', '/') + ".class";
24-
25-
private String[] args;
26-
private String path, jarfile;
27-
private boolean debug;
28-
private File extractRoot;
29-
30-
public JarMain(String[] args) throws Exception {
27+
28+
static final String MAIN = "/" + JarMain.class.getName().replace('.', '/') + ".class";
29+
30+
final boolean debug = isDebug();
31+
32+
protected final String[] args;
33+
protected final String archive;
34+
private final String path;
35+
36+
protected File extractRoot;
37+
38+
JarMain(String[] args) {
3139
this.args = args;
3240
URL mainClass = getClass().getResource(MAIN);
33-
this.path = mainClass.toURI().getSchemeSpecificPart();
34-
this.jarfile = this.path.replace("!" + MAIN, "").replace("file:", "");
35-
this.debug = isDebug();
36-
this.extractRoot = File.createTempFile("jruby", "extract");
37-
this.extractRoot.delete();
38-
this.extractRoot.mkdirs();
41+
try {
42+
this.path = mainClass.toURI().getSchemeSpecificPart();
43+
}
44+
catch (URISyntaxException e) {
45+
throw new RuntimeException(e);
46+
}
47+
archive = this.path.replace("!" + MAIN, "").replace("file:", "");
48+
3949
Runtime.getRuntime().addShutdownHook(new Thread(this));
4050
}
51+
52+
protected URL[] extractArchive() throws Exception {
53+
final JarFile jarFile = new JarFile(archive);
54+
try {
55+
Map<String, JarEntry> jarNames = new HashMap<String, JarEntry>();
56+
for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
57+
JarEntry entry = e.nextElement();
58+
String extractPath = getExtractEntryPath(entry);
59+
if ( extractPath != null ) jarNames.put(extractPath, entry);
60+
}
61+
62+
extractRoot = File.createTempFile("jruby", "extract");
63+
extractRoot.delete(); extractRoot.mkdirs();
4164

42-
private URL[] extractJRuby() throws Exception {
43-
JarFile jf = new JarFile(this.jarfile);
44-
List<String> jarNames = new ArrayList<String>();
45-
for (Enumeration<JarEntry> eje = jf.entries(); eje.hasMoreElements(); ) {
46-
String name = eje.nextElement().getName();
47-
if (name.startsWith("META-INF/lib") && name.endsWith(".jar")) {
48-
jarNames.add("/" + name);
65+
final List<URL> urls = new ArrayList<URL>();
66+
for (Map.Entry<String, JarEntry> e : jarNames.entrySet()) {
67+
URL entryURL = extractEntry(e.getValue(), e.getKey());
68+
if (entryURL != null) urls.add( entryURL );
4969
}
70+
return (URL[]) urls.toArray(new URL[urls.size()]);
5071
}
51-
52-
List<URL> urls = new ArrayList<URL>();
53-
for (String name : jarNames) {
54-
urls.add(extractJar(name));
72+
finally {
73+
jarFile.close();
5574
}
56-
57-
return (URL[]) urls.toArray(new URL[urls.size()]);
5875
}
5976

60-
private URL extractJar(String jarpath) throws Exception {
61-
InputStream jarStream = new URI("jar", path.replace(MAIN, jarpath), null).toURL().openStream();
62-
String jarname = jarpath.substring(jarpath.lastIndexOf("/") + 1, jarpath.lastIndexOf("."));
63-
File jarFile = new File(extractRoot, jarname + ".jar");
64-
jarFile.deleteOnExit();
65-
FileOutputStream outStream = new FileOutputStream(jarFile);
77+
protected String getExtractEntryPath(final JarEntry entry) {
78+
final String name = entry.getName();
79+
if ( name.startsWith("META-INF/lib") && name.endsWith(".jar") ) {
80+
return name.substring(name.lastIndexOf("/") + 1);
81+
}
82+
return null; // do not extract entry
83+
}
84+
85+
protected URL extractEntry(final JarEntry entry, final String path) throws Exception {
86+
final File file = new File(extractRoot, path);
87+
if ( entry.isDirectory() ) {
88+
file.mkdirs();
89+
return null;
90+
}
91+
final String entryPath = entryPath(entry.getName());
92+
final InputStream entryStream;
93+
try {
94+
entryStream = new URI("jar", entryPath, null).toURL().openStream();
95+
}
96+
catch (IllegalArgumentException e) {
97+
// TODO gems '%' file name "encoding" ?!
98+
debug("failed to open jar:" + entryPath + " skipping entry: " + entry.getName(), e);
99+
return null;
100+
}
101+
final File parent = file.getParentFile();
102+
if ( parent != null ) parent.mkdirs();
103+
FileOutputStream outStream = new FileOutputStream(file);
104+
final byte[] buf = new byte[65536];
66105
try {
67-
byte[] buf = new byte[65536];
68106
int bytesRead = 0;
69-
while ((bytesRead = jarStream.read(buf)) != -1) {
107+
while ((bytesRead = entryStream.read(buf)) != -1) {
70108
outStream.write(buf, 0, bytesRead);
71109
}
72-
} finally {
73-
jarStream.close();
110+
}
111+
finally {
112+
entryStream.close();
74113
outStream.close();
114+
file.deleteOnExit();
75115
}
76-
debug(jarname + ".jar extracted to " + jarFile.getPath());
77-
return jarFile.toURI().toURL();
116+
if (false) debug(entry.getName() + " extracted to " + file.getPath());
117+
return file.toURI().toURL();
118+
}
119+
120+
protected String entryPath(String name) {
121+
if ( ! name.startsWith("/") ) name = "/" + name;
122+
return path.replace(MAIN, name);
78123
}
79124

80-
private int launchJRuby(URL[] jars) throws Exception {
125+
protected Object newScriptingContainer(final URL[] jars) throws Exception {
81126
System.setProperty("org.jruby.embed.class.path", "");
82-
URLClassLoader loader = new URLClassLoader(jars);
127+
ClassLoader loader = new URLClassLoader(jars);
83128
Class scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, loader);
84129
Object scriptingContainer = scriptingContainerClass.newInstance();
85-
86-
Method argv = scriptingContainerClass.getDeclaredMethod("setArgv", new Class[] {String[].class});
87-
argv.invoke(scriptingContainer, new Object[] {args});
88-
Method setClassLoader = scriptingContainerClass.getDeclaredMethod("setClassLoader", new Class[] {ClassLoader.class});
89-
setClassLoader.invoke(scriptingContainer, new Object[] {loader});
90-
debug("invoking " + jarfile + " with: " + Arrays.deepToString(args));
91-
92-
Method runScriptlet = scriptingContainerClass.getDeclaredMethod("runScriptlet", new Class[] {String.class});
93-
return ((Number) runScriptlet.invoke(scriptingContainer, new Object[] {
94-
"begin\n" +
95-
"require 'META-INF/init.rb'\n" +
96-
"require 'META-INF/main.rb'\n" +
97-
"0\n" +
98-
"rescue SystemExit => e\n" +
99-
"e.status\n" +
100-
"end"
101-
})).intValue();
130+
debug("scripting container class loader urls: " + Arrays.toString(jars));
131+
invokeMethod(scriptingContainer, "setArgv", (Object) args);
132+
invokeMethod(scriptingContainer, "setClassLoader", new Class[] { ClassLoader.class }, loader);
133+
return scriptingContainer;
134+
}
135+
136+
protected int launchJRuby(final URL[] jars) throws Exception {
137+
final Object scriptingContainer = newScriptingContainer(jars);
138+
debug("invoking " + archive + " with: " + Arrays.deepToString(args));
139+
Object outcome = invokeMethod(scriptingContainer, "runScriptlet", launchScript());
140+
return ( outcome instanceof Number ) ? ( (Number) outcome ).intValue() : 0;
102141
}
103142

104-
private int start() throws Exception {
105-
URL[] u = extractJRuby();
106-
return launchJRuby(u);
143+
protected String launchScript() {
144+
return
145+
"begin\n" +
146+
" require 'META-INF/init.rb'\n" +
147+
" require 'META-INF/main.rb'\n" +
148+
" 0\n" +
149+
"rescue SystemExit => e\n" +
150+
" e.status\n" +
151+
"end";
152+
}
153+
154+
protected int start() throws Exception {
155+
final URL[] jars = extractArchive();
156+
return launchJRuby(jars);
107157
}
108158

109-
private void debug(String msg) {
110-
if (debug) {
111-
System.out.println(msg);
112-
}
159+
protected void debug(String msg) {
160+
debug(msg, null);
113161
}
114162

115-
private void delete(File f) {
163+
protected void debug(String msg, Throwable t) {
164+
if (debug) System.out.println(msg);
165+
if (debug && t != null) t.printStackTrace(System.out);
166+
}
167+
168+
protected void delete(File f) {
116169
if (f.isDirectory()) {
117170
File[] children = f.listFiles();
118171
for (int i = 0; i < children.length; i++) {
@@ -121,30 +174,57 @@ private void delete(File f) {
121174
}
122175
f.delete();
123176
}
124-
177+
125178
public void run() {
126-
delete(extractRoot);
179+
if ( extractRoot != null ) delete(extractRoot);
127180
}
128181

129182
public static void main(String[] args) {
183+
doStart(new JarMain(args));
184+
}
185+
186+
protected static void doStart(final JarMain main) {
130187
try {
131188
int exit = new JarMain(args).start();
132189
if(isSystemExitEnabled()) System.exit(exit);
133190
} catch (Exception e) {
191+
System.err.println("error: " + e.toString());
134192
Throwable t = e;
135193
while (t.getCause() != null && t.getCause() != t) {
136194
t = t.getCause();
137195
}
138-
139196
if (isDebug()) {
140197
t.printStackTrace();
141198
}
142199
System.exit(1);
143200
}
144201
}
202+
203+
protected static Object invokeMethod(final Object self, final String name, final Object... args)
204+
throws NoSuchMethodException, IllegalAccessException, Exception {
205+
206+
final Class[] signature = new Class[args.length];
207+
for ( int i = 0; i < args.length; i++ ) signature[i] = args[i].getClass();
208+
return invokeMethod(self, name, signature, args);
209+
}
145210

146-
private static boolean isDebug() {
147-
return System.getProperty("warbler.debug") != null;
211+
protected static Object invokeMethod(final Object self, final String name, final Class[] signature, final Object... args)
212+
throws NoSuchMethodException, IllegalAccessException, Exception {
213+
Method method = self.getClass().getDeclaredMethod(name, signature);
214+
try {
215+
return method.invoke(self, args);
216+
}
217+
catch (InvocationTargetException e) {
218+
Throwable target = e.getTargetException();
219+
if (target instanceof Exception) {
220+
throw (Exception) target;
221+
}
222+
throw e;
223+
}
224+
}
225+
226+
static boolean isDebug() {
227+
return Boolean.getBoolean("warbler.debug");
148228
}
149229

150230
/**
@@ -155,4 +235,5 @@ private static boolean isDebug() {
155235
private static boolean isSystemExitEnabled(){
156236
return System.getProperty("warbler.skip_system_exit") == null; //omission enables System.exit use
157237
}
238+
158239
}

0 commit comments

Comments
 (0)