warbler 2.0.5 → 2.1.1

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/Rakefile CHANGED
@@ -6,77 +6,58 @@
6
6
  # See the file LICENSE.txt for details.
7
7
  #++
8
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
9
+ require 'bundler/gem_helper'
10
+ Bundler::GemHelper.install_tasks :dir => File.dirname(__FILE__)
17
11
 
18
12
  require 'rake/clean'
19
- CLEAN << "pkg" << "doc" << Dir['integration/**/target']
20
-
21
- unless Rake::Application.method_defined? :last_comment
22
- Rake::Application.module_eval do
23
- alias_method :last_comment, :last_description
24
- end
25
- end # Rake 11 compatibility (due rspec/core/rake_task < 3.0)
13
+ CLEAN << "pkg" << "doc" << Dir['integration/**/target'] << "lib/warbler_jar.jar"
26
14
 
27
15
  require 'rspec/core/rake_task'
28
16
  RSpec::Core::RakeTask.new(:spec) do |t|
29
17
  t.rspec_opts = ['--color', "--format documentation"]
30
18
  end
31
19
 
20
+ task :spec => :jar
21
+
32
22
  task :default => :spec
33
23
 
34
24
  # use Mavenfile to define :jar task
35
25
  require 'maven/ruby/maven'
36
26
  mvn = Maven::Ruby::Maven.new
37
- if defined?(JRUBY_VERISON) && ! (JRUBY_VERSION =~ /^9.0/)
38
- mvn.inherit_jruby_version
39
- end
27
+ mvn << "-Djruby.version=#{JRUBY_VERSION}"
28
+ mvn << "-Dbundler.version=#{Bundler::VERSION}"
29
+ mvn << '--no-transfer-progress'
30
+ mvn << '--color=always'
40
31
 
41
32
  desc 'compile java sources and build jar'
42
33
  task :jar do
43
- mvn.prepare_package
34
+ success = mvn.prepare_package
35
+ exit(1) unless success
44
36
  end
45
37
 
46
38
  desc 'run some integration test'
47
- task :integration do
48
- mvn.verify
39
+ task :integration => :jar do
40
+ success = mvn.verify
41
+ exit(1) unless success
49
42
  end
50
43
 
51
44
  desc 'generate the pom.xml from the Mavenfile'
52
45
  task :pom do
53
- mvn.validate('-Dpolyglot.dump.pom=pom.xml')
46
+ success = mvn.validate('-Dpolyglot.dump.pom=pom.xml')
47
+ exit(1) unless success
54
48
  end
55
49
 
56
50
  # Make sure jar gets compiled before the gem is built
57
51
  task :build => :jar
58
52
 
59
- load_gemspec = lambda do
60
- Gem::Specification.load(File.expand_path('warbler.gemspec', File.dirname(__FILE__)))
61
- end
62
-
63
53
  require 'rdoc/task'
64
54
  RDoc::Task.new(:docs) do |rd|
65
- gemspec = load_gemspec.call
55
+ gemspec = Gem::Specification.load(File.expand_path('warbler.gemspec', File.dirname(__FILE__)))
66
56
  rd.rdoc_dir = "doc"
67
- rd.rdoc_files.include("README.rdoc", "History.txt", "LICENSE.txt")
57
+ rd.rdoc_files.include("README.rdoc", "CHANGELOG.md", "LICENSE.txt")
68
58
  rd.rdoc_files += gemspec.require_paths
69
59
  rd.options << '--title' << "#{gemspec.name}-#{gemspec.version} Documentation"
70
60
  rd.options += gemspec.rdoc_options
71
61
  end
72
62
 
