8
8
import java .io .File ;
9
9
import java .io .FileOutputStream ;
10
10
import java .io .InputStream ;
11
+ import java .lang .reflect .InvocationTargetException ;
11
12
import java .lang .reflect .Method ;
12
13
import java .net .URI ;
14
+ import java .net .URISyntaxException ;
13
15
import java .net .URL ;
14
16
import java .net .URLClassLoader ;
15
17
import java .util .ArrayList ;
16
18
import java .util .Arrays ;
17
19
import java .util .Enumeration ;
20
+ import java .util .HashMap ;
18
21
import java .util .List ;
22
+ import java .util .Map ;
19
23
import java .util .jar .JarEntry ;
20
24
import java .util .jar .JarFile ;
21
25
22
26
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 ) {
31
39
this .args = args ;
32
40
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
+
39
49
Runtime .getRuntime ().addShutdownHook (new Thread (this ));
40
50
}
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 ();
41
64
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 );
49
69
}
70
+ return (URL []) urls .toArray (new URL [urls .size ()]);
50
71
}
51
-
52
- List <URL > urls = new ArrayList <URL >();
53
- for (String name : jarNames ) {
54
- urls .add (extractJar (name ));
72
+ finally {
73
+ jarFile .close ();
55
74
}
56
-
57
- return (URL []) urls .toArray (new URL [urls .size ()]);
58
75
}
59
76
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 ];
66
105
try {
67
- byte [] buf = new byte [65536 ];
68
106
int bytesRead = 0 ;
69
- while ((bytesRead = jarStream .read (buf )) != -1 ) {
107
+ while ((bytesRead = entryStream .read (buf )) != -1 ) {
70
108
outStream .write (buf , 0 , bytesRead );
71
109
}
72
- } finally {
73
- jarStream .close ();
110
+ }
111
+ finally {
112
+ entryStream .close ();
74
113
outStream .close ();
114
+ file .deleteOnExit ();
75
115
}
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 );
78
123
}
79
124
80
- private int launchJRuby ( URL [] jars ) throws Exception {
125
+ protected Object newScriptingContainer ( final URL [] jars ) throws Exception {
81
126
System .setProperty ("org.jruby.embed.class.path" , "" );
82
- URLClassLoader loader = new URLClassLoader (jars );
127
+ ClassLoader loader = new URLClassLoader (jars );
83
128
Class scriptingContainerClass = Class .forName ("org.jruby.embed.ScriptingContainer" , true , loader );
84
129
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 ;
102
141
}
103
142
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 );
107
157
}
108
158
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 );
113
161
}
114
162
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 ) {
116
169
if (f .isDirectory ()) {
117
170
File [] children = f .listFiles ();
118
171
for (int i = 0 ; i < children .length ; i ++) {
@@ -121,30 +174,57 @@ private void delete(File f) {
121
174
}
122
175
f .delete ();
123
176
}
124
-
177
+
125
178
public void run () {
126
- delete (extractRoot );
179
+ if ( extractRoot != null ) delete (extractRoot );
127
180
}
128
181
129
182
public static void main (String [] args ) {
183
+ doStart (new JarMain (args ));
184
+ }
185
+
186
+ protected static void doStart (final JarMain main ) {
130
187
try {
131
188
int exit = new JarMain (args ).start ();
132
189
if (isSystemExitEnabled ()) System .exit (exit );
133
190
} catch (Exception e ) {
191
+ System .err .println ("error: " + e .toString ());
134
192
Throwable t = e ;
135
193
while (t .getCause () != null && t .getCause () != t ) {
136
194
t = t .getCause ();
137
195
}
138
-
139
196
if (isDebug ()) {
140
197
t .printStackTrace ();
141
198
}
142
199
System .exit (1 );
143
200
}
144
201
}
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
+ }
145
210
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" );
148
228
}
149
229
150
230
/**
@@ -155,4 +235,5 @@ private static boolean isDebug() {
155
235
private static boolean isSystemExitEnabled (){
156
236
return System .getProperty ("warbler.skip_system_exit" ) == null ; //omission enables System.exit use
157
237
}
238
+
158
239
}
0 commit comments