warbler 1.3.6 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/.travis.yml +21 -8
- data/Gemfile +4 -7
- data/LICENSE.txt +1 -1
- data/README.rdoc +95 -105
- data/Rakefile +4 -3
- data/ext/JarMain.java +184 -75
- data/ext/WarMain.java +194 -65
- data/ext/WarblerJar.java +11 -1
- data/lib/warbler.rb +2 -1
- data/lib/warbler/application.rb +4 -1
- data/lib/warbler/config.rb +14 -3
- data/lib/warbler/jar.rb +10 -2
- data/lib/warbler/task.rb +7 -7
- data/lib/warbler/templates/jar.erb +3 -2
- data/lib/warbler/templates/war.erb +1 -3
- data/lib/warbler/traits/bundler.rb +11 -3
- data/lib/warbler/traits/gemspec.rb +1 -1
- data/lib/warbler/traits/jar.rb +5 -2
- data/lib/warbler/traits/nogemspec.rb +7 -2
- data/lib/warbler/traits/war.rb +13 -2
- data/lib/warbler/version.rb +1 -1
- data/lib/warbler/web_server.rb +2 -2
- data/lib/warbler_jar.jar +0 -0
- data/spec/sample_war/Rakefile +5 -1
- data/spec/sample_war/lib/ruby_one_nine.rb +8 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/warbler/application_spec.rb +5 -5
- data/spec/warbler/bundler_spec.rb +47 -20
- data/spec/warbler/config_spec.rb +3 -1
- data/spec/warbler/jar_spec.rb +25 -4
- data/spec/warbler/task_spec.rb +17 -1
- data/warble.rb +6 -2
- data/warbler.gemspec +21 -21
- metadata +286 -257
- data/Gemfile.lock +0 -41
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,12 +1,25 @@
|
|
1
1
|
rvm:
|
2
|
-
-
|
3
|
-
-
|
4
|
-
- jruby-18mode
|
5
|
-
# NOTE: seems to be failing on Travis the same way as jruby-rack did, stack
|
6
|
-
# seems to overflow when gems are loaded (does not happen locally :() :
|
7
|
-
# SystemStackError: stack level too deep
|
8
|
-
# ./spec/../lib/warbler/traits/bundler.rb:30:in `after_configure'
|
9
|
-
#- jruby-19mode
|
2
|
+
- jruby
|
3
|
+
- jruby-head
|
10
4
|
branches:
|
11
5
|
only:
|
12
6
|
- master
|
7
|
+
env:
|
8
|
+
- JRUBY_OPTS="--1.8 --server -Xcext.enabled=false -Xcompile.invokedynamic=false"
|
9
|
+
- JRUBY_OPTS="--1.9 --server -Xcext.enabled=false -Xcompile.invokedynamic=false"
|
10
|
+
matrix:
|
11
|
+
include:
|
12
|
+
- rvm: 1.8.7
|
13
|
+
env: ''
|
14
|
+
- rvm: 1.9.2
|
15
|
+
env: ''
|
16
|
+
- rvm: 1.9.3
|
17
|
+
env: ''
|
18
|
+
notifications:
|
19
|
+
irc:
|
20
|
+
channels:
|
21
|
+
- "irc.freenode.org#jruby"
|
22
|
+
on_success: change
|
23
|
+
on_failure: always
|
24
|
+
template:
|
25
|
+
- "%{repository} (%{branch}:%{commit} by %{author}): %{message} (%{build_url})"
|
data/Gemfile
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
source "http://rubygems.org/"
|
2
2
|
|
3
|
-
|
4
|
-
gem "rubyzip"
|
5
|
-
gem "jruby-jars"
|
6
|
-
gem "jruby-rack"
|
3
|
+
gemspec
|
7
4
|
|
8
5
|
group :development do
|
9
6
|
gem "jruby-openssl", :platform => :jruby
|
10
|
-
gem "rspec"
|
7
|
+
gem "rspec", ">= 2.8.0"
|
11
8
|
gem "diff-lcs"
|
12
|
-
gem "rcov", ">= 0.9.8"
|
13
|
-
gem "rdoc"
|
9
|
+
gem "rcov", ">= 0.9.8", :platform => :mri_18
|
10
|
+
gem "rdoc", ">= 2.4.2"
|
14
11
|
gem "childprocess", :platform => :mri
|
15
12
|
end
|
data/LICENSE.txt
CHANGED
data/README.rdoc
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
= Warbler
|
1
|
+
= Warbler {<img src="https://travis-ci.org/jruby/warbler.png" />}[https://travis-ci.org/jruby/warbler]
|
2
2
|
|
3
|
-
Warbler is a gem to make a Java jar or war file out of any Ruby,
|
4
|
-
|
5
|
-
|
6
|
-
deployment to a Java environment.
|
3
|
+
Warbler is a gem to make a Java jar or war file out of any Ruby, Rails or Rack
|
4
|
+
application. Warbler provides a minimal, flexible, Ruby-like way to bundle up
|
5
|
+
all of your application files for deployment to a Java environment.
|
7
6
|
|
8
7
|
Warbler provides a sane set of out-of-the box defaults that should allow most
|
9
8
|
Ruby applications to assemble and Just Work.
|
@@ -14,7 +13,8 @@ Ruby applications to assemble and Just Work.
|
|
14
13
|
|
15
14
|
2. Run warbler in the top directory of your application: <tt>warble</tt>.
|
16
15
|
|
17
|
-
3a. For a web project, deploy your myapp.war file to your favorite Java
|
16
|
+
3a. For a web project, deploy your +myapp.war+ file to your favorite Java
|
17
|
+
application server.
|
18
18
|
|
19
19
|
3b. For a standalone applications, just run it: <tt>java -jar myapp.jar</tt>.
|
20
20
|
|
@@ -26,22 +26,24 @@ defined tasks.
|
|
26
26
|
$ warble -T
|
27
27
|
warble compiled # Feature: precompile all Ruby files
|
28
28
|
warble config # Generate a configuration file to customize your archive
|
29
|
-
warble executable # Feature: make an executable archive
|
30
|
-
warble gemjar # Feature: package gem repository inside a
|
29
|
+
warble executable # Feature: make an executable archive (runnable + an emb...
|
30
|
+
warble gemjar # Feature: package gem repository inside a jar
|
31
31
|
warble pluginize # Install Warbler tasks in your Rails application
|
32
|
+
warble runnable # Feature: make a runnable archive (e.g. java -jar rails...
|
32
33
|
warble version # Display version of Warbler
|
33
34
|
warble war # Create the project war file
|
34
35
|
warble war:clean # Remove the project war file
|
35
36
|
warble war:debug # Dump diagnostic information
|
36
37
|
|
38
|
+
|
37
39
|
Type <tt>warble</tt> to create the jar or war file.
|
38
40
|
|
39
41
|
== Features
|
40
42
|
|
41
|
-
Warbler "features" are small Rake tasks that run before the creation
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
Warbler "features" are small Rake tasks that run before the creation of the war
|
44
|
+
file and make manipulations to the archive structure. For instance, the
|
45
|
+
+executable+ feature makes your war file capable of running on its own,
|
46
|
+
without a servlet container (using an embedded web server) :
|
45
47
|
|
46
48
|
warble executable war
|
47
49
|
|
@@ -53,83 +55,81 @@ or configure them in <tt>config/warble.rb</tt> to always be used.
|
|
53
55
|
|
54
56
|
config.features = %w(FEATURE)
|
55
57
|
|
56
|
-
Currently,
|
58
|
+
Currently, the following features are available :
|
57
59
|
|
58
60
|
* +gemjar+: This bundles all gems into a single gem file to reduce the
|
59
61
|
number of files in the .war. This is mostly useful for Google
|
60
62
|
AppEngine where the number of files per application has a limit.
|
61
63
|
(Note: not applicable for jar-based applications.)
|
62
|
-
* +
|
63
|
-
|
64
|
-
|
64
|
+
* +runnable+: This makes a (standard Java) runnable .war archive thus you can
|
65
|
+
execute binary bundled (gem) commands e.g. "rake". You should use the -S
|
66
|
+
switch to specify the binary followed by any arguments in takes e.g.
|
67
|
+
<tt>java -jar myrailsapp.war -S rake db:migrate</tt>.
|
68
|
+
* +executable+: This bundles an embedded web server into the .war so that it
|
69
|
+
can either be deployed into a traditional java web server or run as a
|
70
|
+
standalone application using <tt>java -jar myapp.war</tt>.
|
65
71
|
(Note: jar-based applications are executable by default.)
|
66
|
-
* +compiled+: This uses +jrubyc+ to precompile all .rb files in your
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
require __FILE__.sub(/\.rb$/, '.class')
|
72
|
+
* +compiled+: This uses +jrubyc+ to precompile all .rb files in your application
|
73
|
+
to .class files and includes those in the .war instead of the Ruby sources.
|
74
|
+
NOTE: The war file will still contain .rb files, but they will be short stubs
|
75
|
+
containing the following code : <tt>load __FILE__.sub(/\.rb$/, '.class')</tt>
|
72
76
|
|
73
|
-
Features may form the basis for a third-party plugin system in the
|
74
|
-
|
77
|
+
Features may form the basis for a third-party plugin system (in the future)
|
78
|
+
if there is demand.
|
75
79
|
|
76
|
-
NOTE: Feature tasks must be included in the same command invocation
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
sources included. Always make sure you run
|
80
|
+
NOTE: Feature tasks must be included in the same command invocation and
|
81
|
+
inserted before the +war+ task in order to take effect. For example,
|
82
|
+
<tt>warble compiled; warble war</tt> does not compile and obfuscate +.rb+
|
83
|
+
sources because the second invocation of +warble+ does not run the +compiled+
|
84
|
+
feature and creates a basic war with the sources included, make sure you run :
|
82
85
|
|
83
86
|
warble compiled war
|
84
87
|
|
85
|
-
or, if it's important that the war always be compiled, use the option
|
86
|
-
|
88
|
+
or, if it's important that the war always be compiled, use the option above to
|
89
|
+
put the feature in your <tt>config/warble.rb</tt>.
|
87
90
|
|
88
91
|
== War or Jar?
|
89
92
|
|
90
|
-
War-based projects are for Rails, Merb, or Rack-based web
|
91
|
-
|
92
|
-
|
93
|
-
The presence of these files are used to determine if the project
|
94
|
-
|
95
|
-
for the project.
|
93
|
+
War-based projects are for Rails, Merb, or Rack-based web applications.
|
94
|
+
They usually contain a +config/environment.rb+ file, a +config/init.rb+ file,
|
95
|
+
or a +config.ru+ file.
|
96
|
+
The presence of these files are used to determine if the project is a web
|
97
|
+
application, and thus a Java EE compatible war file is built for the project.
|
96
98
|
|
97
|
-
Jar-based projects are for standalone Ruby applications. Usually a
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
script.
|
99
|
+
Jar-based projects are for standalone Ruby applications. Usually a Ruby
|
100
|
+
application has a launcher script in the +bin+ directory and Ruby code
|
101
|
+
in the <tt>lib</tt> directory. Warbler packages the application so that
|
102
|
+
<tt>java -jar myapp.jar</tt> runs the launcher script.
|
102
103
|
|
103
104
|
== Jar Files
|
104
105
|
|
105
106
|
=== Gem Specification (gemspec) Files
|
106
107
|
|
107
|
-
If your project has a <tt>.gemspec</tt> file in the top directory, it
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
attributes:
|
108
|
+
If your project has a <tt>.gemspec</tt> file in the top directory, it will be
|
109
|
+
used to configure the project's dependencies, launcher script, require paths,
|
110
|
+
and the files to be included in the archive. For best results make sure your
|
111
|
+
gemspec specifies all of the following attributes:
|
112
112
|
|
113
113
|
* +executables+
|
114
114
|
* +require_paths+
|
115
115
|
* runtime dependencies added with +add_dependency+
|
116
116
|
|
117
|
-
If your project do not have a <tt>.gemspec</tt>, Warbler will attempt
|
118
|
-
|
119
|
-
|
120
|
-
|
117
|
+
If your project do not have a <tt>.gemspec</tt>, Warbler will attempt to guess
|
118
|
+
the launcher from the contents of the <tt>bin</tt> directory and use the
|
119
|
+
<tt>lib</tt> directory as the lone require path. All files in the project
|
120
|
+
will be included in the archive.
|
121
121
|
|
122
122
|
=== Bundler
|
123
123
|
|
124
|
-
Applications that use Bundler[http://gembundler.com/], detected via
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
124
|
+
Applications that use Bundler[http://gembundler.com/], detected via presence of
|
125
|
+
a +Gemfile+, will have the gems packaged up into the archive along with the
|
126
|
+
Gemfile. The Bundler groups named ":development", ":test" and ":assets" will be
|
127
|
+
excluded by default, unless you specify with <tt>config.bundle_without</tt> in
|
128
|
+
+config/warble.rb+.
|
129
129
|
|
130
|
-
Warbler supports Bundler for gems and git repositories, but not for
|
131
|
-
|
132
|
-
|
130
|
+
Warbler supports Bundler for gems and git repositories, but not for plain path
|
131
|
+
components. Warbler will warn when a +:path+ component is found in the +Gemfile+
|
132
|
+
and will refuse to include it in the archive.
|
133
133
|
|
134
134
|
== War Files
|
135
135
|
|
@@ -138,42 +138,34 @@ found in the +Gemfile+ and will refuse to include it in the archive.
|
|
138
138
|
Rails applications are detected automatically and configured appropriately.
|
139
139
|
The following items are set up for you:
|
140
140
|
|
141
|
-
* Your application runs in the +production+ environment by default.
|
141
|
+
* Your application runs in the +production+ environment by default.
|
142
|
+
Change it in +config/warble.rb+ (see below).
|
142
143
|
* The Rails gem is packaged if you haven't vendored Rails (Rails <= 2.x).
|
143
|
-
* Other gems configured in Rails.configuration.gems are packaged (
|
144
|
-
* Multi-thread-safe execution (as introduced in Rails 2.2) is detected and
|
145
|
-
|
146
|
-
=== Merb applications
|
147
|
-
|
148
|
-
Merb applications are detected automatically, and the merb-core gem and its
|
149
|
-
dependencies are packaged.
|
144
|
+
* Other gems configured in Rails.configuration.gems are packaged (2.1 - 2.3)
|
145
|
+
* Multi-thread-safe execution (as introduced in Rails 2.2) is detected and
|
146
|
+
runtime pooling is disabled.
|
150
147
|
|
151
148
|
=== Other Rack-based applications
|
152
149
|
|
153
|
-
If you have a +config.ru+ file in the top directory or one of the
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
<tt>ENV['RACK_ENV']</tt> will be set to +production+.
|
150
|
+
If you have a +config.ru+ file in the top directory or one of the immediate
|
151
|
+
subdirectories of your application, it will be included and used as the rackup
|
152
|
+
script for your Rack-based application. You will probably need to specify
|
153
|
+
framework and application gems in +config/warble.rb+ unless you're using Bundler
|
154
|
+
to manage your gems. <tt>ENV['RACK_ENV']</tt> will be set to +production+.
|
159
155
|
|
160
156
|
See {the examples in the jruby-rack project}[http://github.com/jruby/jruby-rack/tree/master/examples/]
|
161
157
|
of how to configure Warbler to package Camping and Sinatra apps.
|
162
158
|
|
163
159
|
=== Configuration auto-detect notes
|
164
160
|
|
165
|
-
* Warbler will load the +environment+ Rake task in a Rails application
|
166
|
-
to
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
needed. Until then, you will have to specify them in
|
174
|
-
+config/warble.rb+. See below.
|
175
|
-
* Is it possible to more generally detect what gems an application
|
176
|
-
uses? Gem.loaded_specs is available, but the application needs to be
|
161
|
+
* Warbler will load the +environment+ Rake task in a Rails application to try
|
162
|
+
to detect some configuration. If you don't have database access in the
|
163
|
+
environment where you package your application, you may wish to set
|
164
|
+
<tt>Warbler.framework_detection = false</tt> at the top of +config.rb+.
|
165
|
+
In this case you may need to specify additional details such as booter, gems
|
166
|
+
and other settings that would normally be gleaned from the app configuration.
|
167
|
+
* Is it possible to more generally detect what gems an application uses?
|
168
|
+
<tt>Gem.loaded_specs</tt> is available, but the application needs to be
|
177
169
|
loaded first before its contents are reliable.
|
178
170
|
|
179
171
|
== Custom configuration
|
@@ -185,17 +177,16 @@ doing this. With the gem, simply run
|
|
185
177
|
|
186
178
|
warble config
|
187
179
|
|
188
|
-
Finally, edit the config/warble.rb to your taste. The generated
|
189
|
-
|
190
|
-
and default values.
|
180
|
+
Finally, edit the +config/warble.rb+ to your taste. The generated file is
|
181
|
+
fully-documented with the available options and default values.
|
191
182
|
|
192
183
|
=== War layout
|
193
184
|
|
194
185
|
The default configuration puts application files (+app+, +config+, +lib+,
|
195
|
-
+log+, +vendor+, +tmp+) under the .war file's WEB-INF directory, and files in
|
186
|
+
+log+, +vendor+, +tmp+) under the .war file's +WEB-INF+ directory, and files in
|
196
187
|
+public+ in the root of the .war file. Any Java .jar files stored in lib will
|
197
|
-
automatically be placed in WEB-INF/lib for placement on the web app's
|
198
|
-
|
188
|
+
automatically be placed in +WEB-INF/lib+ for placement on the web app's
|
189
|
+
class-path.
|
199
190
|
|
200
191
|
=== Web.xml
|
201
192
|
|
@@ -220,27 +211,27 @@ For more information on configuration, see Warbler::Config.
|
|
220
211
|
|
221
212
|
== Rakefile integration
|
222
213
|
|
223
|
-
If you'd like to control Warbler from your own project's Rakefile
|
224
|
-
simply add the following code somewhere in the Rakefile:
|
214
|
+
If you'd like to control Warbler from your own project's +Rakefile+,
|
215
|
+
simply add the following code somewhere in the +Rakefile+ :
|
225
216
|
|
226
217
|
require 'warbler'
|
227
218
|
Warbler::Task.new
|
228
219
|
|
229
|
-
If you're using Bundler, you'll want to add Warbler to your Gemfile:
|
220
|
+
If you're using Bundler, you'll want to add Warbler to your +Gemfile+ :
|
230
221
|
|
231
222
|
group :development do
|
232
223
|
gem "warbler"
|
233
224
|
end
|
234
225
|
|
235
|
-
Now you should be able to invoke
|
226
|
+
Now you should be able to invoke <tt>rake war</tt> to create your war file.
|
236
227
|
|
237
228
|
== Troubleshooting
|
238
229
|
|
239
|
-
If Warbler isn't packaging the files you were expecting, use the
|
240
|
-
|
230
|
+
If Warbler isn't packaging the files you were expecting, use the +war:debug+
|
231
|
+
task to give you more insight into what's going on.
|
241
232
|
|
242
233
|
If you think you found a bug, please file one at
|
243
|
-
|
234
|
+
https://github.com/jruby/warbler/issues.
|
244
235
|
|
245
236
|
== Source
|
246
237
|
|
@@ -248,22 +239,21 @@ You can get the Warbler source using Git, in any of the following ways:
|
|
248
239
|
|
249
240
|
git clone git://git.caldersphere.net/warbler.git
|
250
241
|
git clone git://github.com/jruby/warbler.git
|
251
|
-
git clone git://kenai.com/warbler~main
|
252
242
|
|
253
243
|
You can also download a tarball of Warbler source at
|
254
244
|
https://github.com/jruby/warbler/tarball/master.
|
255
245
|
|
256
246
|
== Development
|
257
247
|
|
258
|
-
You can develop Warbler with any implementation of Ruby. To write
|
259
|
-
|
260
|
-
and run "bundle install" once.
|
248
|
+
You can develop Warbler with any implementation of Ruby. To write Warbler code
|
249
|
+
and run specs, you need to have Bundler installed and run <tt>bundle</tt> once.
|
261
250
|
|
262
|
-
After that, simply run
|
251
|
+
After that, simply run <tt>rake</tt>.
|
263
252
|
|
264
253
|
== License
|
265
254
|
|
266
255
|
Warbler is provided under the terms of the MIT license.
|
267
256
|
|
268
|
-
Warbler (c) 2010-
|
257
|
+
Warbler (c) 2010-2013 Engine Yard, Inc.
|
258
|
+
|
269
259
|
Warbler (c) 2007-2009 Sun Microsystems, Inc.
|
data/Rakefile
CHANGED
@@ -7,9 +7,10 @@
|
|
7
7
|
|
8
8
|
begin
|
9
9
|
require 'bundler/setup'
|
10
|
-
rescue LoadError
|
11
|
-
|
12
|
-
puts "Please install
|
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
|
13
14
|
end
|
14
15
|
|
15
16
|
require 'bundler/gem_helper'
|
data/ext/JarMain.java
CHANGED
@@ -6,143 +6,252 @@
|
|
6
6
|
*/
|
7
7
|
|
8
8
|
import java.io.File;
|
9
|
+
import java.io.IOException;
|
9
10
|
import java.io.FileOutputStream;
|
10
11
|
import java.io.InputStream;
|
12
|
+
import java.lang.reflect.InvocationTargetException;
|
11
13
|
import java.lang.reflect.Method;
|
14
|
+
import java.net.URI;
|
15
|
+
import java.net.URISyntaxException;
|
12
16
|
import java.net.URL;
|
13
17
|
import java.net.URLClassLoader;
|
14
18
|
import java.util.ArrayList;
|
15
19
|
import java.util.Arrays;
|
16
20
|
import java.util.Enumeration;
|
21
|
+
import java.util.HashMap;
|
17
22
|
import java.util.List;
|
23
|
+
import java.util.Map;
|
18
24
|
import java.util.jar.JarEntry;
|
19
25
|
import java.util.jar.JarFile;
|
20
26
|
|
21
27
|
public class JarMain implements Runnable {
|
22
|
-
|
28
|
+
|
29
|
+
static final String MAIN = "/" + JarMain.class.getName().replace('.', '/') + ".class";
|
23
30
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
31
|
+
final boolean debug = isDebug();
|
32
|
+
|
33
|
+
protected final String[] args;
|
34
|
+
protected final String archive;
|
35
|
+
private final String path;
|
36
|
+
|
37
|
+
protected File extractRoot;
|
28
38
|
|
29
|
-
|
39
|
+
JarMain(String[] args) {
|
30
40
|
this.args = args;
|
31
41
|
URL mainClass = getClass().getResource(MAIN);
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
42
|
+
try {
|
43
|
+
this.path = mainClass.toURI().getSchemeSpecificPart();
|
44
|
+
}
|
45
|
+
catch (URISyntaxException e) {
|
46
|
+
throw new RuntimeException(e);
|
47
|
+
}
|
48
|
+
archive = this.path.replace("!" + MAIN, "").replace("file:", "");
|
49
|
+
|
38
50
|
Runtime.getRuntime().addShutdownHook(new Thread(this));
|
39
51
|
}
|
52
|
+
|
53
|
+
protected URL[] extractArchive() throws Exception {
|
54
|
+
final JarFile jarFile = new JarFile(archive);
|
55
|
+
try {
|
56
|
+
Map<String, JarEntry> jarNames = new HashMap<String, JarEntry>();
|
57
|
+
for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
|
58
|
+
JarEntry entry = e.nextElement();
|
59
|
+
String extractPath = getExtractEntryPath(entry);
|
60
|
+
if ( extractPath != null ) jarNames.put(extractPath, entry);
|
61
|
+
}
|
62
|
+
|
63
|
+
extractRoot = File.createTempFile("jruby", "extract");
|
64
|
+
extractRoot.delete(); extractRoot.mkdirs();
|
40
65
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
String name = eje.nextElement().getName();
|
46
|
-
if (name.startsWith("META-INF/lib") && name.endsWith(".jar")) {
|
47
|
-
jarNames.add("/" + name);
|
66
|
+
final List<URL> urls = new ArrayList<URL>();
|
67
|
+
for (Map.Entry<String, JarEntry> e : jarNames.entrySet()) {
|
68
|
+
URL entryURL = extractEntry(e.getValue(), e.getKey());
|
69
|
+
if (entryURL != null) urls.add( entryURL );
|
48
70
|
}
|
71
|
+
return (URL[]) urls.toArray(new URL[urls.size()]);
|
49
72
|
}
|
50
|
-
|
51
|
-
|
52
|
-
for (String name : jarNames) {
|
53
|
-
urls.add(extractJar(name));
|
73
|
+
finally {
|
74
|
+
jarFile.close();
|
54
75
|
}
|
55
|
-
|
56
|
-
return (URL[]) urls.toArray(new URL[urls.size()]);
|
57
76
|
}
|
58
77
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
78
|
+
protected String getExtractEntryPath(final JarEntry entry) {
|
79
|
+
final String name = entry.getName();
|
80
|
+
if ( name.startsWith("META-INF/lib") && name.endsWith(".jar") ) {
|
81
|
+
return name.substring(name.lastIndexOf("/") + 1);
|
82
|
+
}
|
83
|
+
return null; // do not extract entry
|
84
|
+
}
|
85
|
+
|
86
|
+
protected URL extractEntry(final JarEntry entry, final String path) throws Exception {
|
87
|
+
final File file = new File(extractRoot, path);
|
88
|
+
if ( entry.isDirectory() ) {
|
89
|
+
file.mkdirs();
|
90
|
+
return null;
|
91
|
+
}
|
92
|
+
final String entryPath = entryPath(entry.getName());
|
93
|
+
final InputStream entryStream;
|
94
|
+
try {
|
95
|
+
entryStream = new URI("jar", entryPath, null).toURL().openStream();
|
96
|
+
}
|
97
|
+
catch (IllegalArgumentException e) {
|
98
|
+
// TODO gems '%' file name "encoding" ?!
|
99
|
+
debug("failed to open jar:" + entryPath + " skipping entry: " + entry.getName(), e);
|
100
|
+
return null;
|
101
|
+
}
|
102
|
+
final File parent = file.getParentFile();
|
103
|
+
if ( parent != null ) parent.mkdirs();
|
104
|
+
FileOutputStream outStream = new FileOutputStream(file);
|
105
|
+
final byte[] buf = new byte[65536];
|
65
106
|
try {
|
66
|
-
byte[] buf = new byte[65536];
|
67
107
|
int bytesRead = 0;
|
68
|
-
while ((bytesRead =
|
108
|
+
while ((bytesRead = entryStream.read(buf)) != -1) {
|
69
109
|
outStream.write(buf, 0, bytesRead);
|
70
110
|
}
|
71
|
-
}
|
72
|
-
|
111
|
+
}
|
112
|
+
finally {
|
113
|
+
entryStream.close();
|
73
114
|
outStream.close();
|
115
|
+
file.deleteOnExit();
|
74
116
|
}
|
75
|
-
debug(
|
76
|
-
return
|
117
|
+
if (false) debug(entry.getName() + " extracted to " + file.getPath());
|
118
|
+
return file.toURI().toURL();
|
119
|
+
}
|
120
|
+
|
121
|
+
protected String entryPath(String name) {
|
122
|
+
if ( ! name.startsWith("/") ) name = "/" + name;
|
123
|
+
return path.replace(MAIN, name);
|
77
124
|
}
|
78
125
|
|
79
|
-
|
126
|
+
protected Object newScriptingContainer(final URL[] jars) throws Exception {
|
80
127
|
System.setProperty("org.jruby.embed.class.path", "");
|
81
|
-
|
128
|
+
ClassLoader loader = new URLClassLoader(jars);
|
82
129
|
Class scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, loader);
|
83
130
|
Object scriptingContainer = scriptingContainerClass.newInstance();
|
131
|
+
debug("scripting container class loader urls: " + Arrays.toString(jars));
|
132
|
+
invokeMethod(scriptingContainer, "setArgv", (Object) args);
|
133
|
+
invokeMethod(scriptingContainer, "setClassLoader", new Class[] { ClassLoader.class }, loader);
|
134
|
+
return scriptingContainer;
|
135
|
+
}
|
136
|
+
|
137
|
+
protected int launchJRuby(final URL[] jars) throws Exception {
|
138
|
+
final Object scriptingContainer = newScriptingContainer(jars);
|
139
|
+
debug("invoking " + archive + " with: " + Arrays.deepToString(args));
|
140
|
+
Object outcome = invokeMethod(scriptingContainer, "runScriptlet", launchScript());
|
141
|
+
return ( outcome instanceof Number ) ? ( (Number) outcome ).intValue() : 0;
|
142
|
+
}
|
84
143
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
"end"
|
100
|
-
})).intValue();
|
144
|
+
protected String launchScript() {
|
145
|
+
return
|
146
|
+
"begin\n" +
|
147
|
+
" require 'META-INF/init.rb'\n" +
|
148
|
+
" require 'META-INF/main.rb'\n" +
|
149
|
+
" 0\n" +
|
150
|
+
"rescue SystemExit => e\n" +
|
151
|
+
" e.status\n" +
|
152
|
+
"end";
|
153
|
+
}
|
154
|
+
|
155
|
+
protected int start() throws Exception {
|
156
|
+
final URL[] jars = extractArchive();
|
157
|
+
return launchJRuby(jars);
|
101
158
|
}
|
102
159
|
|
103
|
-
|
104
|
-
|
105
|
-
return launchJRuby(u);
|
160
|
+
protected void debug(String msg) {
|
161
|
+
debug(msg, null);
|
106
162
|
}
|
107
163
|
|
108
|
-
|
109
|
-
if (debug)
|
110
|
-
|
164
|
+
protected void debug(String msg, Throwable t) {
|
165
|
+
if (debug) System.out.println(msg);
|
166
|
+
if (debug && t != null) t.printStackTrace(System.out);
|
167
|
+
}
|
168
|
+
|
169
|
+
protected void delete(File f) {
|
170
|
+
try {
|
171
|
+
if (f.isDirectory() && !isSymlink(f)) {
|
172
|
+
File[] children = f.listFiles();
|
173
|
+
for (int i = 0; i < children.length; i++) {
|
174
|
+
delete(children[i]);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
f.delete();
|
178
|
+
} catch (IOException e) {
|
179
|
+
System.err.println("error: " + e.toString());
|
111
180
|
}
|
112
181
|
}
|
113
182
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
183
|
+
protected boolean isSymlink(File file) throws IOException {
|
184
|
+
if (file == null)
|
185
|
+
throw new NullPointerException("File must not be null");
|
186
|
+
File canon;
|
187
|
+
if (file.getParent() == null) {
|
188
|
+
canon = file;
|
189
|
+
} else {
|
190
|
+
File canonDir = file.getParentFile().getCanonicalFile();
|
191
|
+
canon = new File(canonDir, file.getName());
|
192
|
+
}
|
193
|
+
return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
|
122
194
|
}
|
123
195
|
|
124
196
|
public void run() {
|
125
|
-
delete(extractRoot);
|
197
|
+
if ( extractRoot != null ) delete(extractRoot);
|
126
198
|
}
|
127
199
|
|
128
200
|
public static void main(String[] args) {
|
201
|
+
doStart(new JarMain(args));
|
202
|
+
}
|
203
|
+
|
204
|
+
protected static void doStart(final JarMain main) {
|
129
205
|
try {
|
130
|
-
int exit =
|
131
|
-
System.exit(exit);
|
206
|
+
int exit = main.start();
|
207
|
+
if(isSystemExitEnabled()) System.exit(exit);
|
132
208
|
} catch (Exception e) {
|
209
|
+
System.err.println("error: " + e.toString());
|
133
210
|
Throwable t = e;
|
134
211
|
while (t.getCause() != null && t.getCause() != t) {
|
135
212
|
t = t.getCause();
|
136
213
|
}
|
137
|
-
|
138
214
|
if (isDebug()) {
|
139
215
|
t.printStackTrace();
|
140
216
|
}
|
141
217
|
System.exit(1);
|
142
218
|
}
|
143
219
|
}
|
220
|
+
|
221
|
+
protected static Object invokeMethod(final Object self, final String name, final Object... args)
|
222
|
+
throws NoSuchMethodException, IllegalAccessException, Exception {
|
223
|
+
|
224
|
+
final Class[] signature = new Class[args.length];
|
225
|
+
for ( int i = 0; i < args.length; i++ ) signature[i] = args[i].getClass();
|
226
|
+
return invokeMethod(self, name, signature, args);
|
227
|
+
}
|
144
228
|
|
145
|
-
|
146
|
-
|
229
|
+
protected static Object invokeMethod(final Object self, final String name, final Class[] signature, final Object... args)
|
230
|
+
throws NoSuchMethodException, IllegalAccessException, Exception {
|
231
|
+
Method method = self.getClass().getDeclaredMethod(name, signature);
|
232
|
+
try {
|
233
|
+
return method.invoke(self, args);
|
234
|
+
}
|
235
|
+
catch (InvocationTargetException e) {
|
236
|
+
Throwable target = e.getTargetException();
|
237
|
+
if (target instanceof Exception) {
|
238
|
+
throw (Exception) target;
|
239
|
+
}
|
240
|
+
throw e;
|
241
|
+
}
|
147
242
|
}
|
243
|
+
|
244
|
+
static boolean isDebug() {
|
245
|
+
return Boolean.getBoolean("warbler.debug");
|
246
|
+
}
|
247
|
+
|
248
|
+
/**
|
249
|
+
* if warbler.skip_system_exit system property is defined, we will not
|
250
|
+
* call System.exit in the normal flow. System.exit can cause problems
|
251
|
+
* for wrappers like procrun
|
252
|
+
*/
|
253
|
+
private static boolean isSystemExitEnabled(){
|
254
|
+
return System.getProperty("warbler.skip_system_exit") == null; //omission enables System.exit use
|
255
|
+
}
|
256
|
+
|
148
257
|
}
|