Extended the WAR maker to be able to add code files.
[jsvc.git] / src / dolda / jsvc / j2ee / Archive.java
1 package dolda.jsvc.j2ee;
2
3 import java.util.*;
4 import java.io.*;
5 import java.net.*;
6 import java.util.zip.*;
7 import java.util.jar.*;
8
9 public class Archive {
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);
32     }
33     
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
42     public void jarprops(String[] jars, String propres) throws IOException {
43         URL[] urls = new URL[jars.length];
44         try {
45             for(int i = 0; i < jars.length; i++)
46                 urls[i] = new URL("file", "", jars[i]);
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
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)
84                 throw(new RuntimeException("Missing required property " + pn));
85             ln = ln.substring(0, p) + pv + ln.substring(p2 + 1);
86             p = p + pv.length();
87         }
88         return(ln);
89     }
90
91     private void writewebxml() throws IOException {
92         zip().putNextEntry(new ZipEntry("WEB-INF/web.xml"));
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"));
97             BufferedWriter w = new BufferedWriter(new OutputStreamWriter(zip(), cs));
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     
109     public void addcode(String name, InputStream in) throws IOException {
110         zip().putNextEntry(new ZipEntry("WEB-INF/classes/" + name));
111         cpstream(in, zip());
112     }
113
114     private static String basename(String fn) {
115         int p = fn.lastIndexOf('/');
116         if(p >= 0)
117             return(fn.substring(p + 1));
118         return(fn);
119     }
120
121     public void addjars(String[] jars) throws IOException {
122         jarprops(jars, "/jsvc.properties");
123         ZipOutputStream zip = zip();
124         for(String jar : jars) {
125             zip.putNextEntry(new ZipEntry("WEB-INF/lib/" + basename(jar)));
126             InputStream jarin = new FileInputStream(jar);
127             try {
128                 cpstream(jarin, zip);
129             } finally {
130                 jarin.close();
131             }
132         }
133     }
134
135     public void finish() throws IOException {
136         zip().finish();
137     }
138
139     private static void usage(PrintStream out) {
140         out.println("usage: dolda.jsvc.j2ee.Archive [-h] [-p PROPFILE] [-n DISPLAY-NAME] [(-c CODE-FILE)...] WAR-FILE JAR-FILE...");
141     }
142     
143     public static void main(String[] args) throws IOException {
144         PosixArgs opt = PosixArgs.getopt(args, "hp:n:c:");
145         if(opt == null) {
146             usage(System.err);
147             System.exit(1);
148         }
149         if(opt.rest.length < 2) {
150             usage(System.err);
151             System.exit(1);
152         }
153         String war = opt.rest[0];
154         String[] jars = Arrays.copyOfRange(opt.rest, 1, opt.rest.length);
155         
156         OutputStream out = new FileOutputStream(war);
157         try {
158             Archive ar = new Archive(out);
159         
160             for(char c : opt.parsed()) {
161                 switch(c) {
162                 case 'p':
163                     {
164                         InputStream in = new FileInputStream(opt.arg);
165                         try {
166                             ar.loadprops(in);
167                         } finally {
168                             in.close();
169                         }
170                     }
171                     break;
172                 case 'n':
173                     ar.putprop("jsvc.j2ee.appname", opt.arg);
174                     break;
175                 case 'c':
176                     {
177                         InputStream in = new FileInputStream(opt.arg);
178                         try {
179                             ar.addcode(basename(opt.arg), in);
180                         } finally {
181                             in.close();
182                         }
183                     }
184                     break;
185                 case 'h':
186                     usage(System.out);
187                     return;
188                 }
189             }
190             
191             ar.addjars(jars);
192             ar.writewebxml();
193             
194             ar.finish();
195         } finally {
196             out.close();
197         }
198     }
199 }