warbler_updated 2.1.0

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +10 -0
  3. data/History.txt +411 -0
  4. data/LICENSE.txt +27 -0
  5. data/Mavenfile +32 -0
  6. data/README.rdoc +280 -0
  7. data/Rakefile +63 -0
  8. data/bin/warble +11 -0
  9. data/ext/JarMain.java +334 -0
  10. data/ext/WarMain.java +375 -0
  11. data/ext/WarblerJar.java +199 -0
  12. data/ext/WarblerJarService.java +18 -0
  13. data/lib/warbler/application.rb +104 -0
  14. data/lib/warbler/bundler_helper.rb +22 -0
  15. data/lib/warbler/config.rb +265 -0
  16. data/lib/warbler/executable_helper.rb +25 -0
  17. data/lib/warbler/gems.rb +77 -0
  18. data/lib/warbler/jar.rb +348 -0
  19. data/lib/warbler/pathmap_helper.rb +20 -0
  20. data/lib/warbler/platform_helper.rb +26 -0
  21. data/lib/warbler/rake_helper.rb +30 -0
  22. data/lib/warbler/scripts/rails.rb +5 -0
  23. data/lib/warbler/task.rb +185 -0
  24. data/lib/warbler/templates/bundler.erb +19 -0
  25. data/lib/warbler/templates/config.erb +1 -0
  26. data/lib/warbler/templates/jar.erb +11 -0
  27. data/lib/warbler/templates/jbundler.erb +2 -0
  28. data/lib/warbler/templates/rack.erb +5 -0
  29. data/lib/warbler/templates/rails.erb +1 -0
  30. data/lib/warbler/templates/war.erb +19 -0
  31. data/lib/warbler/traits/bundler.rb +157 -0
  32. data/lib/warbler/traits/gemspec.rb +79 -0
  33. data/lib/warbler/traits/jar.rb +58 -0
  34. data/lib/warbler/traits/jbundler.rb +48 -0
  35. data/lib/warbler/traits/nogemspec.rb +47 -0
  36. data/lib/warbler/traits/rack.rb +33 -0
  37. data/lib/warbler/traits/rails.rb +91 -0
  38. data/lib/warbler/traits/war.rb +260 -0
  39. data/lib/warbler/traits.rb +124 -0
  40. data/lib/warbler/version.rb +10 -0
  41. data/lib/warbler/war.rb +8 -0
  42. data/lib/warbler/web_server.rb +125 -0
  43. data/lib/warbler/zip_support.rb +13 -0
  44. data/lib/warbler.rb +40 -0
  45. data/lib/warbler_jar.jar +0 -0
  46. data/warble.rb +188 -0
  47. data/warbler.gemspec +37 -0
  48. data/web.xml.erb +57 -0
  49. metadata +188 -0
