warbler_updated 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }