Wrote an ant task to create jsvc war files.
[jsvc.git] / src / dolda / jsvc / j2ee / Archive.java
CommitLineData
f179d8ba
FT
1package dolda.jsvc.j2ee;
2
3import java.util.*;
4import java.io.*;
5import java.net.*;
6import java.util.zip.*;
7import java.util.jar.*;
8
9public class Archive {
5f3753cb
FT
10 private Properties props = defprops();
11 private JarOutputStream zipout = null;
12 private final OutputStream realout;
13
14 public Archive(OutputStream out) {
15 this.realout = out;
16 }
17
18 private void initzip() throws IOException {
19 Manifest man = new Manifest();
20 man.getMainAttributes().put(new Attributes.Name("Manifest-Version"), "1.0");
21 man.getMainAttributes().put(new Attributes.Name("Created-By"), "jsvc");
22 JarOutputStream zip = new JarOutputStream(realout, man);
23 zip.putNextEntry(new ZipEntry("WEB-INF/"));
24 zip.putNextEntry(new ZipEntry("WEB-INF/lib/"));
25 this.zipout = zip;
26 }
27
28 private ZipOutputStream zip() throws IOException {
29 if(zipout == null)
30 initzip();
31 return(this.zipout);
f179d8ba
FT
32 }
33
5f3753cb
FT
34 public void putprop(String key, String val) {
35 props.put(key, val);
36 }
37
38 public void loadprops(InputStream in) throws IOException {
39 props.load(in);
40 }
41
17204d14 42 public void jarprops(File[] jars, String propres) throws IOException {
f179d8ba
FT
43 URL[] urls = new URL[jars.length];
44 try {
45 for(int i = 0; i < jars.length; i++)
17204d14 46 urls[i] = new URL("file", "", jars[i].toString());
f179d8ba
FT
47 } catch(MalformedURLException e) {
48 throw(new Error(e));
49 }
50 ClassLoader cl = new URLClassLoader(urls);
51 InputStream in = cl.getResourceAsStream(propres);
52 if(in != null) {
53 try {
54 props.load(in);
55 } finally {
56 in.close();
57 }
58 }
59 }
60
61 private static Properties defprops() {
62 Properties props = new Properties();
63 props.put("jsvc.j2ee.webxml.coding", "UTF-8");
64 return(props);
65 }
66
67 private static void cpstream(InputStream in, OutputStream out) throws IOException {
68 byte[] buf = new byte[4096];
69 while(true) {
70 int ret = in.read(buf, 0, buf.length);
71 if(ret < 0)
72 return;
73 out.write(buf, 0, ret);
74 }
75 }
76
17204d14
FT
77 private static class MissingPropException extends RuntimeException {
78 public final String prop;
79
80 private MissingPropException(String prop) {
81 super("Missing required property " + prop);
82 this.prop = prop;
83 }
84 }
85
f179d8ba
FT
86 private static String subst(String ln, Properties props) {
87 int p = 0;
88 while((p = ln.indexOf("${", p)) >= 0) {
89 int p2 = ln.indexOf('}', p + 2);
90 String pn = ln.substring(p + 2, p2);
91 String pv = (String)props.get(pn);
92 if(pv == null)
17204d14 93 throw(new MissingPropException(pn));
f179d8ba
FT
94 ln = ln.substring(0, p) + pv + ln.substring(p2 + 1);
95 p = p + pv.length();
96 }
97 return(ln);
98 }
99
5f3753cb
FT
100 private void writewebxml() throws IOException {
101 zip().putNextEntry(new ZipEntry("WEB-INF/web.xml"));
f179d8ba
FT
102 InputStream tmpl = Archive.class.getResourceAsStream("web.xml.template");
103 String cs = (String)props.get("jsvc.j2ee.webxml.coding");
104 try {
105 BufferedReader r = new BufferedReader(new InputStreamReader(tmpl, "US-ASCII"));
5f3753cb 106 BufferedWriter w = new BufferedWriter(new OutputStreamWriter(zip(), cs));
f179d8ba
FT
107 String ln;
108 while((ln = r.readLine()) != null) {
109 w.write(subst(ln, props));
110 w.write('\n');
111 }
112 w.flush();
113 } finally {
114 tmpl.close();
115 }
116 }
117
5f3753cb
FT
118 public void addcode(String name, InputStream in) throws IOException {
119 zip().putNextEntry(new ZipEntry("WEB-INF/classes/" + name));
120 cpstream(in, zip());
121 }
122
17204d14 123 public void addjars(File[] jars) throws IOException {
104fa785 124 jarprops(jars, "jsvc.properties");
5f3753cb 125 ZipOutputStream zip = zip();
17204d14
FT
126 for(File jar : jars) {
127 zip.putNextEntry(new ZipEntry("WEB-INF/lib/" + jar.getName()));
f179d8ba
FT
128 InputStream jarin = new FileInputStream(jar);
129 try {
130 cpstream(jarin, zip);
131 } finally {
132 jarin.close();
133 }
134 }
f179d8ba
FT
135 }
136
5f3753cb
FT
137 public void finish() throws IOException {
138 zip().finish();
139 }
17204d14
FT
140
141 public static class AntTask extends org.apache.tools.ant.Task {
142 private org.apache.tools.ant.types.FileSet jars, code;
143 private File props, outfile;
144 private String appname;
145
146 private static File[] getfiles(org.apache.tools.ant.types.FileSet fs) {
147 org.apache.tools.ant.DirectoryScanner ds = fs.getDirectoryScanner();
148 ds.scan();
149 String[] nms = ds.getIncludedFiles();
150 File[] ret = new File[nms.length];
151 for(int i = 0; i < nms.length; i++)
152 ret[i] = new File(ds.getBasedir(), nms[i]);
153 return(ret);
154 }
155
156 private void rebuild(File[] jars, File[] code) throws IOException {
157 OutputStream out = new FileOutputStream(outfile);
158
159 System.out.println("Building " + outfile);
160
161 try {
162 Archive ar = new Archive(out);
163 if(appname != null)
164 ar.putprop("jsvc.j2ee.appname", appname);
165 if(props != null) {
166 InputStream in = new FileInputStream(props);
167 try {
168 ar.loadprops(in);
169 } finally {
170 in.close();
171 }
172 }
173
174 for(File f : code) {
175 InputStream in = new FileInputStream(f);
176 try {
177 ar.addcode(f.getName(), in);
178 } finally {
179 in.close();
180 }
181 }
182
183 ar.addjars(jars);
184 ar.writewebxml();
185
186 ar.finish();
187 } catch(MissingPropException e) {
188 throw(new org.apache.tools.ant.BuildException(e.getMessage(), e));
189 } finally {
190 out.close();
191 }
192 }
193
194 public void execute() {
195 File[] jars = (this.jars != null)?getfiles(this.jars):new File[0];
196 File[] code = (this.code != null)?getfiles(this.code):new File[0];
197 if(jars.length < 1)
198 throw(new org.apache.tools.ant.BuildException("Must have at least one JAR file", getLocation()));
199 if(outfile == null)
200 throw(new org.apache.tools.ant.BuildException("No output file specified", getLocation()));
201
202 Collection<File> deps = new LinkedList<File>();
203 deps.addAll(Arrays.asList(jars));
204 deps.addAll(Arrays.asList(code));
205 if(props != null)
206 deps.add(props);
207
208 boolean rebuild = false;
209 for(File dep : deps) {
210 if(!dep.exists())
211 throw(new org.apache.tools.ant.BuildException(dep + " does not exist", getLocation()));
212 if(dep.lastModified() > outfile.lastModified()) {
213 rebuild = true;
214 break;
215 }
216 }
217
218 if(rebuild) {
219 try {
220 rebuild(jars, code);
221 } catch(IOException e) {
222 throw(new org.apache.tools.ant.BuildException(e));
223 }
224 }
225 }
226
227 public void addJars(org.apache.tools.ant.types.FileSet path) {
228 this.jars = path;
229 }
230
231 public void setJars(org.apache.tools.ant.types.FileSet path) {
232 this.jars = path;
233 }
234
235 public void setCode(org.apache.tools.ant.types.FileSet path) {
236 this.code = path;
237 }
238
239 public void setPropfile(File propfile) {
240 this.props = propfile;
241 }
242
243 public void setDestfile(File outfile) {
244 this.outfile = outfile;
245 }
246
247 public void setAppname(String name) {
248 this.appname = name;
249 }
250 }
5f3753cb
FT
251
252 private static void usage(PrintStream out) {
253 out.println("usage: dolda.jsvc.j2ee.Archive [-h] [-p PROPFILE] [-n DISPLAY-NAME] [(-c CODE-FILE)...] WAR-FILE JAR-FILE...");
254 }
255
f179d8ba 256 public static void main(String[] args) throws IOException {
5f3753cb 257 PosixArgs opt = PosixArgs.getopt(args, "hp:n:c:");
f179d8ba
FT
258 if(opt == null) {
259 usage(System.err);
260 System.exit(1);
261 }
262 if(opt.rest.length < 2) {
263 usage(System.err);
264 System.exit(1);
265 }
266 String war = opt.rest[0];
17204d14
FT
267 File[] jars = new File[opt.rest.length - 1];
268 for(int i = 1; i < opt.rest.length; i++)
269 jars[i - 1] = new File(opt.rest[i]);
f179d8ba 270
5f3753cb
FT
271 OutputStream out = new FileOutputStream(war);
272 try {
273 Archive ar = new Archive(out);
f179d8ba 274
5f3753cb
FT
275 for(char c : opt.parsed()) {
276 switch(c) {
277 case 'p':
278 {
279 InputStream in = new FileInputStream(opt.arg);
280 try {
281 ar.loadprops(in);
282 } finally {
283 in.close();
284 }
285 }
286 break;
287 case 'n':
288 ar.putprop("jsvc.j2ee.appname", opt.arg);
289 break;
290 case 'c':
291 {
17204d14
FT
292 File f = new File(opt.arg);
293 InputStream in = new FileInputStream(f);
5f3753cb 294 try {
17204d14 295 ar.addcode(f.getName(), in);
5f3753cb
FT
296 } finally {
297 in.close();
298 }
f179d8ba 299 }
5f3753cb
FT
300 break;
301 case 'h':
302 usage(System.out);
303 return;
f179d8ba 304 }
f179d8ba 305 }
5f3753cb
FT
306
307 ar.addjars(jars);
308 ar.writewebxml();
309
310 ar.finish();
f179d8ba
FT
311 } finally {
312 out.close();
313 }
314 }
315}