001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "Ant" and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: */
054:
055: package org.objectweb.jonas.ant;
056:
057: import java.io.File;
058: import java.io.FileInputStream;
059: import java.io.FileNotFoundException;
060: import java.io.IOException;
061: import java.io.InputStream;
062: import java.net.URL;
063: import java.util.Hashtable;
064:
065: import org.apache.tools.ant.Project;
066: import org.apache.tools.ant.Task;
067: import org.xml.sax.Attributes;
068: import org.xml.sax.InputSource;
069: import org.xml.sax.SAXException;
070:
071: /**
072: * Inner class used by EjbJar to facilitate the parsing of deployment
073: * descriptors and the capture of appropriate information. Extends
074: * HandlerBase so it only implements the methods needed. During parsing
075: * creates a hashtable consisting of entries mapping the name it should be
076: * inserted into an EJB jar as to a File representing the file on disk. This
077: * list can then be accessed through the getFiles() method.
078: */
079: public class DescriptorHandler extends
080: org.xml.sax.helpers.DefaultHandler {
081: private static final int STATE_LOOKING_EJBJAR = 1;
082: private static final int STATE_IN_EJBJAR = 2;
083: private static final int STATE_IN_BEANS = 3;
084: private static final int STATE_IN_SESSION = 4;
085: private static final int STATE_IN_ENTITY = 5;
086: private static final int STATE_IN_MESSAGE = 6;
087:
088: private static final int STATE_IN_SERVICE_REF = 7;
089: private static final int STATE_IN_PORT_COMPONENT_REF = 8;
090: private static final int STATE_IN_HANDLER = 9;
091:
092: private Task owningTask;
093:
094: private String publicId = null;
095:
096: /**
097: * Bunch of constants used for storing entries in a hashtable, and for
098: * constructing the filenames of various parts of the ejb jar.
099: */
100: private static final String EJB_REF = "ejb-ref";
101: private static final String EJB_LOCAL_REF = "ejb-local-ref";
102: private static final String HOME_INTERFACE = "home";
103: private static final String REMOTE_INTERFACE = "remote";
104: private static final String LOCAL_HOME_INTERFACE = "local-home";
105: private static final String LOCAL_INTERFACE = "local";
106: private static final String ENDPOINT_INTERFACE = "service-endpoint";
107: private static final String BEAN_CLASS = "ejb-class";
108: private static final String PK_CLASS = "prim-key-class";
109: private static final String EJB_NAME = "ejb-name";
110: private static final String EJB_JAR = "ejb-jar";
111: private static final String ENTERPRISE_BEANS = "enterprise-beans";
112: private static final String ENTITY_BEAN = "entity";
113: private static final String SESSION_BEAN = "session";
114: private static final String MESSAGE_BEAN = "message-driven";
115:
116: /**
117: * Add support for service-ref elements
118: */
119: private static final String SERVICE_REF = "service-ref";
120: private static final String SERVICE_INTERFACE = "service-interface";
121: private static final String WSDL_FILE = "wsdl-file";
122: private static final String JAXRPC_MAPPING_FILE = "jaxrpc-mapping-file";
123: private static final String PORT_COMPONENT_REF = "port-component-ref";
124: private static final String SERVICE_ENDPOINT_INTERFACE = "service-endpoint-interface";
125: private static final String HANDLER = "handler";
126: private static final String HANDLER_CLASS = "handler-class";
127:
128: /**
129: * The state of the parsing
130: */
131: private int parseState = STATE_LOOKING_EJBJAR;
132:
133: /**
134: * The old state of the parsing (used only for service-ref)
135: */
136: private int oldParseState;
137:
138: /**
139: * Instance variable used to store the name of the current element being
140: * processed by the SAX parser. Accessed by the SAX parser call-back methods
141: * startElement() and endElement().
142: */
143: protected String currentElement = null;
144:
145: /**
146: * The text of the current element
147: */
148: protected String currentText = null;
149:
150: /**
151: * Instance variable that stores the names of the files as they will be
152: * put into the jar file, mapped to File objects Accessed by the SAX
153: * parser call-back method characters().
154: */
155: protected Hashtable ejbFiles = null;
156:
157: /**
158: * Instance variable that stores the value found in the <ejb-name> element
159: */
160: protected String ejbName = null;
161:
162: private Hashtable fileDTDs = new Hashtable();
163:
164: private Hashtable resourceDTDs = new Hashtable();
165:
166: private boolean inEJBRef = false;
167:
168: private Hashtable urlDTDs = new Hashtable();
169:
170: /**
171: * The directory containing the bean classes and interfaces. This is
172: * used for performing dependency file lookups.
173: */
174: private File srcDir;
175:
176: public DescriptorHandler(Task task, File srcDir) {
177: this .owningTask = task;
178: this .srcDir = srcDir;
179: }
180:
181: public void registerDTD(String publicId, String location) {
182: if (location == null) {
183: return;
184: }
185:
186: File fileDTD = new File(location);
187: if (!fileDTD.exists()) {
188: // resolve relative to project basedir
189: fileDTD = owningTask.getProject().resolveFile(location);
190: }
191:
192: if (fileDTD.exists()) {
193: if (publicId != null) {
194: fileDTDs.put(publicId, fileDTD);
195: owningTask.log("Mapped publicId " + publicId
196: + " to file " + fileDTD, Project.MSG_VERBOSE);
197: }
198: return;
199: }
200:
201: if (getClass().getResource(location) != null) {
202: if (publicId != null) {
203: resourceDTDs.put(publicId, location);
204: owningTask.log("Mapped publicId " + publicId
205: + " to resource " + location,
206: Project.MSG_VERBOSE);
207: }
208: }
209:
210: try {
211: if (publicId != null) {
212: URL urldtd = new URL(location);
213: urlDTDs.put(publicId, urldtd);
214: }
215: } catch (java.net.MalformedURLException e) {
216: //ignored
217: }
218:
219: }
220:
221: public InputSource resolveEntity(String publicId, String systemId)
222: throws SAXException {
223: this .publicId = publicId;
224:
225: File dtdFile = (File) fileDTDs.get(publicId);
226: if (dtdFile != null) {
227: try {
228: owningTask.log("Resolved " + publicId
229: + " to local file " + dtdFile,
230: Project.MSG_VERBOSE);
231: return new InputSource(new FileInputStream(dtdFile));
232: } catch (FileNotFoundException ex) {
233: // ignore
234: }
235: }
236:
237: String dtdResourceName = (String) resourceDTDs.get(publicId);
238: if (dtdResourceName != null) {
239: InputStream is = this .getClass().getResourceAsStream(
240: dtdResourceName);
241: if (is != null) {
242: owningTask.log("Resolved " + publicId
243: + " to local resource " + dtdResourceName,
244: Project.MSG_VERBOSE);
245: return new InputSource(is);
246: }
247: }
248:
249: URL dtdUrl = (URL) urlDTDs.get(publicId);
250: if (dtdUrl != null) {
251: try {
252: InputStream is = dtdUrl.openStream();
253: owningTask.log("Resolved " + publicId + " to url "
254: + dtdUrl, Project.MSG_VERBOSE);
255: return new InputSource(is);
256: } catch (IOException ioe) {
257: //ignore
258: }
259: }
260:
261: owningTask.log("Could not resolve ( publicId: " + publicId
262: + ", systemId: " + systemId + ") to a local entity",
263: Project.MSG_INFO);
264:
265: return null;
266: }
267:
268: /**
269: * Getter method that returns the set of files to include in the EJB jar.
270: */
271: public Hashtable getFiles() {
272: return (ejbFiles == null) ? new Hashtable() : ejbFiles;
273: }
274:
275: /**
276: * Get the publicId of the DTD
277: */
278: public String getPublicId() {
279: return publicId;
280: }
281:
282: /**
283: * Getter method that returns the value of the <ejb-name> element.
284: */
285: public String getEjbName() {
286: return ejbName;
287: }
288:
289: /**
290: * SAX parser call-back method that is used to initialize the values of some
291: * instance variables to ensure safe operation.
292: */
293: public void startDocument() throws SAXException {
294: this .ejbFiles = new Hashtable(10, 1);
295: this .currentElement = null;
296: inEJBRef = false;
297: }
298:
299: /**
300: * SAX parser call-back method that is invoked when a new element is entered
301: * into. Used to store the context (attribute name) in the currentAttribute
302: * instance variable.
303: * @param uri The namespace of the element being entered.
304: * @param localname The name of the element being entered.
305: * @param qname The qname of the element being entered.
306: * @param attrs Attributes associated to the element.
307: */
308: public void startElement(String uri, String localname,
309: String qname, Attributes attrs) throws SAXException {
310:
311: String name = qname;
312:
313: this .currentElement = name;
314: currentText = "";
315: if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
316: inEJBRef = true;
317: } else if (parseState == STATE_LOOKING_EJBJAR
318: && name.equals(EJB_JAR)) {
319: parseState = STATE_IN_EJBJAR;
320: } else if (parseState == STATE_IN_EJBJAR
321: && name.equals(ENTERPRISE_BEANS)) {
322: parseState = STATE_IN_BEANS;
323: } else if (parseState == STATE_IN_BEANS
324: && name.equals(SESSION_BEAN)) {
325: parseState = STATE_IN_SESSION;
326: } else if (parseState == STATE_IN_BEANS
327: && name.equals(ENTITY_BEAN)) {
328: parseState = STATE_IN_ENTITY;
329: } else if (parseState == STATE_IN_BEANS
330: && name.equals(MESSAGE_BEAN)) {
331: parseState = STATE_IN_MESSAGE;
332: } else if (((parseState == STATE_IN_SESSION)
333: || (parseState == STATE_IN_ENTITY) || (parseState == STATE_IN_MESSAGE))
334: && (name.equals(SERVICE_REF))) {
335: oldParseState = parseState;
336: parseState = STATE_IN_SERVICE_REF;
337: } else if ((parseState == STATE_IN_SERVICE_REF)
338: && (name.equals(PORT_COMPONENT_REF))) {
339: parseState = STATE_IN_PORT_COMPONENT_REF;
340: } else if ((parseState == STATE_IN_SERVICE_REF)
341: && (name.equals(HANDLER))) {
342: parseState = STATE_IN_HANDLER;
343: }
344: }
345:
346: /**
347: * SAX parser call-back method that is invoked when an element is exited.
348: * Used to blank out (set to the empty string, not nullify) the name of
349: * the currentAttribute. A better method would be to use a stack as an
350: * instance variable, however since we are only interested in leaf-node
351: * data this is a simpler and workable solution.
352: * @param name The name of the attribute being exited. Ignored
353: * in this implementation.
354: */
355: public void endElement(String uri, String localname, String qname)
356: throws SAXException {
357: String name = qname;
358:
359: processElement();
360: currentText = "";
361: this .currentElement = "";
362: if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
363: inEJBRef = false;
364: } else if (parseState == STATE_IN_HANDLER
365: && name.equals(HANDLER)) {
366: parseState = STATE_IN_SERVICE_REF;
367: } else if (parseState == STATE_IN_PORT_COMPONENT_REF
368: && name.equals(PORT_COMPONENT_REF)) {
369: parseState = STATE_IN_SERVICE_REF;
370: } else if (parseState == STATE_IN_SERVICE_REF
371: && name.equals(SERVICE_REF)) {
372: parseState = oldParseState;
373: } else if (parseState == STATE_IN_ENTITY
374: && name.equals(ENTITY_BEAN)) {
375: parseState = STATE_IN_BEANS;
376: } else if (parseState == STATE_IN_SESSION
377: && name.equals(SESSION_BEAN)) {
378: parseState = STATE_IN_BEANS;
379: } else if (parseState == STATE_IN_MESSAGE
380: && name.equals(MESSAGE_BEAN)) {
381: parseState = STATE_IN_BEANS;
382: } else if (parseState == STATE_IN_BEANS
383: && name.equals(ENTERPRISE_BEANS)) {
384: parseState = STATE_IN_EJBJAR;
385: } else if (parseState == STATE_IN_EJBJAR
386: && name.equals(EJB_JAR)) {
387: parseState = STATE_LOOKING_EJBJAR;
388: }
389: }
390:
391: /**
392: * SAX parser call-back method invoked whenever characters are located within
393: * an element. currentAttribute (modified by startElement and endElement)
394: * tells us whether we are in an interesting element (one of the up to four
395: * classes of an EJB). If so then converts the classname from the format
396: * org.apache.tools.ant.Parser to the convention for storing such a class,
397: * org/apache/tools/ant/Parser.class. This is then resolved into a file
398: * object under the srcdir which is stored in a Hashtable.
399: * @param ch A character array containing all the characters in
400: * the element, and maybe others that should be ignored.
401: * @param start An integer marking the position in the char
402: * array to start reading from.
403: * @param length An integer representing an offset into the
404: * char array where the current data terminates.
405: */
406: public void characters(char[] ch, int start, int length)
407: throws SAXException {
408:
409: currentText += new String(ch, start, length);
410: }
411:
412: protected void processElement() {
413: if (inEJBRef
414: || (parseState != STATE_IN_ENTITY
415: && parseState != STATE_IN_SESSION
416: && parseState != STATE_IN_MESSAGE
417: && parseState != STATE_IN_SERVICE_REF
418: && parseState != STATE_IN_PORT_COMPONENT_REF && parseState != STATE_IN_HANDLER)) {
419: return;
420: }
421:
422: if (currentElement.equals(HOME_INTERFACE)
423: || currentElement.equals(REMOTE_INTERFACE)
424: || currentElement.equals(LOCAL_INTERFACE)
425: || currentElement.equals(ENDPOINT_INTERFACE)
426: || currentElement.equals(LOCAL_HOME_INTERFACE)
427: || currentElement.equals(BEAN_CLASS)
428: || currentElement.equals(PK_CLASS)
429: || currentElement.equals(SERVICE_INTERFACE)
430: || currentElement.equals(SERVICE_ENDPOINT_INTERFACE)
431: || currentElement.equals(HANDLER_CLASS)) {
432:
433: // Get the filename into a String object
434: File classFile = null;
435: String className = currentText.trim();
436:
437: // If it's a primitive wrapper then we shouldn't try and put
438: // it into the jar, so ignore it.
439: if (!className.startsWith("java.")
440: && !className.startsWith("javax.")) {
441: // Translate periods into path separators, add .class to the
442: // name, create the File object and add it to the Hashtable.
443: className = className.replace('.', File.separatorChar);
444: className += ".class";
445: classFile = new File(srcDir, className);
446: ejbFiles.put(className, classFile);
447: }
448: }
449:
450: // service-ref support for simple file
451: if (currentElement.equals(WSDL_FILE)
452: || currentElement.equals(JAXRPC_MAPPING_FILE)) {
453: //owningTask.log("***** element name : " + currentElement, Project.MSG_VERBOSE);
454: String filename = currentText.trim();
455: File file = new File(srcDir, filename);
456: ejbFiles.put(filename, file);
457: }
458:
459: // Get the value of the <ejb-name> tag. Only the first occurrence.
460: if (currentElement.equals(EJB_NAME)) {
461: if (ejbName == null) {
462: ejbName = currentText.trim();
463: }
464: }
465: }
466: }
|