data/README.rdoc ADDED
@@ -0,0 +1,280 @@
1
+ = Warbler {<img src="https://badge.fury.io/rb/warbler.svg" alt="Gem Version" />}[http://badge.fury.io/rb/warbler] {<img src="https://github.com/jruby/warbler/actions/workflows/ci.yml/badge.svg" alt="Build Status" />}[https://github.com/jruby/warbler/actions/workflows/ci.yml]
2
+
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.
6
+
7
+ Warbler provides a sane set of out-of-the box defaults that should allow most
8
+ Ruby applications to assemble and Just Work.
9
+
10
+ Version 2.x of Warbler supports versions of JRuby from 9.2.0.0 and up.
11
+
12
+ Version 1.4.x of Warbler supports versions of JRuby up to 1.7.x. The {1.x-dev branch}[https://github.com/jruby/warbler/tree/1.x-dev] is the working code for this.
13
+
14
+ == Getting Started
15
+
16
+ 1. Install the gem: <tt>gem install warbler</tt>.
17
+
18
+ 2. Run warbler in the top directory of your application: <tt>warble</tt>.
19
+
20
+ 3. Choose one:
21
+
22
+ * For a web project, deploy your +myapp.war+ file to your favorite Java application server.
23
+
24
+ * For a standalone applications, just run it: <tt>java -jar myapp.jar</tt>.
25
+
26
+ == Usage
27
+
28
+ Warbler's +warble+ command is just a small wrapper around Rake with internally
29
+ defined tasks.
30
+
31
+ $ warble -T
32
+ warble compiled # Feature: precompile all Ruby files
33
+ warble config # Generate a configuration file to customize your archive
34
+ warble executable # Feature: make an executable archive (runnable + an emb...
35
+ warble gemjar # Feature: package gem repository inside a jar
36
+ warble pluginize # Install Warbler tasks in your Rails application
37
+ warble runnable # Feature: make a runnable archive (e.g. java -jar rails...
38
+ warble version # Display version of Warbler
39
+ warble war # Create the project war file
40
+ warble war:clean # Remove the project war file
41
+ warble war:debug # Dump diagnostic information
42
+
43
+
44
+ Type <tt>warble</tt> to create the jar or war file.
45
+
46
+ == Features
47
+
48
+ Warbler "features" are small Rake tasks that run before the creation of the war
49
+ file and make manipulations to the archive structure. For instance, the
50
+ +executable+ feature makes your war file capable of running on its own,
51
+ without a servlet container (using an embedded web server) :
52
+
53
+ warble executable war
54
+
55
+ You can either add features to the warbler command line:
56
+
57
+ warble FEATURE war
58
+
59
+ or configure them in <tt>config/warble.rb</tt> to always be used.
60
+
61
+ config.features = %w(FEATURE)
62
+
63
+ Currently, the following features are available :
64
+
65
+ * +gemjar+: This bundles all gems into a single gem file to reduce the
66
+ number of files in the .war. This is mostly useful for Google
67
+ AppEngine where the number of files per application has a limit.
68
+ (Note: not applicable for jar-based applications.)
69
+ * +runnable+: This makes a (standard Java) runnable .war archive thus you can
70
+ execute binary bundled (gem) commands e.g. "rake". You should use the -S
71
+ switch to specify the binary followed by any arguments in takes e.g.
72
+ <tt>java -jar myrailsapp.war -S rake db:migrate</tt>.
73
+ * +executable+: This bundles an embedded web server into the .war so that it
74
+ can either be deployed into a traditional java web server or run as a
75
+ standalone application using <tt>java -jar myapp.war</tt>.
76
+ (Note: jar-based applications are executable by default.)
77
+ * +compiled+: This uses +jrubyc+ to precompile all .rb files in your application
78
+ to .class files and includes those in the .war instead of the Ruby sources.
79
+ NOTE: The war file will still contain .rb files, but they will be short stubs
80
+ containing the following code : <tt>load __FILE__.sub(/\.rb$/, '.class')</tt>
81
+
82
+ Features may form the basis for a third-party plugin system (in the future)
83
+ if there is demand.
84
+
85
+ NOTE: Feature tasks must be included in the same command invocation and
86
+ inserted before the +war+ task in order to take effect. For example,
87
+ <tt>warble compiled; warble war</tt> does not compile and obfuscate +.rb+
88
+ sources because the second invocation of +warble+ does not run the +compiled+
89
+ feature and creates a basic war with the sources included, make sure you run :
90
+
91
+ warble compiled war
92
+
93
+ or, if it's important that the war always be compiled, use the option above to
94
+ put the feature in your <tt>config/warble.rb</tt>.
95
+
96
+ == .war or .jar?
97
+
98
+ War-based projects are for Rails, Merb, or Rack-based web applications.
99
+ They usually contain a +config/environment.rb+ file, a +config/init.rb+ file,
100
+ or a +config.ru+ file.
101
+ The presence of these files are used to determine if the project is a web
102
+ application, and thus a Java EE compatible war file is built for the project.
103
+
104
+ Jar-based projects are for standalone Ruby applications. Usually a Ruby
105
+ application has a launcher script in the +bin+ directory and Ruby code
106
+ in the <tt>lib</tt> directory. Warbler packages the application so that
107
+ <tt>java -jar myapp.jar</tt> runs the launcher script.
108
+
109
+ == Jar Files
110
+
111
+ === Gem Specification Files
112
+
113
+ If your project has a <tt>.gemspec</tt> file in the top directory, it will be
114
+ used to configure the project's dependencies, launcher script, require paths,
115
+ and the files to be included in the archive. For best results make sure your
116
+ gemspec specifies all of the following attributes:
117
+
118
+ * +executables+
119
+ * +require_paths+
120
+ * runtime dependencies added with +add_dependency+
121
+
122
+ If your project do not have a <tt>.gemspec</tt>, Warbler will attempt to guess
123
+ the launcher from the contents of the <tt>bin</tt> directory and use the
124
+ <tt>lib</tt> directory as the lone require path. All files in the project
125
+ will be included in the archive.
126
+
127
+ === Bundler
128
+
129
+ Applications that use Bundler[http://gembundler.com/], detected via presence of
130
+ a +Gemfile+, will have the gems packaged up into the archive along with the
131
+ Gemfile. The Bundler groups named ":development", ":test" and ":assets" will be
132
+ excluded by default, unless you specify with <tt>config.bundle_without</tt> in
133
+ +config/warble.rb+.
134
+
135
+ Warbler supports Bundler for gems and git repositories, but not for plain path
136
+ components. Warbler will warn when a +:path+ component is found in the +Gemfile+
137
+ and will refuse to include it in the archive.
138
+
139
+ === JBundler (experimental)
140
+
141
+ Applications that use JBundler[http://github.com/mkristian/jbundler], detected
142
+ via presence of a +Jarfile+, will have the jars packaged up into the archive. the JBundler gem is **not** needed for runtime since all jars are already part of the classloader.
143
+
144
+ == War Files
145
+
146
+ === Rails applications
147
+
148
+ Rails applications are detected automatically and configured appropriately.
149
+ The following items are set up for you:
150
+
151
+ * Your application runs in the +production+ environment by default.
152
+ Change it in +config/warble.rb+ (see below).
153
+ * The Rails gem is packaged if you haven't vendored Rails (Rails <= 2.x).
154
+ * Other gems configured in Rails.configuration.gems are packaged (2.1 - 2.3)
155
+ * Multi-thread-safe execution (as introduced in Rails 2.2) is detected and
156
+ runtime pooling is disabled.
157
+
158
+ === Other Rack Applications
159
+
160
+ If you have a +config.ru+ file in the top directory or one of the immediate
161
+ subdirectories of your application, it will be included and used as the rackup
162
+ script for your Rack-based application. You will probably need to specify
163
+ framework and application gems in +config/warble.rb+ unless you're using Bundler
164
+ to manage your gems. <tt>ENV['RACK_ENV']</tt> will be set to +production+.
165
+
166
+ See {the examples in the jruby-rack project}[http://github.com/jruby/jruby-rack/tree/master/examples/]
167
+ of how to configure Warbler to package Camping and Sinatra apps.
168
+
169
+ === Configuration Notes
170
+
171
+ * Warbler will load the +environment+ Rake task in a Rails application to try
172
+ to detect some configuration. If you don't have database access in the
173
+ environment where you package your application, you may wish to set
174
+ <tt>Warbler.framework_detection = false</tt> at the top of +config.rb+.
175
+ In this case you may need to specify additional details such as booter, gems
176
+ and other settings that would normally be gleaned from the app configuration.
177
+ * Is it possible to more generally detect what gems an application uses?
178
+ <tt>Gem.loaded_specs</tt> is available, but the application needs to be
179
+ loaded first before its contents are reliable.
180
+
181
+ == Custom Configuration
182
+
183
+ If the default settings are not appropriate for your application, you can
184
+ customize Warbler's behavior. To customize files, libraries, and gems included
185
+ in the .war file, you'll need a config/warble.rb file. There a two ways of
186
+ doing this. With the gem, simply run
187
+
188
+ warble config
189
+
190
+ Finally, edit the +config/warble.rb+ to your taste. The generated file is
191
+ fully-documented with the available options and default values.
192
+
193
+ === Archive Layout
194
+
195
+ The default configuration puts application files (+app+, +config+, +lib+,
196
+ +log+, +vendor+, +tmp+) under the .war file's +WEB-INF+ directory, and files in
197
+ +public+ in the root of the .war file. Any Java .jar files stored in lib will
198
+ automatically be placed in +WEB-INF/lib+ for placement on the web app's
199
+ class-path.
200
+
201
+ === web.xml
202
+
203
+ Java web applications are configured mainly through this file, and Warbler
204
+ creates a suitable default file for you for use. However, if you need to
205
+ customize it in any way, you have two options.
206
+
207
+ 1. If you just want a static web.xml file whose contents you manually
208
+ control, you may unzip the one generated for you in
209
+ <tt>yourapp.war:WEB-INF/web.xml</tt> to <tt>config/web.xml</tt> and
210
+ modify as needed. It will be copied into subsequent copies of the
211
+ war file for you.
212
+ 2. If you want to inject some dynamic information into the file, copy
213
+ the <tt>WARBLER_HOME/web.xml.erb</tt> to
214
+ <tt>config/web.xml.erb</tt>. Its contents will be evaluated for you
215
+ and put in the webapp. Note that you can also pass arbitrary
216
+ properties to the ERb template by setting
217
+ <tt>config.webxml.customkey</tt> values in your
218
+ <tt>config/warble.rb</tt> file.
219
+
220
+ For more information on configuration, see Warbler::Config.
221
+
222
+ == Rakefile Integration
223
+
224
+ If you'd like to control Warbler from your own project's +Rakefile+,
225
+ simply add the following code somewhere in the +Rakefile+ :
226
+
227
+ require 'warbler'
228
+ Warbler::Task.new
229
+
230
+ If you're using Bundler, you'll want to add Warbler to your +Gemfile+ :
231
+
232
+ group :development do
233
+ gem "warbler", :require => false
234
+ end
235
+
236
+ Now you should be able to invoke <tt>rake war</tt> to create your war file.
237
+
238
+ == Troubleshooting
239
+
240
+ If Warbler isn't packaging the files you were expecting, use the +war:debug+
241
+ task to give you more insight into what's going on.
242
+
243
+ If you think you found a bug, please file one at
244
+ https://github.com/jruby/warbler/issues.
245
+
246
+ == Source
247
+
248
+ You can get the Warbler source using Git, in any of the following ways:
249
+
250
+ git clone git://github.com/jruby/warbler.git
251
+
252
+ You can also download a tarball of Warbler source at
253
+ https://github.com/jruby/warbler/archive/master.zip.
254
+
255
+ == Development
256
+
257
+ You can develop Warbler with any implementation of Ruby. To write Warbler code
258
+ and run specs, you need to have Bundler installed and run <tt>bundle</tt> once.
259
+
260
+ After that, simply run <tt>rake</tt>.
261
+
262
+ === Integration Tests
263
+
264
+ There are a few integration tests in the `integration` directory that build WAR file
265
+ with Warbler, and run some basic smoke tests against them. You can run these like so:
266
+
267
+ cd integration
268
+ mvn verify
269
+
270
+ You'll need to have Maven >= 3.1.1 installed, of course: http://maven.apache.org/
271
+
272
+ == License
273
+
274
+ Warbler is provided under the terms of the MIT license.
275
+
276
+ Warbler (c) 2013-2018 The JRuby Team
277
+
278
+ Warbler (c) 2010-2012 Engine Yard, Inc.
279
+
280
+ Warbler (c) 2007-2009 Sun Microsystems, Inc.
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ #-*- mode: ruby -*-
2
+ #--
3
+ # Copyright (c) 2010-2012 Engine Yard, Inc.
4
+ # Copyright (c) 2007-2009 Sun Microsystems, Inc.
5
+ # This source code is available under the MIT license.
6
+ # See the file LICENSE.txt for details.
7
+ #++
8
+
9
+ begin
10
+ require 'bundler'
11
+ rescue LoadError
12
+ warn "\nPlease `gem install bundler' and run `bundle install' to ensure you have all dependencies.\n\n"
13
+ else
14
+ require 'bundler/gem_helper'
15
+ Bundler::GemHelper.install_tasks :dir => File.dirname(__FILE__)
16
+ end
17
+
18
+ require 'rake/clean'
19
+ CLEAN << "pkg" << "doc" << Dir['integration/**/target']
20
+
21
+ require 'rspec/core/rake_task'
22
+ RSpec::Core::RakeTask.new(:spec) do |t|
23
+ t.rspec_opts = ['--color', "--format documentation"]
24
+ end
25
+
26
+ task :default => :spec
27
+
28
+ # use Mavenfile to define :jar task
29
+ require 'maven/ruby/maven'
30
+ mvn = Maven::Ruby::Maven.new
31
+ if defined?(JRUBY_VERSION) && !JRUBY_VERSION.start_with?('9.0')
32
+ mvn.inherit_jruby_version
33
+ end
34
+
35
+ desc 'compile java sources and build jar'
36
+ task :jar do
37
+ mvn.prepare_package
38
+ end
39
+
40
+ desc 'run some integration test'
41
+ task :integration do
42
+ mvn.verify
43
+ end
44
+
45
+ desc 'generate the pom.xml from the Mavenfile'
46
+ task :pom do
47
+ mvn.validate('-Dpolyglot.dump.pom=pom.xml')
48
+ end
49
+
50
+ # Make sure jar gets compiled before the gem is built
51
+ task :build => :jar
52
+
53
+ require 'rdoc/task'
54
+ RDoc::Task.new(:docs) do |rd|
55
+ gemspec = Gem::Specification.load(File.expand_path('warbler.gemspec', File.dirname(__FILE__)))
56
+ rd.rdoc_dir = "doc"
57
+ rd.rdoc_files.include("README.rdoc", "History.txt", "LICENSE.txt")
58
+ rd.rdoc_files += gemspec.require_paths
59
+ rd.options << '--title' << "#{gemspec.name}-#{gemspec.version} Documentation"
60
+ rd.options += gemspec.rdoc_options
61
+ end
62
+
63
+ task :release => :docs
data/bin/warble ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ #--
4
+ # Copyright (c) 2010-2011 Engine Yard, Inc.
5
+ # Copyright (c) 2007-2009 Sun Microsystems, Inc.
6
+ # This source code is available under the MIT license.
7
+ # See the file LICENSE.txt for details.
8
+ #++
9
+
10
+ require 'warbler'
11
+ Warbler::Application.run
data/ext/JarMain.java ADDED
@@ -0,0 +1,334 @@
1
+ /**
2
+ * Copyright (c) 2010-2012 Engine Yard, Inc.
3
+ * Copyright (c) 2007-2009 Sun Microsystems, Inc.
4
+ * This source code is available under the MIT license.
5
+ * See the file LICENSE.txt for details.
6
+ */
7
+
8
+ import java.io.File;
9
+ import java.io.IOException;
10
+ import java.io.FileOutputStream;
11
+ import java.io.InputStream;
12
+ import java.io.PrintStream;
13
+ import java.lang.reflect.InvocationTargetException;
14
+ import java.lang.reflect.Method;
15
+ import java.net.URI;
16
+ import java.net.URISyntaxException;
17
+ import java.net.URL;
18
+ import java.net.URLClassLoader;
19
+ import java.util.ArrayList;
20
+ import java.util.Arrays;
21
+ import java.util.Enumeration;
22
+ import java.util.HashMap;
23
+ import java.util.List;
24
+ import java.util.Map;
25
+ import java.util.jar.JarEntry;
26
+ import java.util.jar.JarFile;
27
+
28
+ public class JarMain implements Runnable {
29
+
30
+ static final String MAIN = '/' + JarMain.class.getName().replace('.', '/') + ".class";
31
+
32
+ protected final String[] args;
33
+ protected final String archive;
34
+ private final String path;
35
+
36
+ protected File extractRoot;
37
+
38
+ protected URLClassLoader classLoader;
39
+
40
+ JarMain(String[] args) {
41
+ this.args = args;
42
+ URL mainClass = getClass().getResource(MAIN);
43
+ URI uri;
44
+ File file;
45
+
46
+ try {
47
+ this.path = mainClass.toURI().getSchemeSpecificPart();
48
+ uri = new URI(this.path.replace("!" + MAIN, ""));
49
+ }
50
+ catch (URISyntaxException e) {
51
+ throw new RuntimeException(e);
52
+ }
53
+
54
+ archive = new File(uri.getPath()).getAbsolutePath();
55
+
56
+ Runtime.getRuntime().addShutdownHook(new Thread(this));
57
+ }
58
+
59
+ protected URL[] extractArchive() throws Exception {
60
+ final JarFile jarFile = new JarFile(archive);
61
+ try {
62
+ Map<String, JarEntry> jarNames = new HashMap<String, JarEntry>();
63
+ for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
64
+ JarEntry entry = e.nextElement();
65
+ String extractPath = getExtractEntryPath(entry);
66
+ if ( extractPath != null ) jarNames.put(extractPath, entry);
67
+ }
68
+
69
+ extractRoot = File.createTempFile("jruby", "extract");
70
+ extractRoot.delete(); extractRoot.mkdirs();
71
+
72
+ final List<URL> urls = new ArrayList<URL>(jarNames.size());
73
+ for (Map.Entry<String, JarEntry> e : jarNames.entrySet()) {
74
+ URL entryURL = extractEntry(e.getValue(), e.getKey());
75
+ if (entryURL != null) urls.add( entryURL );
76
+ }
77
+ return urls.toArray(new URL[urls.size()]);
78
+ }
79
+ finally {
80
+ jarFile.close();
81
+ }
82
+ }
83
+
84
+ protected String getExtractEntryPath(final JarEntry entry) {
85
+ final String name = entry.getName();
86
+ if ( name.startsWith("META-INF/lib") && name.endsWith(".jar") ) {
87
+ return name.substring(name.lastIndexOf('/') + 1);
88
+ }
89
+ return null; // do not extract entry
90
+ }
91
+
92
+ protected URL extractEntry(final JarEntry entry, final String path) throws Exception {
93
+ final File file = new File(extractRoot, path);
94
+ if ( entry.isDirectory() ) {
95
+ file.mkdirs();
96
+ return null;
97
+ }
98
+ final String entryPath = entryPath(entry.getName());
99
+ final InputStream entryStream;
100
+ try {
101
+ entryStream = new URI("jar", entryPath, null).toURL().openStream();
102
+ }
103
+ catch (IllegalArgumentException e) {
104
+ // TODO gems '%' file name "encoding" ?!
105
+ debug("failed to open jar:" + entryPath + " skipping entry: " + entry.getName(), e);
106
+ return null;
107
+ }
108
+ final File parent = file.getParentFile();
109
+ if ( parent != null ) parent.mkdirs();
110
+ FileOutputStream outStream = new FileOutputStream(file);
111
+ final byte[] buf = new byte[65536];
112
+ try {
113
+ int bytesRead;
114
+ while ((bytesRead = entryStream.read(buf)) != -1) {
115
+ outStream.write(buf, 0, bytesRead);
116
+ }
117
+ }
118
+ finally {
119
+ entryStream.close();
120
+ outStream.close();
121
+ file.deleteOnExit();
122
+ }
123
+ // if (false) debug(entry.getName() + " extracted to " + file.getPath());
124
+ return file.toURI().toURL();
125
+ }
126
+
127
+ protected String entryPath(String name) {
128
+ if ( ! name.startsWith("/") ) name = "/" + name;
129
+ return path.replace(MAIN, name);
130
+ }
131
+
132
+ protected Object newScriptingContainer(final URL[] jars) throws Exception {
133
+ setSystemProperty("org.jruby.embed.class.path", "");
134
+ classLoader = new URLClassLoader(jars);
135
+ Class scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, classLoader);
136
+ Object scriptingContainer = scriptingContainerClass.newInstance();
137
+ debug("scripting container class loader urls: " + Arrays.toString(jars));
138
+ invokeMethod(scriptingContainer, "setArgv", (Object) args);
139
+ invokeMethod(scriptingContainer, "setClassLoader", new Class[] { ClassLoader.class }, classLoader);
140
+ return scriptingContainer;
141
+ }
142
+
143
+ protected int launchJRuby(final URL[] jars) throws Exception {
144
+ final Object scriptingContainer = newScriptingContainer(jars);
145
+ debug("invoking " + archive + " with: " + Arrays.deepToString(args));
146
+ Object outcome = invokeMethod(scriptingContainer, "runScriptlet", launchScript());
147
+ return ( outcome instanceof Number ) ? ( (Number) outcome ).intValue() : 0;
148
+ }
149
+
150
+ protected String launchScript() {
151
+ return
152
+ "begin\n" +
153
+ " require 'META-INF/init.rb'\n" +
154
+ " require 'META-INF/main.rb'\n" +
155
+ " 0\n" +
156
+ "rescue SystemExit => e\n" +
157
+ " e.status\n" +
158
+ "end";
159
+ }
160
+
161
+ protected int start() throws Exception {
162
+ final URL[] jars = extractArchive();
163
+ return launchJRuby(jars);
164
+ }
165
+
166
+ protected void debug(String msg) {
167
+ debug(msg, null);
168
+ }
169
+
170
+ protected void debug(String msg, Throwable t) {
171
+ if ( isDebug() ) System.out.println(msg);
172
+ if ( isDebug() && t != null ) t.printStackTrace(System.out);
173
+ }
174
+
175
+ protected static void debug(Throwable t) {
176
+ debug(t, System.out);
177
+ }
178
+
179
+ private static void debug(Throwable t, PrintStream out) {
180
+ if ( isDebug() ) t.printStackTrace(out);
181
+ }
182
+
183
+ protected void warn(String msg) {
184
+ System.out.println("WARNING: " + msg);
185
+ }
186
+
187
+ protected static void error(Throwable t) {
188
+ error(t.toString(), t);
189
+ }
190
+
191
+ protected static void error(String msg, Throwable t) {
192
+ System.err.println("ERROR: " + msg);
193
+ debug(t, System.err);
194
+ }
195
+
196
+ protected void delete(File f) {
197
+ try {
198
+ if (f.isDirectory() && !isSymlink(f)) {
199
+ File[] children = f.listFiles();
200
+ for (int i = 0; i < children.length; i++) {
201
+ delete(children[i]);
202
+ }
203
+ }
204
+ f.delete();
205
+ }
206
+ catch (IOException e) { error(e); }
207
+ }
208
+
209
+ protected boolean isSymlink(File file) throws IOException {
210
+ if (file == null) throw new NullPointerException("File must not be null");
211
+ final File canonical;
212
+ if ( file.getParent() == null ) canonical = file;
213
+ else {
214
+ File parentDir = file.getParentFile().getCanonicalFile();
215
+ canonical = new File(parentDir, file.getName());
216
+ }
217
+ return ! canonical.getCanonicalFile().equals( canonical.getAbsoluteFile() );
218
+ }
219
+
220
+ public void run() {
221
+ // If the URLClassLoader isn't closed, on Windows, temp JARs won't be cleaned up
222
+ try {
223
+ invokeMethod(classLoader, "close");
224
+ }
225
+ catch (NoSuchMethodException e) { } // We're not being run on Java >= 7
226
+ catch (Exception e) { error(e); }
227
+
228
+ if ( extractRoot != null ) delete(extractRoot);
229
+ }
230
+
231
+ public static void main(String[] args) {
232
+ doStart(new JarMain(args));
233
+ }
234
+
235
+ protected static void doStart(final JarMain main) {
236
+ int exit;
237
+ try {
238
+ exit = main.start();
239
+ }
240
+ catch (Exception e) {
241
+ Throwable t = e;
242
+ while ( t.getCause() != null && t.getCause() != t ) {
243
+ t = t.getCause();
244
+ }
245
+ error(e.toString(), t);
246
+ exit = 1;
247
+ }
248
+ try {
249
+ if ( isSystemExitEnabled() ) System.exit(exit);
250
+ }
251
+ catch (SecurityException e) {
252
+ debug(e);
253
+ }
254
+ }
255
+
256
+ protected static Object invokeMethod(final Object self, final String name, final Object... args)
257
+ throws NoSuchMethodException, IllegalAccessException, Exception {
258
+
259
+ final Class[] signature = new Class[args.length];
260
+ for ( int i = 0; i < args.length; i++ ) signature[i] = args[i].getClass();
261
+ return invokeMethod(self, name, signature, args);
262
+ }
263
+
264
+ protected static Object invokeMethod(final Object self, final String name, final Class[] signature, final Object... args)
265
+ throws NoSuchMethodException, IllegalAccessException, Exception {
266
+ Method method = self.getClass().getDeclaredMethod(name, signature);
267
+ try {
268
+ return method.invoke(self, args);
269
+ }
270
+ catch (InvocationTargetException e) {
271
+ Throwable target = e.getTargetException();
272
+ if (target instanceof Exception) {
273
+ throw (Exception) target;
274
+ }
275
+ throw e;
276
+ }
277
+ }
278
+
279
+ private static final boolean debug;
280
+ static {
281
+ debug = Boolean.parseBoolean( getSystemProperty("warbler.debug", "false") );
282
+ }
283
+
284
+ static boolean isDebug() { return debug; }
285
+
286
+ /**
287
+ * if warbler.skip_system_exit system property is defined, we will not
288
+ * call System.exit in the normal flow. System.exit can cause problems
289
+ * for wrappers like procrun
290
+ */
291
+ private static boolean isSystemExitEnabled(){
292
+ return getSystemProperty("warbler.skip_system_exit") == null; //omission enables System.exit use
293
+ }
294
+
295
+ static String getSystemProperty(final String name) {
296
+ return getSystemProperty(name, null);
297
+ }
298
+
299
+ static String getSystemProperty(final String name, final String defaultValue) {
300
+ try {
301
+ return System.getProperty(name, defaultValue);
302
+ }
303
+ catch (SecurityException e) {
304
+ return defaultValue;
305
+ }
306
+ }
307
+
308
+ static boolean setSystemProperty(final String name, final String value) {
309
+ try {
310
+ System.setProperty(name, value);
311
+ return true;
312
+ }
313
+ catch (SecurityException e) {
314
+ return false;
315
+ }
316
+ }
317
+
318
+ static String getENV(final String name) {
319
+ return getENV(name, null);
320
+ }
321
+
322
+ static String getENV(final String name, final String defaultValue) {
323
+ try {
324
+ if ( System.getenv().containsKey(name) ) {
325
+ return System.getenv().get(name);
326
+ }
327
+ return defaultValue;
328
+ }
329
+ catch (SecurityException e) {
330
+ return defaultValue;
331
+ }
332
+ }
333
+
334
+ }