lucidimagination-warbler 1.3.2.dev
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/Gemfile +16 -0
- data/History.txt +217 -0
- data/LICENSE.txt +26 -0
- data/Manifest.txt +92 -0
- data/README.txt +255 -0
- data/Rakefile +103 -0
- data/bin/warble +11 -0
- data/ext/JarMain.java +148 -0
- data/ext/WarMain.java +112 -0
- data/ext/WarblerJar.java +182 -0
- data/ext/WarblerJarService.java +18 -0
- data/lib/warbler.rb +33 -0
- data/lib/warbler/application.rb +86 -0
- data/lib/warbler/config.rb +223 -0
- data/lib/warbler/gems.rb +37 -0
- data/lib/warbler/jar.rb +253 -0
- data/lib/warbler/task.rb +183 -0
- data/lib/warbler/templates/bundler.erb +3 -0
- data/lib/warbler/templates/config.erb +1 -0
- data/lib/warbler/templates/jar.erb +5 -0
- data/lib/warbler/templates/rack.erb +1 -0
- data/lib/warbler/templates/rails.erb +1 -0
- data/lib/warbler/templates/war.erb +1 -0
- data/lib/warbler/traits.rb +107 -0
- data/lib/warbler/traits/bundler.rb +104 -0
- data/lib/warbler/traits/gemspec.rb +59 -0
- data/lib/warbler/traits/jar.rb +56 -0
- data/lib/warbler/traits/merb.rb +35 -0
- data/lib/warbler/traits/nogemspec.rb +42 -0
- data/lib/warbler/traits/rack.rb +33 -0
- data/lib/warbler/traits/rails.rb +62 -0
- data/lib/warbler/traits/war.rb +197 -0
- data/lib/warbler/version.rb +10 -0
- data/lib/warbler/war.rb +8 -0
- data/lib/warbler_jar.jar +0 -0
- data/spec/drb_helper.rb +41 -0
- data/spec/sample_bundler/Gemfile.lock +10 -0
- data/spec/sample_bundler/config.ru +0 -0
- data/spec/sample_jar/History.txt +6 -0
- data/spec/sample_jar/Manifest.txt +8 -0
- data/spec/sample_jar/README.txt +30 -0
- data/spec/sample_jar/lib/sample_jar.rb +6 -0
- data/spec/sample_jar/sample_jar.gemspec +40 -0
- data/spec/sample_jar/test/test_sample_jar.rb +8 -0
- data/spec/sample_war/app/controllers/application.rb +15 -0
- data/spec/sample_war/app/helpers/application_helper.rb +3 -0
- data/spec/sample_war/config/boot.rb +109 -0
- data/spec/sample_war/config/database.yml +19 -0
- data/spec/sample_war/config/environment.rb +67 -0
- data/spec/sample_war/config/environments/development.rb +17 -0
- data/spec/sample_war/config/environments/production.rb +25 -0
- data/spec/sample_war/config/environments/test.rb +22 -0
- data/spec/sample_war/config/initializers/inflections.rb +10 -0
- data/spec/sample_war/config/initializers/mime_types.rb +5 -0
- data/spec/sample_war/config/initializers/new_rails_defaults.rb +15 -0
- data/spec/sample_war/config/routes.rb +41 -0
- data/spec/sample_war/lib/tasks/utils.rake +0 -0
- data/spec/sample_war/public/404.html +30 -0
- data/spec/sample_war/public/422.html +30 -0
- data/spec/sample_war/public/500.html +30 -0
- data/spec/sample_war/public/favicon.ico +0 -0
- data/spec/sample_war/public/index.html +274 -0
- data/spec/sample_war/public/robots.txt +5 -0
- data/spec/spec_helper.rb +112 -0
- data/spec/warbler/application_spec.rb +95 -0
- data/spec/warbler/bundler_spec.rb +136 -0
- data/spec/warbler/config_spec.rb +130 -0
- data/spec/warbler/gems_spec.rb +40 -0
- data/spec/warbler/jar_spec.rb +718 -0
- data/spec/warbler/task_spec.rb +170 -0
- data/spec/warbler/traits_spec.rb +17 -0
- data/spec/warbler/war_spec.rb +14 -0
- data/warble.rb +142 -0
- data/web.xml.erb +32 -0
- metadata +198 -0
data/Rakefile
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2010-2011 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
|
+
begin
|
9
|
+
require 'bundler/setup'
|
10
|
+
rescue LoadError
|
11
|
+
puts "Please install Bundler and run 'bundle install' to ensure you have all dependencies"
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'spec/rake/spectask'
|
15
|
+
require 'spec/rake/verify_rcov'
|
16
|
+
|
17
|
+
MANIFEST = FileList["History.txt", "Manifest.txt", "README.txt", "Gemfile",
|
18
|
+
"LICENSE.txt", "Rakefile", "*.erb", "*.rb", "bin/*",
|
19
|
+
"ext/**/*", "lib/**/*", "spec/**/*.rb", "spec/sample*/**/*.*"
|
20
|
+
].to_a.reject{|f| f=~%r{spec/sample/(MANIFEST|link|web.xml)}}.sort.uniq
|
21
|
+
|
22
|
+
begin
|
23
|
+
File.open("Manifest.txt", "wb") {|f| MANIFEST.each {|n| f << "#{n}\n"} }
|
24
|
+
require 'hoe'
|
25
|
+
require File.dirname(__FILE__) + '/lib/warbler/version'
|
26
|
+
Hoe.plugin :rubyforge
|
27
|
+
hoe = Hoe.spec("warbler") do |p|
|
28
|
+
p.version = Warbler::VERSION
|
29
|
+
p.rubyforge_name = "caldersphere"
|
30
|
+
p.url = "http://caldersphere.rubyforge.org/warbler"
|
31
|
+
p.author = "Nick Sieger"
|
32
|
+
p.email = "nick@nicksieger.com"
|
33
|
+
p.summary = "Warbler chirpily constructs .war files of your Rails applications."
|
34
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
35
|
+
p.description = p.paragraphs_of('README.txt', 1...2).join("\n\n")
|
36
|
+
p.extra_deps += [['rake', '~> 0.8.7'], ['jruby-jars', '>= 1.4.0'], ['jruby-rack', '>= 1.0.0'], ['rubyzip', '>= 0.9.4']]
|
37
|
+
p.clean_globs += %w(MANIFEST web.xml init.rb).map{|f| "spec/sample*/#{f}*" }
|
38
|
+
end
|
39
|
+
hoe.spec.files = MANIFEST
|
40
|
+
hoe.spec.rdoc_options += ["-SHN", "-f", "darkfish"]
|
41
|
+
|
42
|
+
task :gemspec do
|
43
|
+
File.open("#{hoe.name}.gemspec", "w") {|f| f << hoe.spec.to_ruby }
|
44
|
+
end
|
45
|
+
task :package => :gemspec
|
46
|
+
rescue LoadError
|
47
|
+
puts "You really need Hoe installed to be able to package this gem"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Leave my tasks alone, Hoe
|
51
|
+
%w(default spec rcov).each do |task|
|
52
|
+
Rake::Task[task].prerequisites.clear
|
53
|
+
Rake::Task[task].actions.clear
|
54
|
+
end
|
55
|
+
|
56
|
+
Spec::Rake::SpecTask.new do |t|
|
57
|
+
t.spec_opts ||= []
|
58
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
59
|
+
end
|
60
|
+
|
61
|
+
Spec::Rake::SpecTask.new("spec:rcov") do |t|
|
62
|
+
t.spec_opts ||= []
|
63
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
64
|
+
t.rcov_opts ||= []
|
65
|
+
t.rcov_opts << "-x" << "/gems/"
|
66
|
+
t.rcov = true
|
67
|
+
end
|
68
|
+
|
69
|
+
RCov::VerifyTask.new(:rcov => "spec:rcov") do |t|
|
70
|
+
t.threshold = 100
|
71
|
+
end
|
72
|
+
|
73
|
+
task :default => :spec
|
74
|
+
|
75
|
+
desc "Compile and jar the Warbler Java helper classes"
|
76
|
+
begin
|
77
|
+
require 'ant'
|
78
|
+
task :jar => :compile do
|
79
|
+
ant.jar :basedir => "pkg/classes", :destfile => "lib/warbler_jar.jar", :includes => "*.class"
|
80
|
+
end
|
81
|
+
|
82
|
+
directory "pkg/classes"
|
83
|
+
task :compile => "pkg/classes" do |t|
|
84
|
+
ant.javac :srcdir => "ext", :destdir => t.prerequisites.first,
|
85
|
+
:source => "1.5", :target => "1.5", :debug => true,
|
86
|
+
:classpath => "${java.class.path}:${sun.boot.class.path}",
|
87
|
+
:includeantRuntime => false
|
88
|
+
end
|
89
|
+
rescue LoadError
|
90
|
+
task :jar do
|
91
|
+
puts "Run 'jar' with JRuby >= 1.5 to re-compile the java jar booster"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Make sure jar gets compiled before the gem is built
|
96
|
+
task Rake::Task['gem'].prerequisites.first => :jar
|
97
|
+
|
98
|
+
task :warbler_jar => 'pkg' do
|
99
|
+
ruby "-rubygems", "-Ilib", "-S", "bin/warble"
|
100
|
+
mv "warbler.jar", "pkg/warbler-#{Warbler::VERSION}.jar"
|
101
|
+
end
|
102
|
+
|
103
|
+
task :package => :warbler_jar
|
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.new.run
|
data/ext/JarMain.java
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2010-2011 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.FileOutputStream;
|
10
|
+
import java.io.InputStream;
|
11
|
+
import java.lang.reflect.Method;
|
12
|
+
import java.net.URL;
|
13
|
+
import java.net.URLClassLoader;
|
14
|
+
import java.util.ArrayList;
|
15
|
+
import java.util.Arrays;
|
16
|
+
import java.util.Enumeration;
|
17
|
+
import java.util.List;
|
18
|
+
import java.util.jar.JarEntry;
|
19
|
+
import java.util.jar.JarFile;
|
20
|
+
|
21
|
+
public class JarMain implements Runnable {
|
22
|
+
public static final String MAIN = "/" + JarMain.class.getName().replace('.', '/') + ".class";
|
23
|
+
|
24
|
+
private String[] args;
|
25
|
+
private String path, jarfile;
|
26
|
+
private boolean debug;
|
27
|
+
private File extractRoot;
|
28
|
+
|
29
|
+
public JarMain(String[] args) throws Exception {
|
30
|
+
this.args = args;
|
31
|
+
URL mainClass = getClass().getResource(MAIN);
|
32
|
+
this.path = mainClass.toURI().getSchemeSpecificPart();
|
33
|
+
this.jarfile = this.path.replace("!" + MAIN, "").replace("file:", "");
|
34
|
+
this.debug = isDebug();
|
35
|
+
this.extractRoot = File.createTempFile("jruby", "extract");
|
36
|
+
this.extractRoot.delete();
|
37
|
+
this.extractRoot.mkdirs();
|
38
|
+
Runtime.getRuntime().addShutdownHook(new Thread(this));
|
39
|
+
}
|
40
|
+
|
41
|
+
private URL[] extractJRuby() throws Exception {
|
42
|
+
JarFile jf = new JarFile(this.jarfile);
|
43
|
+
List<String> jarNames = new ArrayList<String>();
|
44
|
+
for (Enumeration<JarEntry> eje = jf.entries(); eje.hasMoreElements(); ) {
|
45
|
+
String name = eje.nextElement().getName();
|
46
|
+
if (name.startsWith("META-INF/lib") && name.endsWith(".jar")) {
|
47
|
+
jarNames.add("/" + name);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
List<URL> urls = new ArrayList<URL>();
|
52
|
+
for (String name : jarNames) {
|
53
|
+
urls.add(extractJar(name));
|
54
|
+
}
|
55
|
+
|
56
|
+
return (URL[]) urls.toArray(new URL[urls.size()]);
|
57
|
+
}
|
58
|
+
|
59
|
+
private URL extractJar(String jarpath) throws Exception {
|
60
|
+
InputStream jarStream = new URL("jar:" + path.replace(MAIN, jarpath)).openStream();
|
61
|
+
String jarname = jarpath.substring(jarpath.lastIndexOf("/") + 1, jarpath.lastIndexOf("."));
|
62
|
+
File jarFile = new File(extractRoot, jarname + ".jar");
|
63
|
+
jarFile.deleteOnExit();
|
64
|
+
FileOutputStream outStream = new FileOutputStream(jarFile);
|
65
|
+
try {
|
66
|
+
byte[] buf = new byte[65536];
|
67
|
+
int bytesRead = 0;
|
68
|
+
while ((bytesRead = jarStream.read(buf)) != -1) {
|
69
|
+
outStream.write(buf, 0, bytesRead);
|
70
|
+
}
|
71
|
+
} finally {
|
72
|
+
jarStream.close();
|
73
|
+
outStream.close();
|
74
|
+
}
|
75
|
+
debug(jarname + ".jar extracted to " + jarFile.getPath());
|
76
|
+
return jarFile.toURI().toURL();
|
77
|
+
}
|
78
|
+
|
79
|
+
private int launchJRuby(URL[] jars) throws Exception {
|
80
|
+
System.setProperty("org.jruby.embed.class.path", "");
|
81
|
+
URLClassLoader loader = new URLClassLoader(jars);
|
82
|
+
Class scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, loader);
|
83
|
+
Object scriptingContainer = scriptingContainerClass.newInstance();
|
84
|
+
|
85
|
+
Method argv = scriptingContainerClass.getDeclaredMethod("setArgv", new Class[] {String[].class});
|
86
|
+
argv.invoke(scriptingContainer, new Object[] {args});
|
87
|
+
Method setClassLoader = scriptingContainerClass.getDeclaredMethod("setClassLoader", new Class[] {ClassLoader.class});
|
88
|
+
setClassLoader.invoke(scriptingContainer, new Object[] {loader});
|
89
|
+
debug("invoking " + jarfile + " with: " + Arrays.deepToString(args));
|
90
|
+
|
91
|
+
Method runScriptlet = scriptingContainerClass.getDeclaredMethod("runScriptlet", new Class[] {String.class});
|
92
|
+
return ((Number) runScriptlet.invoke(scriptingContainer, new Object[] {
|
93
|
+
"begin\n" +
|
94
|
+
"require 'META-INF/init.rb'\n" +
|
95
|
+
"require 'META-INF/main.rb'\n" +
|
96
|
+
"0\n" +
|
97
|
+
"rescue SystemExit => e\n" +
|
98
|
+
"e.status\n" +
|
99
|
+
"end"
|
100
|
+
})).intValue();
|
101
|
+
}
|
102
|
+
|
103
|
+
private int start() throws Exception {
|
104
|
+
URL[] u = extractJRuby();
|
105
|
+
return launchJRuby(u);
|
106
|
+
}
|
107
|
+
|
108
|
+
private void debug(String msg) {
|
109
|
+
if (debug) {
|
110
|
+
System.out.println(msg);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
private void delete(File f) {
|
115
|
+
if (f.isDirectory()) {
|
116
|
+
File[] children = f.listFiles();
|
117
|
+
for (int i = 0; i < children.length; i++) {
|
118
|
+
delete(children[i]);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
f.delete();
|
122
|
+
}
|
123
|
+
|
124
|
+
public void run() {
|
125
|
+
delete(extractRoot);
|
126
|
+
}
|
127
|
+
|
128
|
+
public static void main(String[] args) {
|
129
|
+
try {
|
130
|
+
int exit = new JarMain(args).start();
|
131
|
+
System.exit(exit);
|
132
|
+
} catch (Exception e) {
|
133
|
+
Throwable t = e;
|
134
|
+
while (t.getCause() != null && t.getCause() != t) {
|
135
|
+
t = t.getCause();
|
136
|
+
}
|
137
|
+
|
138
|
+
if (isDebug()) {
|
139
|
+
t.printStackTrace();
|
140
|
+
}
|
141
|
+
System.exit(1);
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
private static boolean isDebug() {
|
146
|
+
return System.getProperty("warbler.debug") != null;
|
147
|
+
}
|
148
|
+
}
|
data/ext/WarMain.java
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2010-2011 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.net.URLClassLoader;
|
9
|
+
import java.net.URL;
|
10
|
+
import java.lang.reflect.Method;
|
11
|
+
import java.io.IOException;
|
12
|
+
import java.io.InputStream;
|
13
|
+
import java.io.File;
|
14
|
+
import java.io.FileOutputStream;
|
15
|
+
import java.util.Arrays;
|
16
|
+
|
17
|
+
public class WarMain implements Runnable {
|
18
|
+
public static final String MAIN = "/" + WarMain.class.getName().replace('.', '/') + ".class";
|
19
|
+
public static final String WINSTONE_JAR = "/WEB-INF/winstone.jar";
|
20
|
+
|
21
|
+
private String[] args;
|
22
|
+
private String path, warfile;
|
23
|
+
private boolean debug;
|
24
|
+
private File webroot;
|
25
|
+
|
26
|
+
public WarMain(String[] args) throws Exception {
|
27
|
+
this.args = args;
|
28
|
+
URL mainClass = getClass().getResource(MAIN);
|
29
|
+
this.path = mainClass.toURI().getSchemeSpecificPart();
|
30
|
+
this.warfile = this.path.replace("!" + MAIN, "").replace("file:", "");
|
31
|
+
this.debug = isDebug();
|
32
|
+
this.webroot = File.createTempFile("winstone", "webroot");
|
33
|
+
this.webroot.delete();
|
34
|
+
this.webroot.mkdirs();
|
35
|
+
this.webroot = new File(this.webroot, new File(warfile).getName());
|
36
|
+
Runtime.getRuntime().addShutdownHook(new Thread(this));
|
37
|
+
}
|
38
|
+
|
39
|
+
private URL extractWinstone() throws Exception {
|
40
|
+
InputStream jarStream = new URL("jar:" + path.replace(MAIN, WINSTONE_JAR)).openStream();
|
41
|
+
File jarFile = File.createTempFile("winstone", ".jar");
|
42
|
+
jarFile.deleteOnExit();
|
43
|
+
FileOutputStream outStream = new FileOutputStream(jarFile);
|
44
|
+
try {
|
45
|
+
byte[] buf = new byte[4096];
|
46
|
+
int bytesRead = 0;
|
47
|
+
while ((bytesRead = jarStream.read(buf)) != -1) {
|
48
|
+
outStream.write(buf, 0, bytesRead);
|
49
|
+
}
|
50
|
+
} finally {
|
51
|
+
jarStream.close();
|
52
|
+
outStream.close();
|
53
|
+
}
|
54
|
+
debug("winstone.jar extracted to " + jarFile.getPath());
|
55
|
+
return jarFile.toURI().toURL();
|
56
|
+
}
|
57
|
+
|
58
|
+
private void launchWinstone(URL jar) throws Exception {
|
59
|
+
URLClassLoader loader = new URLClassLoader(new URL[] {jar});
|
60
|
+
Class klass = Class.forName("winstone.Launcher", true, loader);
|
61
|
+
Method main = klass.getDeclaredMethod("main", new Class[] {String[].class});
|
62
|
+
String[] newargs = new String[args.length + 3];
|
63
|
+
newargs[0] = "--warfile=" + warfile;
|
64
|
+
newargs[1] = "--webroot=" + webroot;
|
65
|
+
newargs[2] = "--directoryListings=false";
|
66
|
+
System.arraycopy(args, 0, newargs, 3, args.length);
|
67
|
+
debug("invoking Winstone with: " + Arrays.deepToString(newargs));
|
68
|
+
main.invoke(null, new Object[] {newargs});
|
69
|
+
}
|
70
|
+
|
71
|
+
private void start() throws Exception {
|
72
|
+
URL u = extractWinstone();
|
73
|
+
launchWinstone(u);
|
74
|
+
}
|
75
|
+
|
76
|
+
private void debug(String msg) {
|
77
|
+
if (debug) {
|
78
|
+
System.out.println(msg);
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
private void delete(File f) {
|
83
|
+
if (f.isDirectory()) {
|
84
|
+
File[] children = f.listFiles();
|
85
|
+
for (int i = 0; i < children.length; i++) {
|
86
|
+
delete(children[i]);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
f.delete();
|
90
|
+
}
|
91
|
+
|
92
|
+
public void run() {
|
93
|
+
delete(webroot.getParentFile());
|
94
|
+
}
|
95
|
+
|
96
|
+
public static void main(String[] args) {
|
97
|
+
try {
|
98
|
+
new WarMain(args).start();
|
99
|
+
} catch (Exception e) {
|
100
|
+
System.err.println("error: " + e.toString());
|
101
|
+
if (isDebug()) {
|
102
|
+
e.printStackTrace();
|
103
|
+
}
|
104
|
+
System.exit(1);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
private static boolean isDebug() {
|
109
|
+
return System.getProperty("warbler.debug") != null;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
data/ext/WarblerJar.java
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright (c) 2010-2011 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.Closeable;
|
9
|
+
import java.io.File;
|
10
|
+
import java.io.FileInputStream;
|
11
|
+
import java.io.FileNotFoundException;
|
12
|
+
import java.io.FileOutputStream;
|
13
|
+
import java.io.IOException;
|
14
|
+
import java.io.InputStream;
|
15
|
+
import java.util.regex.Matcher;
|
16
|
+
import java.util.regex.Pattern;
|
17
|
+
import java.util.zip.ZipEntry;
|
18
|
+
import java.util.zip.ZipInputStream;
|
19
|
+
import java.util.zip.ZipOutputStream;
|
20
|
+
|
21
|
+
import org.jruby.Ruby;
|
22
|
+
import org.jruby.RubyArray;
|
23
|
+
import org.jruby.RubyHash;
|
24
|
+
import org.jruby.RubyModule;
|
25
|
+
import org.jruby.RubyString;
|
26
|
+
import org.jruby.anno.JRubyMethod;
|
27
|
+
import org.jruby.runtime.Block;
|
28
|
+
import org.jruby.runtime.ThreadContext;
|
29
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
30
|
+
import org.jruby.util.ByteList;
|
31
|
+
import org.jruby.util.JRubyFile;
|
32
|
+
|
33
|
+
public class WarblerJar {
|
34
|
+
public static void create(Ruby runtime) {
|
35
|
+
RubyModule task = runtime.getClassFromPath("Warbler::Jar");
|
36
|
+
task.defineAnnotatedMethods(WarblerJar.class);
|
37
|
+
}
|
38
|
+
|
39
|
+
@JRubyMethod
|
40
|
+
public static IRubyObject create_jar(ThreadContext context, IRubyObject recv, IRubyObject jar_path, IRubyObject entries) {
|
41
|
+
final Ruby runtime = recv.getRuntime();
|
42
|
+
|
43
|
+
if (!(entries instanceof RubyHash)) {
|
44
|
+
throw runtime.newArgumentError("expected a hash for the second argument");
|
45
|
+
}
|
46
|
+
|
47
|
+
RubyHash hash = (RubyHash) entries;
|
48
|
+
try {
|
49
|
+
FileOutputStream file = newFile(jar_path);
|
50
|
+
try {
|
51
|
+
ZipOutputStream zip = new ZipOutputStream(file);
|
52
|
+
addEntries(context, zip, hash);
|
53
|
+
zip.finish();
|
54
|
+
} finally {
|
55
|
+
close(file);
|
56
|
+
}
|
57
|
+
} catch (IOException e) {
|
58
|
+
if (runtime.isDebug()) {
|
59
|
+
e.printStackTrace();
|
60
|
+
}
|
61
|
+
throw runtime.newIOErrorFromException(e);
|
62
|
+
}
|
63
|
+
|
64
|
+
return runtime.getNil();
|
65
|
+
}
|
66
|
+
|
67
|
+
@JRubyMethod
|
68
|
+
public static IRubyObject entry_in_jar(ThreadContext context, IRubyObject recv, IRubyObject jar_path, IRubyObject entry) {
|
69
|
+
final Ruby runtime = recv.getRuntime();
|
70
|
+
try {
|
71
|
+
InputStream entryStream = getStream(jar_path.convertToString().getUnicodeValue(),
|
72
|
+
entry.convertToString().getUnicodeValue());
|
73
|
+
try {
|
74
|
+
byte[] buf = new byte[16384];
|
75
|
+
ByteList blist = new ByteList();
|
76
|
+
int bytesRead = -1;
|
77
|
+
while ((bytesRead = entryStream.read(buf)) != -1) {
|
78
|
+
blist.append(buf, 0, bytesRead);
|
79
|
+
}
|
80
|
+
IRubyObject stringio = runtime.getModule("StringIO");
|
81
|
+
return stringio.callMethod(context, "new", runtime.newString(blist));
|
82
|
+
} finally {
|
83
|
+
close(entryStream);
|
84
|
+
}
|
85
|
+
} catch (IOException e) {
|
86
|
+
if (runtime.isDebug()) {
|
87
|
+
e.printStackTrace();
|
88
|
+
}
|
89
|
+
throw runtime.newIOErrorFromException(e);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
private static void addEntries(ThreadContext context, ZipOutputStream zip, RubyHash entries) throws IOException {
|
94
|
+
RubyArray keys = entries.keys().sort(context, Block.NULL_BLOCK);
|
95
|
+
for (int i = 0; i < keys.getLength(); i++) {
|
96
|
+
IRubyObject key = keys.entry(i);
|
97
|
+
IRubyObject value = entries.op_aref(context, key);
|
98
|
+
addEntry(context, zip, key.convertToString().getUnicodeValue(), value);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
private static void addEntry(ThreadContext context, ZipOutputStream zip, String entryName, IRubyObject value) throws IOException {
|
103
|
+
if (value.respondsTo("read")) {
|
104
|
+
RubyString str = (RubyString) value.callMethod(context, "read").checkStringType();
|
105
|
+
byte[] contents = str.getByteList().getUnsafeBytes();
|
106
|
+
zip.putNextEntry(new ZipEntry(entryName));
|
107
|
+
zip.write(contents);
|
108
|
+
} else {
|
109
|
+
File f;
|
110
|
+
if (value.isNil() || (f = getFile(value)).isDirectory()) {
|
111
|
+
zip.putNextEntry(new ZipEntry(entryName + "/"));
|
112
|
+
} else {
|
113
|
+
String path = f.getPath();
|
114
|
+
if (!f.exists()) {
|
115
|
+
path = value.convertToString().getUnicodeValue();
|
116
|
+
}
|
117
|
+
|
118
|
+
InputStream inFile = getStream(path, null);
|
119
|
+
try {
|
120
|
+
zip.putNextEntry(new ZipEntry(entryName));
|
121
|
+
byte[] buf = new byte[16384];
|
122
|
+
int bytesRead = -1;
|
123
|
+
while ((bytesRead = inFile.read(buf)) != -1) {
|
124
|
+
zip.write(buf, 0, bytesRead);
|
125
|
+
}
|
126
|
+
} finally {
|
127
|
+
close(inFile);
|
128
|
+
}
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
private static FileOutputStream newFile(IRubyObject jar_path) throws IOException {
|
134
|
+
return new FileOutputStream(getFile(jar_path));
|
135
|
+
}
|
136
|
+
|
137
|
+
private static File getFile(IRubyObject path) {
|
138
|
+
return JRubyFile.create(path.getRuntime().getCurrentDirectory(),
|
139
|
+
path.convertToString().getUnicodeValue());
|
140
|
+
}
|
141
|
+
|
142
|
+
private static void close(Closeable c) {
|
143
|
+
try {
|
144
|
+
c.close();
|
145
|
+
} catch (Exception e) {
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
private static Pattern PROTOCOL = Pattern.compile("^[a-z][a-z0-9]+:");
|
150
|
+
|
151
|
+
private static InputStream getStream(String jar, String entry) throws IOException {
|
152
|
+
Matcher m = PROTOCOL.matcher(jar);
|
153
|
+
while (m.find()) {
|
154
|
+
jar = jar.substring(m.end());
|
155
|
+
m = PROTOCOL.matcher(jar);
|
156
|
+
}
|
157
|
+
|
158
|
+
String[] path = jar.split("!/");
|
159
|
+
InputStream stream = new FileInputStream(path[0]);
|
160
|
+
for (int i = 1; i < path.length; i++) {
|
161
|
+
stream = entryInJar(stream, path[i]);
|
162
|
+
}
|
163
|
+
|
164
|
+
if (entry == null) {
|
165
|
+
return stream;
|
166
|
+
}
|
167
|
+
|
168
|
+
return entryInJar(stream, entry);
|
169
|
+
}
|
170
|
+
|
171
|
+
private static InputStream entryInJar(InputStream jar, String entry) throws IOException {
|
172
|
+
ZipInputStream jstream = new ZipInputStream(jar);
|
173
|
+
ZipEntry zentry = null;
|
174
|
+
while ((zentry = jstream.getNextEntry()) != null) {
|
175
|
+
if (zentry.getName().equals(entry)) {
|
176
|
+
return jstream;
|
177
|
+
}
|
178
|
+
jstream.closeEntry();
|
179
|
+
}
|
180
|
+
throw new FileNotFoundException("entry '" + entry + "' not found in " + jar);
|
181
|
+
}
|
182
|
+
}
|