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