001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.jk.config;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.io.StringReader;
023: import java.util.Hashtable;
024: import java.util.Vector;
025:
026: import javax.xml.parsers.DocumentBuilder;
027: import javax.xml.parsers.DocumentBuilderFactory;
028: import javax.xml.parsers.ParserConfigurationException;
029:
030: import org.apache.tomcat.util.IntrospectionUtils;
031: import org.w3c.dom.Document;
032: import org.w3c.dom.Node;
033: import org.xml.sax.EntityResolver;
034: import org.xml.sax.InputSource;
035: import org.xml.sax.SAXException;
036:
037: /* Naming conventions:
038:
039: JK_CONF_DIR == serverRoot/work ( XXX /jkConfig ? )
040:
041: - Each vhost has a sub-dir named after the canonycal name
042:
043: - For each webapp in a vhost, there is a separate WEBAPP_NAME.jkmap
044:
045: - In httpd.conf ( or equivalent servers ), in each virtual host you
046: should "Include JK_CONF_DIR/VHOST/jk_apache.conf". The config
047: file will contain the Alias declarations and other rules required
048: for apache operation. Same for other servers.
049:
050: - WebXml2Jk will be invoked by a config tool or automatically for each
051: webapp - it'll generate the WEBAPP.jkmap files and config fragments.
052:
053: WebXml2Jk will _not_ generate anything else but mappings.
054: It should _not_ try to guess locations or anything else - that's
055: another components' job.
056:
057: */
058:
059: /**
060: * Read a web.xml file and generate the mappings for jk2.
061: * It can be used from the command line or ant.
062: *
063: * In order for the web server to serve static pages, all webapps
064: * must be deployed on the computer that runs Apache, IIS, etc.
065: *
066: * Dynamic pages can be executed on that computer or other servers
067: * in a pool, but even if the main server doesn't run tomcat,
068: * it must have all the static files and WEB-INF/web.xml.
069: * ( you could have a script remove everything else, including jsps - if
070: * security paranoia is present ).
071: *
072: * XXX We could have this in WEB-INF/urimap.properties.
073: *
074: * @author Costin Manolache
075: */
076: public class WebXml2Jk {
077: String vhost = "";
078: String cpath = "";
079: String docBase;
080: String file;
081: String worker = "lb";
082:
083: // -------------------- Settings --------------------
084:
085: // XXX We can also generate location-independent mappings.
086:
087: /** Set the canonycal name of the virtual host.
088: */
089: public void setHost(String vhost) {
090: this .vhost = vhost;
091: }
092:
093: /** Set the canonical name of the virtual host.
094: */
095: public void setContext(String contextPath) {
096: this .cpath = contextPath;
097: }
098:
099: /** Set the base directory where the application is
100: * deployed ( on the web server ).
101: */
102: public void setDocBase(String docBase) {
103: this .docBase = docBase;
104: }
105:
106: // Automatically generated.
107: // /** The file where the jk2 mapping will be generated
108: // */
109: // public void setJk2Conf( String outFile ) {
110: // file=outFile;
111: // type=CONFIG_JK2_URIMAP;
112: // }
113:
114: // /** Backward compat: generate JkMounts for mod_jk1
115: // */
116: // public void setJkmountFile( String outFile ) {
117: // file=outFile;
118: // type=CONFIG_JK_MOUNT;
119: // }
120:
121: /* By default we map to the lb - in jk2 this is automatically
122: * created and includes all tomcat instances.
123: *
124: * This is equivalent to the worker in jk1.
125: */
126: public void setGroup(String route) {
127: worker = route;
128: }
129:
130: // -------------------- Generators --------------------
131: public static interface MappingGenerator {
132: void setWebXmlReader(WebXml2Jk wxml);
133:
134: /** Start section( vhost declarations, etc )
135: */
136: void generateStart() throws IOException;
137:
138: void generateEnd() throws IOException;
139:
140: void generateServletMapping(String servlet, String url)
141: throws IOException;
142:
143: void generateFilterMapping(String servlet, String url)
144: throws IOException;
145:
146: void generateLoginConfig(String loginPage, String errPage,
147: String authM) throws IOException;
148:
149: void generateErrorPage(int err, String location)
150: throws IOException;
151:
152: void generateConstraints(Vector urls, Vector methods,
153: Vector roles, boolean isSSL) throws IOException;
154: }
155:
156: // -------------------- Implementation --------------------
157: Node webN;
158: File jkDir;
159:
160: /** Return the top level node
161: */
162: public Node getWebXmlNode() {
163: return webN;
164: }
165:
166: public File getJkDir() {
167: return jkDir;
168: }
169:
170: /** Extract the wellcome files from the web.xml
171: */
172: public Vector getWellcomeFiles() {
173: Node n0 = getChild(webN, "welcome-file-list");
174: Vector wF = new Vector();
175: if (n0 != null) {
176: for (Node mapN = getChild(webN, "welcome-file"); mapN != null; mapN = getNext(mapN)) {
177: wF.addElement(getContent(mapN));
178: }
179: }
180: // XXX Add index.html, index.jsp
181: return wF;
182: }
183:
184: void generate(MappingGenerator gen) throws IOException {
185: gen.generateStart();
186: log.info("Generating mappings for servlets ");
187: for (Node mapN = getChild(webN, "servlet-mapping"); mapN != null; mapN = getNext(mapN)) {
188:
189: String serv = getChildContent(mapN, "servlet-name");
190: String url = getChildContent(mapN, "url-pattern");
191:
192: gen.generateServletMapping(serv, url);
193: }
194:
195: log.info("Generating mappings for filters ");
196: for (Node mapN = getChild(webN, "filter-mapping"); mapN != null; mapN = getNext(mapN)) {
197:
198: String filter = getChildContent(mapN, "filter-name");
199: String url = getChildContent(mapN, "url-pattern");
200:
201: gen.generateFilterMapping(filter, url);
202: }
203:
204: for (Node mapN = getChild(webN, "error-page"); mapN != null; mapN = getNext(mapN)) {
205: String errorCode = getChildContent(mapN, "error-code");
206: String location = getChildContent(mapN, "location");
207:
208: if (errorCode != null && !"".equals(errorCode)) {
209: try {
210: int err = new Integer(errorCode).intValue();
211: gen.generateErrorPage(err, location);
212: } catch (Exception ex) {
213: log.error("Format error " + location, ex);
214: }
215: }
216: }
217:
218: Node lcN = getChild(webN, "login-config");
219: if (lcN != null) {
220: log.info("Generating mapping for login-config ");
221:
222: String authMeth = getContent(getChild(lcN, "auth-method"));
223: if (authMeth == null)
224: authMeth = "BASIC";
225:
226: Node n1 = getChild(lcN, "form-login-config");
227: String loginPage = getChildContent(n1, "form-login-page");
228: String errPage = getChildContent(n1, "form-error-page");
229:
230: if (loginPage != null) {
231: int lpos = loginPage.lastIndexOf("/");
232: String jscurl = loginPage.substring(0, lpos + 1)
233: + "j_security_check";
234: gen.generateLoginConfig(jscurl, errPage, authMeth);
235: }
236: }
237:
238: log.info("Generating mappings for security constraints ");
239: for (Node mapN = getChild(webN, "security-constraint"); mapN != null; mapN = getNext(mapN)) {
240:
241: Vector methods = new Vector();
242: Vector urls = new Vector();
243: Vector roles = new Vector();
244: boolean isSSL = false;
245:
246: Node wrcN = getChild(mapN, "web-resource-collection");
247: for (Node uN = getChild(wrcN, "http-method"); uN != null; uN = getNext(uN)) {
248: methods.addElement(getContent(uN));
249: }
250: for (Node uN = getChild(wrcN, "url-pattern"); uN != null; uN = getNext(uN)) {
251: urls.addElement(getContent(uN));
252: }
253:
254: // Not used at the moment
255: Node acN = getChild(mapN, "auth-constraint");
256: for (Node rN = getChild(acN, "role-name"); rN != null; rN = getNext(rN)) {
257: roles.addElement(getContent(rN));
258: }
259:
260: Node ucN = getChild(mapN, "user-data-constraint");
261: String transp = getContent(getChild(ucN,
262: "transport-guarantee"));
263: if (transp != null) {
264: if ("INTEGRAL".equalsIgnoreCase(transp)
265: || "CONFIDENTIAL".equalsIgnoreCase(transp)) {
266: isSSL = true;
267: }
268: }
269:
270: gen.generateConstraints(urls, methods, roles, isSSL);
271: }
272: gen.generateEnd();
273: }
274:
275: // -------------------- Main and ant wrapper --------------------
276:
277: public void execute() {
278: try {
279: if (docBase == null) {
280: log
281: .error("No docbase - please specify the base directory of you web application ( -docBase PATH )");
282: return;
283: }
284: if (cpath == null) {
285: log
286: .error("No context - please specify the mount ( -context PATH )");
287: return;
288: }
289: File docbF = new File(docBase);
290: File wXmlF = new File(docBase, "WEB-INF/web.xml");
291:
292: Document wXmlN = readXml(wXmlF);
293: if (wXmlN == null)
294: return;
295:
296: webN = wXmlN.getDocumentElement();
297: if (webN == null) {
298: log.error("Can't find web-app");
299: return;
300: }
301:
302: jkDir = new File(docbF, "WEB-INF/jk2");
303: jkDir.mkdirs();
304:
305: MappingGenerator generator = new GeneratorJk2();
306: generator.setWebXmlReader(this );
307: generate(generator);
308:
309: generator = new GeneratorJk1();
310: generator.setWebXmlReader(this );
311: generate(generator);
312:
313: generator = new GeneratorApache2();
314: generator.setWebXmlReader(this );
315: generate(generator);
316:
317: } catch (Exception ex) {
318: ex.printStackTrace();
319: }
320: }
321:
322: public static void main(String args[]) {
323: try {
324: if (args.length == 1
325: && ("-?".equals(args[0]) || "-h".equals(args[0]))) {
326: System.out.println("Usage: ");
327: System.out.println(" WebXml2Jk [OPTIONS]");
328: System.out.println();
329: System.out
330: .println(" -docBase DIR The location of the webapp. Required");
331: System.out
332: .println(" -group GROUP Group, if you have multiple tomcats with diffrent content. ");
333: System.out
334: .println(" The default is 'lb', and should be used in most cases");
335: System.out
336: .println(" -host HOSTNAME Canonical hostname - for virtual hosts");
337: System.out
338: .println(" -context /CPATH Context path where the app will be mounted");
339: return;
340: }
341:
342: WebXml2Jk w2jk = new WebXml2Jk();
343:
344: /* do ant-style property setting */
345: IntrospectionUtils.processArgs(w2jk, args, new String[] {},
346: null, new Hashtable());
347: w2jk.execute();
348: } catch (Exception ex) {
349: ex.printStackTrace();
350: }
351:
352: }
353:
354: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
355: .getLog(WebXml2Jk.class);
356:
357: // -------------------- DOM utils --------------------
358:
359: /** Get the content of a node
360: */
361: public static String getContent(Node n) {
362: if (n == null)
363: return null;
364: Node n1 = n.getFirstChild();
365: // XXX Check if it's a text node
366:
367: String s1 = n1.getNodeValue();
368: return s1.trim();
369: }
370:
371: /** Get the first child
372: */
373: public static Node getChild(Node parent, String name) {
374: if (parent == null)
375: return null;
376: Node first = parent.getFirstChild();
377: if (first == null)
378: return null;
379: for (Node node = first; node != null; node = node
380: .getNextSibling()) {
381: //System.out.println("getNode: " + name + " " + node.getNodeName());
382: if (name.equals(node.getNodeName())) {
383: return node;
384: }
385: }
386: return null;
387: }
388:
389: /** Get the first child's content ( i.e. it's included TEXT node )
390: */
391: public static String getChildContent(Node parent, String name) {
392: Node first = parent.getFirstChild();
393: if (first == null)
394: return null;
395: for (Node node = first; node != null; node = node
396: .getNextSibling()) {
397: //System.out.println("getNode: " + name + " " + node.getNodeName());
398: if (name.equals(node.getNodeName())) {
399: return getContent(node);
400: }
401: }
402: return null;
403: }
404:
405: /** Get the node in the list of siblings
406: */
407: public static Node getNext(Node current) {
408: Node first = current.getNextSibling();
409: String name = current.getNodeName();
410: if (first == null)
411: return null;
412: for (Node node = first; node != null; node = node
413: .getNextSibling()) {
414: //System.out.println("getNode: " + name + " " + node.getNodeName());
415: if (name.equals(node.getNodeName())) {
416: return node;
417: }
418: }
419: return null;
420: }
421:
422: public static class NullResolver implements EntityResolver {
423: public InputSource resolveEntity(String publicId,
424: String systemId) throws SAXException, IOException {
425: if (log.isDebugEnabled())
426: log
427: .debug("ResolveEntity: " + publicId + " "
428: + systemId);
429: return new InputSource(new StringReader(""));
430: }
431: }
432:
433: public static Document readXml(File xmlF) throws SAXException,
434: IOException, ParserConfigurationException {
435: if (!xmlF.exists()) {
436: log.error("No xml file " + xmlF);
437: return null;
438: }
439: DocumentBuilderFactory dbf = DocumentBuilderFactory
440: .newInstance();
441:
442: dbf.setValidating(false);
443: dbf.setIgnoringComments(false);
444: dbf.setIgnoringElementContentWhitespace(true);
445: //dbf.setCoalescing(true);
446: //dbf.setExpandEntityReferences(true);
447:
448: DocumentBuilder db = null;
449: db = dbf.newDocumentBuilder();
450: db.setEntityResolver(new NullResolver());
451:
452: // db.setErrorHandler( new MyErrorHandler());
453:
454: Document doc = db.parse(xmlF);
455: return doc;
456: }
457:
458: }
|