73
- task :release_docs => :docs do
74
- config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml"))) rescue nil
75
- if config
76
- gemspec = load_gemspec.call
77
- dir = "/var/www/gforge-projects/#{gemspec.rubyforge_project}/#{gemspec.name}"
78
- dest = "#{config["username"]}@rubyforge.org:#{dir}"
79
- sh %{rsync -rl --delete doc/ #{dest}}
80
- end
81
- end
82
- task :release => :release_docs
63
+ task :release => :docs
data/ext/JarMain.java CHANGED
@@ -5,10 +5,12 @@
5
5
  * See the file LICENSE.txt for details.
6
6
  */
7
7
 
8
+ import java.io.Closeable;
8
9
  import java.io.File;
9
- import java.io.IOException;
10
10
  import java.io.FileOutputStream;
11
+ import java.io.IOException;
11
12
  import java.io.InputStream;
13
+ import java.io.OutputStream;
12
14
  import java.io.PrintStream;
13
15
  import java.lang.reflect.InvocationTargetException;
14
16
  import java.lang.reflect.Method;
@@ -18,14 +20,18 @@ import java.net.URL;
18
20
  import java.net.URLClassLoader;
19
21
  import java.util.ArrayList;
20
22
  import java.util.Arrays;
23
+ import java.util.Deque;
21
24
  import java.util.Enumeration;
22
25
  import java.util.HashMap;
23
26
  import java.util.List;
24
27
  import java.util.Map;
28
+ import java.util.Objects;
29
+ import java.util.concurrent.Callable;
30
+ import java.util.concurrent.ConcurrentLinkedDeque;
25
31
  import java.util.jar.JarEntry;
26
32
  import java.util.jar.JarFile;
27
33
 
28
- public class JarMain implements Runnable {
34
+ public class JarMain implements Closeable {
29
35
 
30
36
  static final String MAIN = '/' + JarMain.class.getName().replace('.', '/') + ".class";
31
37
 
@@ -34,59 +40,61 @@ public class JarMain implements Runnable {
34
40
  private final String path;
35
41
 
36
42
  protected File extractRoot;
37
-
38
- protected URLClassLoader classLoader;
43
+ final Deque<Closeable> closeables = new ConcurrentLinkedDeque<>();
39
44
 
40
45
  JarMain(String[] args) {
41
46
  this.args = args;
42
- URL mainClass = getClass().getResource(MAIN);
47
+ URL mainClass = Objects.requireNonNull(getClass().getResource(MAIN), MAIN + " not found!");
48
+ URI uri;
49
+ String pathWithoutMain;
50
+
43
51
  try {
44
52
  this.path = mainClass.toURI().getSchemeSpecificPart();
45
- }
46
- catch (URISyntaxException e) {
53
+ pathWithoutMain = mainClass.toURI().getRawSchemeSpecificPart().replace("!" + MAIN, "");
54
+ uri = new URI(pathWithoutMain);
55
+ } catch (URISyntaxException e) {
47
56
  throw new RuntimeException(e);
48
57
  }
49
- archive = this.path.replace("!" + MAIN, "").replace("file:", "");
50
58
 
51
- Runtime.getRuntime().addShutdownHook(new Thread(this));
59
+ archive = new File(uri.getPath()).getAbsolutePath();
60
+
61
+ Runtime.getRuntime().addShutdownHook(new Thread(this::close, "Warbler-Shutdown"));
52
62
  }
53
63
 
54
64
  protected URL[] extractArchive() throws Exception {
55
- final JarFile jarFile = new JarFile(archive);
56
- try {
57
- Map<String, JarEntry> jarNames = new HashMap<String, JarEntry>();
65
+ try (JarFile jarFile = new JarFile(archive)) {
66
+ Map<String, JarEntry> jarNames = new HashMap<>();
58
67
  for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
59
68
  JarEntry entry = e.nextElement();
60
69
  String extractPath = getExtractEntryPath(entry);
61
- if ( extractPath != null ) jarNames.put(extractPath, entry);
70
+ if (extractPath != null) jarNames.put(extractPath, entry);
62
71
  }
63
72
 
64
73
  extractRoot = File.createTempFile("jruby", "extract");
65
- extractRoot.delete(); extractRoot.mkdirs();
74
+ extractRoot.delete();
75
+ extractRoot.mkdirs();
76
+ closeables.add(() -> deleteAll(extractRoot));
66
77
 
67
- final List<URL> urls = new ArrayList<URL>(jarNames.size());
78
+ final List<URL> urls = new ArrayList<>(jarNames.size());
68
79
  for (Map.Entry<String, JarEntry> e : jarNames.entrySet()) {
69
80
  URL entryURL = extractEntry(e.getValue(), e.getKey());
70
- if (entryURL != null) urls.add( entryURL );
81
+ if (entryURL != null) urls.add(entryURL);
71
82
  }
72
- return urls.toArray(new URL[urls.size()]);
73
- }
74
- finally {
75
- jarFile.close();
83
+ return urls.toArray(new URL[0]);
76
84
  }
77
85
  }
78
86
 
79
87
  protected String getExtractEntryPath(final JarEntry entry) {
80
88
  final String name = entry.getName();
81
- if ( name.startsWith("META-INF/lib") && name.endsWith(".jar") ) {
89
+ if (name.startsWith("META-INF/lib") && name.endsWith(".jar")) {
82
90
  return name.substring(name.lastIndexOf('/') + 1);
83
91
  }
84
92
  return null; // do not extract entry
85
93
  }
86
94
 
87
- protected URL extractEntry(final JarEntry entry, final String path) throws Exception {
88
- final File file = new File(extractRoot, path);
89
- if ( entry.isDirectory() ) {
95
+ protected URL extractEntry(final JarEntry entry, String path) throws Exception {
96
+ File file = new File(extractRoot, path);
97
+ if (entry.isDirectory()) {
90
98
  file.mkdirs();
91
99
  return null;
92
100
  }
@@ -94,44 +102,34 @@ public class JarMain implements Runnable {
94
102
  final InputStream entryStream;
95
103
  try {
96
104
  entryStream = new URI("jar", entryPath, null).toURL().openStream();
97
- }
98
- catch (IllegalArgumentException e) {
105
+ } catch (IllegalArgumentException e) {
99
106
  // TODO gems '%' file name "encoding" ?!
100
107
  debug("failed to open jar:" + entryPath + " skipping entry: " + entry.getName(), e);
101
108
  return null;
102
109
  }
103
110
  final File parent = file.getParentFile();
104
- if ( parent != null ) parent.mkdirs();
105
- FileOutputStream outStream = new FileOutputStream(file);
106
- final byte[] buf = new byte[65536];
107
- try {
108
- int bytesRead;
109
- while ((bytesRead = entryStream.read(buf)) != -1) {
110
- outStream.write(buf, 0, bytesRead);
111
- }
112
- }
113
- finally {
114
- entryStream.close();
115
- outStream.close();
116
- file.deleteOnExit();
117
- }
111
+ if (parent != null) parent.mkdirs();
112
+
113
+ transferAndClose(() -> entryStream, () -> new FileOutputStream(file));
114
+ file.deleteOnExit();
118
115
  // if (false) debug(entry.getName() + " extracted to " + file.getPath());
119
116
  return file.toURI().toURL();
120
117
  }
121
118
 
122
119
  protected String entryPath(String name) {
123
- if ( ! name.startsWith("/") ) name = "/" + name;
120
+ if (!name.startsWith("/")) name = "/" + name;
124
121
  return path.replace(MAIN, name);
125
122
  }
126
123
 
127
124
  protected Object newScriptingContainer(final URL[] jars) throws Exception {
128
125
  setSystemProperty("org.jruby.embed.class.path", "");
129
- classLoader = new URLClassLoader(jars);
130
- Class scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, classLoader);
126
+ URLClassLoader scriptingClassLoader = new URLClassLoader(jars);
127
+ closeables.add(scriptingClassLoader);
128
+ Class<?> scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, scriptingClassLoader);
131
129
  Object scriptingContainer = scriptingContainerClass.newInstance();
132
130
  debug("scripting container class loader urls: " + Arrays.toString(jars));
133
131
  invokeMethod(scriptingContainer, "setArgv", (Object) args);
134
- invokeMethod(scriptingContainer, "setClassLoader", new Class[] { ClassLoader.class }, classLoader);
132
+ invokeMethod(scriptingContainer, "setClassLoader", new Class[]{ClassLoader.class}, scriptingClassLoader);
135
133
  return scriptingContainer;
136
134
  }
137
135
 
@@ -139,18 +137,18 @@ public class JarMain implements Runnable {
139
137
  final Object scriptingContainer = newScriptingContainer(jars);
140
138
  debug("invoking " + archive + " with: " + Arrays.deepToString(args));
141
139
  Object outcome = invokeMethod(scriptingContainer, "runScriptlet", launchScript());
142
- return ( outcome instanceof Number ) ? ( (Number) outcome ).intValue() : 0;
140
+ return (outcome instanceof Number) ? ((Number) outcome).intValue() : 0;
143
141
  }
144
142
 
145
143
  protected String launchScript() {
146
144
  return
147
- "begin\n" +
148
- " require 'META-INF/init.rb'\n" +
149
- " require 'META-INF/main.rb'\n" +
150
- " 0\n" +
151
- "rescue SystemExit => e\n" +
152
- " e.status\n" +
153
- "end";
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";
154
152
  }
155
153
 
156
154
  protected int start() throws Exception {
@@ -163,8 +161,8 @@ public class JarMain implements Runnable {
163
161
  }
164
162
 
165
163
  protected void debug(String msg, Throwable t) {
166
- if ( isDebug() ) System.out.println(msg);
167
- if ( isDebug() && t != null ) t.printStackTrace(System.out);
164
+ if (isDebug()) System.out.println(msg);
165
+ if (isDebug() && t != null) t.printStackTrace(System.out);
168
166
  }
169
167
 
170
168
  protected static void debug(Throwable t) {
@@ -172,7 +170,7 @@ public class JarMain implements Runnable {
172
170
  }
173
171
 
174
172
  private static void debug(Throwable t, PrintStream out) {
175
- if ( isDebug() ) t.printStackTrace(out);
173
+ if (isDebug()) t.printStackTrace(out);
176
174
  }
177
175
 
178
176
  protected void warn(String msg) {
@@ -188,39 +186,42 @@ public class JarMain implements Runnable {
188
186
  debug(t, System.err);
189
187
  }
190
188
 
191
- protected void delete(File f) {
189
+ protected void deleteAll(File f) {
192
190
  try {
193
- if (f.isDirectory() && !isSymlink(f)) {
194
- File[] children = f.listFiles();
195
- for (int i = 0; i < children.length; i++) {
196
- delete(children[i]);
197
- }
198
- }
199
- f.delete();
191
+ if (f.isDirectory() && !isSymlink(f)) {
192
+ File[] children = f.listFiles();
193
+ if (children != null) {
194
+ for (File child : children) {
195
+ deleteAll(child);
196
+ }
197
+ }
198
+ }
199
+ f.delete();
200
+ } catch (IOException e) {
201
+ error(e);
200
202
  }
201
- catch (IOException e) { error(e); }
202
203
  }
203
204
 
204
205
  protected boolean isSymlink(File file) throws IOException {
205
206
  if (file == null) throw new NullPointerException("File must not be null");
206
207
  final File canonical;
207
- if ( file.getParent() == null ) canonical = file;
208
+ if (file.getParent() == null) canonical = file;
208
209
  else {
209
210
  File parentDir = file.getParentFile().getCanonicalFile();
210
211
  canonical = new File(parentDir, file.getName());
211
212
  }
212
- return ! canonical.getCanonicalFile().equals( canonical.getAbsoluteFile() );
213
+ return !canonical.getCanonicalFile().equals(canonical.getAbsoluteFile());
213
214
  }
214
215
 
215
- public void run() {
216
- // If the URLClassLoader isn't closed, on Windows, temp JARs won't be cleaned up
217
- try {
218
- invokeMethod(classLoader, "close");
219
- }
220
- catch (NoSuchMethodException e) { } // We're not being run on Java >= 7
221
- catch (Exception e) { error(e); }
222
-
223
- if ( extractRoot != null ) delete(extractRoot);
216
+ @Override
217
+ public void close() {
218
+ closeables.descendingIterator().forEachRemaining(closeableResource -> {
219
+ try {
220
+ closeableResource.close();
221
+ } catch (Exception e) {
222
+ error("Error during shutdown", e);
223
+ }
224
+ });
224
225
  }
225
226
 
226
227
  public static void main(String[] args) {
@@ -231,38 +232,35 @@ public class JarMain implements Runnable {
231
232
  int exit;
232
233
  try {
233
234
  exit = main.start();
234
- }
235
- catch (Exception e) {
235
+ } catch (Exception e) {
236
236
  Throwable t = e;
237
- while ( t.getCause() != null && t.getCause() != t ) {
237
+ while (t.getCause() != null && t.getCause() != t) {
238
238
  t = t.getCause();
239
239
  }
240
240
  error(e.toString(), t);
241
241
  exit = 1;
242
242
  }
243
243
  try {
244
- if ( isSystemExitEnabled() ) System.exit(exit);
245
- }
246
- catch (SecurityException e) {
244
+ if (isSystemExitEnabled()) System.exit(exit);
245
+ } catch (SecurityException e) {
247
246
  debug(e);
248
247
  }
249
248
  }
250
249
 
251
250
  protected static Object invokeMethod(final Object self, final String name, final Object... args)
252
- throws NoSuchMethodException, IllegalAccessException, Exception {
251
+ throws Exception {
253
252
 
254
- final Class[] signature = new Class[args.length];
255
- for ( int i = 0; i < args.length; i++ ) signature[i] = args[i].getClass();
253
+ final Class<?>[] signature = new Class[args.length];
254
+ for (int i = 0; i < args.length; i++) signature[i] = args[i].getClass();
256
255
  return invokeMethod(self, name, signature, args);
257
256
  }
258
257
 
259
- protected static Object invokeMethod(final Object self, final String name, final Class[] signature, final Object... args)
260
- throws NoSuchMethodException, IllegalAccessException, Exception {
258
+ protected static Object invokeMethod(final Object self, final String name, final Class<?>[] signature, final Object... args)
259
+ throws Exception {
261
260
  Method method = self.getClass().getDeclaredMethod(name, signature);
262
261
  try {
263
262
  return method.invoke(self, args);
264
- }
265
- catch (InvocationTargetException e) {
263
+ } catch (InvocationTargetException e) {
266
264
  Throwable target = e.getTargetException();
267
265
  if (target instanceof Exception) {
268
266
  throw (Exception) target;
@@ -272,18 +270,21 @@ public class JarMain implements Runnable {
272
270
  }
273
271
 
274
272
  private static final boolean debug;
273
+
275
274
  static {
276
- debug = Boolean.parseBoolean( getSystemProperty("warbler.debug", "false") );
275
+ debug = Boolean.parseBoolean(getSystemProperty("warbler.debug", "false"));
277
276
  }
278
277
 
279
- static boolean isDebug() { return debug; }
278
+ static boolean isDebug() {
279
+ return debug;
280
+ }
280
281
 
281
282
  /**
282
283
  * if warbler.skip_system_exit system property is defined, we will not
283
284
  * call System.exit in the normal flow. System.exit can cause problems
284
285
  * for wrappers like procrun
285
286
  */
286
- private static boolean isSystemExitEnabled(){
287
+ private static boolean isSystemExitEnabled() {
287
288
  return getSystemProperty("warbler.skip_system_exit") == null; //omission enables System.exit use
288
289
  }
289
290
 
@@ -294,8 +295,7 @@ public class JarMain implements Runnable {
294
295
  static String getSystemProperty(final String name, final String defaultValue) {
295
296
  try {
296
297
  return System.getProperty(name, defaultValue);
297
- }
298
- catch (SecurityException e) {
298
+ } catch (SecurityException e) {
299
299
  return defaultValue;
300
300
  }
301
301
  }
@@ -304,8 +304,7 @@ public class JarMain implements Runnable {
304
304
  try {
305
305
  System.setProperty(name, value);
306
306
  return true;
307
- }
308
- catch (SecurityException e) {
307
+ } catch (SecurityException e) {
309
308
  return false;
310
309
  }
311
310
  }
@@ -316,14 +315,23 @@ public class JarMain implements Runnable {
316
315
 
317
316
  static String getENV(final String name, final String defaultValue) {
318
317
  try {
319
- if ( System.getenv().containsKey(name) ) {
318
+ if (System.getenv().containsKey(name)) {
320
319
  return System.getenv().get(name);
321
320
  }
322
321
  return defaultValue;
323
- }
324
- catch (SecurityException e) {
322
+ } catch (SecurityException e) {
325
323
  return defaultValue;
326
324
  }
327
325
  }
328
326
 
327
+ // Can be replaced with InputStream.transferTo(OutputStream) in Java 11+
328
+ static void transferAndClose(Callable<InputStream> is, Callable<OutputStream> os) throws Exception {
329
+ try (InputStream input = is.call(); OutputStream output = os.call()) {
330
+ byte[] buf = new byte[16384];
331
+ int bytesRead;
332
+ while ((bytesRead = input.read(buf)) != -1) {
333
+ output.write(buf, 0, bytesRead);
334
+ }
335
+ }
336
+ }
329
337
  }