| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.tools.ant.taskdefs.optional.ejb; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.URL; |
| import java.util.Hashtable; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.Task; |
| import org.xml.sax.AttributeList; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * Inner class used by EjbJar to facilitate the parsing of deployment |
| * descriptors and the capture of appropriate information. Extends |
| * HandlerBase so it only implements the methods needed. During parsing |
| * creates a hashtable consisting of entries mapping the name it should be |
| * inserted into an EJB jar as to a File representing the file on disk. This |
| * list can then be accessed through the getFiles() method. |
| */ |
| public class DescriptorHandler extends org.xml.sax.HandlerBase { |
| private static final int DEFAULT_HASH_TABLE_SIZE = 10; |
| private static final int STATE_LOOKING_EJBJAR = 1; |
| private static final int STATE_IN_EJBJAR = 2; |
| private static final int STATE_IN_BEANS = 3; |
| private static final int STATE_IN_SESSION = 4; |
| private static final int STATE_IN_ENTITY = 5; |
| private static final int STATE_IN_MESSAGE = 6; |
| |
| private Task owningTask; |
| |
| private String publicId = null; |
| |
| /** |
| * Bunch of constants used for storing entries in a hashtable, and for |
| * constructing the filenames of various parts of the ejb jar. |
| */ |
| private static final String EJB_REF = "ejb-ref"; |
| private static final String EJB_LOCAL_REF = "ejb-local-ref"; |
| private static final String HOME_INTERFACE = "home"; |
| private static final String REMOTE_INTERFACE = "remote"; |
| private static final String LOCAL_HOME_INTERFACE = "local-home"; |
| private static final String LOCAL_INTERFACE = "local"; |
| private static final String BEAN_CLASS = "ejb-class"; |
| private static final String PK_CLASS = "prim-key-class"; |
| private static final String EJB_NAME = "ejb-name"; |
| private static final String EJB_JAR = "ejb-jar"; |
| private static final String ENTERPRISE_BEANS = "enterprise-beans"; |
| private static final String ENTITY_BEAN = "entity"; |
| private static final String SESSION_BEAN = "session"; |
| private static final String MESSAGE_BEAN = "message-driven"; |
| |
| /** |
| * The state of the parsing |
| */ |
| private int parseState = STATE_LOOKING_EJBJAR; |
| |
| // CheckStyle:VisibilityModifier OFF - bc |
| /** |
| * Instance variable used to store the name of the current element being |
| * processed by the SAX parser. Accessed by the SAX parser call-back methods |
| * startElement() and endElement(). |
| */ |
| protected String currentElement = null; |
| |
| /** |
| * The text of the current element |
| */ |
| protected String currentText = null; |
| |
| /** |
| * Instance variable that stores the names of the files as they will be |
| * put into the jar file, mapped to File objects Accessed by the SAX |
| * parser call-back method characters(). |
| */ |
| protected Hashtable ejbFiles = null; |
| |
| /** |
| * Instance variable that stores the value found in the <ejb-name> element |
| */ |
| protected String ejbName = null; |
| |
| private Hashtable fileDTDs = new Hashtable(); |
| |
| private Hashtable resourceDTDs = new Hashtable(); |
| |
| private boolean inEJBRef = false; |
| |
| private Hashtable urlDTDs = new Hashtable(); |
| // CheckStyle:VisibilityModifier OFF - bc |
| |
| /** |
| * The directory containing the bean classes and interfaces. This is |
| * used for performing dependency file lookups. |
| */ |
| private File srcDir; |
| |
| /** |
| * Constructor for DescriptorHandler. |
| * @param task the task that owns this desciptor |
| * @param srcDir the source directory |
| */ |
| public DescriptorHandler(Task task, File srcDir) { |
| this.owningTask = task; |
| this.srcDir = srcDir; |
| } |
| |
| /** |
| * Register a dtd with a location. |
| * The location is one of a filename, a resource name in the classpath, or |
| * a URL. |
| * @param publicId the public identity of the dtd |
| * @param location the location of the dtd |
| */ |
| public void registerDTD(String publicId, String location) { |
| if (location == null) { |
| return; |
| } |
| |
| File fileDTD = new File(location); |
| if (!fileDTD.exists()) { |
| // resolve relative to project basedir |
| fileDTD = owningTask.getProject().resolveFile(location); |
| } |
| |
| if (fileDTD.exists()) { |
| if (publicId != null) { |
| fileDTDs.put(publicId, fileDTD); |
| owningTask.log("Mapped publicId " + publicId + " to file " |
| + fileDTD, Project.MSG_VERBOSE); |
| } |
| return; |
| } |
| |
| if (getClass().getResource(location) != null) { |
| if (publicId != null) { |
| resourceDTDs.put(publicId, location); |
| owningTask.log("Mapped publicId " + publicId + " to resource " |
| + location, Project.MSG_VERBOSE); |
| } |
| } |
| |
| try { |
| if (publicId != null) { |
| URL urldtd = new URL(location); |
| urlDTDs.put(publicId, urldtd); |
| } |
| } catch (java.net.MalformedURLException e) { |
| //ignored |
| } |
| |
| } |
| |
| /** |
| * Resolve the entity. |
| * @see org.xml.sax.EntityResolver#resolveEntity(String, String). |
| * @param publicId The public identifier, or <code>null</code> |
| * if none is available. |
| * @param systemId The system identifier provided in the XML |
| * document. Will not be <code>null</code>. |
| * @return an inputsource for this identifier |
| * @throws SAXException if there is a problem. |
| */ |
| public InputSource resolveEntity(String publicId, String systemId) |
| throws SAXException { |
| this.publicId = publicId; |
| |
| File dtdFile = (File) fileDTDs.get(publicId); |
| if (dtdFile != null) { |
| try { |
| owningTask.log("Resolved " + publicId + " to local file " |
| + dtdFile, Project.MSG_VERBOSE); |
| return new InputSource(new FileInputStream(dtdFile)); |
| } catch (FileNotFoundException ex) { |
| // ignore |
| } |
| } |
| |
| String dtdResourceName = (String) resourceDTDs.get(publicId); |
| if (dtdResourceName != null) { |
| InputStream is = this.getClass().getResourceAsStream(dtdResourceName); |
| if (is != null) { |
| owningTask.log("Resolved " + publicId + " to local resource " |
| + dtdResourceName, Project.MSG_VERBOSE); |
| return new InputSource(is); |
| } |
| } |
| |
| URL dtdUrl = (URL) urlDTDs.get(publicId); |
| if (dtdUrl != null) { |
| try { |
| InputStream is = dtdUrl.openStream(); |
| owningTask.log("Resolved " + publicId + " to url " |
| + dtdUrl, Project.MSG_VERBOSE); |
| return new InputSource(is); |
| } catch (IOException ioe) { |
| //ignore |
| } |
| } |
| |
| owningTask.log("Could not resolve ( publicId: " + publicId |
| + ", systemId: " + systemId + ") to a local entity", Project.MSG_INFO); |
| |
| return null; |
| } |
| |
| /** |
| * Getter method that returns the set of files to include in the EJB jar. |
| * @return the map of files |
| */ |
| public Hashtable getFiles() { |
| return (ejbFiles == null) ? new Hashtable() : ejbFiles; |
| } |
| |
| /** |
| * Get the publicId of the DTD |
| * @return the public id |
| */ |
| public String getPublicId() { |
| return publicId; |
| } |
| |
| /** |
| * Getter method that returns the value of the <ejb-name> element. |
| * @return the ejb name |
| */ |
| public String getEjbName() { |
| return ejbName; |
| } |
| |
| /** |
| * SAX parser call-back method that is used to initialize the values of some |
| * instance variables to ensure safe operation. |
| * @throws SAXException on error |
| */ |
| public void startDocument() throws SAXException { |
| this.ejbFiles = new Hashtable(DEFAULT_HASH_TABLE_SIZE, 1); |
| this.currentElement = null; |
| inEJBRef = false; |
| } |
| |
| |
| /** |
| * SAX parser call-back method that is invoked when a new element is entered |
| * into. Used to store the context (attribute name) in the currentAttribute |
| * instance variable. |
| * @param name The name of the element being entered. |
| * @param attrs Attributes associated to the element. |
| * @throws SAXException on error |
| */ |
| public void startElement(String name, AttributeList attrs) |
| throws SAXException { |
| this.currentElement = name; |
| currentText = ""; |
| if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) { |
| inEJBRef = true; |
| } else if (parseState == STATE_LOOKING_EJBJAR && name.equals(EJB_JAR)) { |
| parseState = STATE_IN_EJBJAR; |
| } else if (parseState == STATE_IN_EJBJAR && name.equals(ENTERPRISE_BEANS)) { |
| parseState = STATE_IN_BEANS; |
| } else if (parseState == STATE_IN_BEANS && name.equals(SESSION_BEAN)) { |
| parseState = STATE_IN_SESSION; |
| } else if (parseState == STATE_IN_BEANS && name.equals(ENTITY_BEAN)) { |
| parseState = STATE_IN_ENTITY; |
| } else if (parseState == STATE_IN_BEANS && name.equals(MESSAGE_BEAN)) { |
| parseState = STATE_IN_MESSAGE; |
| } |
| } |
| |
| |
| /** |
| * SAX parser call-back method that is invoked when an element is exited. |
| * Used to blank out (set to the empty string, not nullify) the name of |
| * the currentAttribute. A better method would be to use a stack as an |
| * instance variable, however since we are only interested in leaf-node |
| * data this is a simpler and workable solution. |
| * @param name The name of the attribute being exited. Ignored |
| * in this implementation. |
| * @throws SAXException on error |
| */ |
| public void endElement(String name) throws SAXException { |
| processElement(); |
| currentText = ""; |
| this.currentElement = ""; |
| if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) { |
| inEJBRef = false; |
| } else if (parseState == STATE_IN_ENTITY && name.equals(ENTITY_BEAN)) { |
| parseState = STATE_IN_BEANS; |
| } else if (parseState == STATE_IN_SESSION && name.equals(SESSION_BEAN)) { |
| parseState = STATE_IN_BEANS; |
| } else if (parseState == STATE_IN_MESSAGE && name.equals(MESSAGE_BEAN)) { |
| parseState = STATE_IN_BEANS; |
| } else if (parseState == STATE_IN_BEANS && name.equals(ENTERPRISE_BEANS)) { |
| parseState = STATE_IN_EJBJAR; |
| } else if (parseState == STATE_IN_EJBJAR && name.equals(EJB_JAR)) { |
| parseState = STATE_LOOKING_EJBJAR; |
| } |
| } |
| |
| /** |
| * SAX parser call-back method invoked whenever characters are located within |
| * an element. currentAttribute (modified by startElement and endElement) |
| * tells us whether we are in an interesting element (one of the up to four |
| * classes of an EJB). If so then converts the classname from the format |
| * org.apache.tools.ant.Parser to the convention for storing such a class, |
| * org/apache/tools/ant/Parser.class. This is then resolved into a file |
| * object under the srcdir which is stored in a Hashtable. |
| * @param ch A character array containing all the characters in |
| * the element, and maybe others that should be ignored. |
| * @param start An integer marking the position in the char |
| * array to start reading from. |
| * @param length An integer representing an offset into the |
| * char array where the current data terminates. |
| * @throws SAXException on error |
| */ |
| public void characters(char[] ch, int start, int length) |
| throws SAXException { |
| |
| currentText += new String(ch, start, length); |
| } |
| |
| |
| /** |
| * Called when an endelement is seen. |
| * This may be overridden in derived classes. |
| * This updates the ejbfiles if the element is an interface or a bean class. |
| * This updates the ejbname if the element is an ejb name. |
| */ |
| protected void processElement() { |
| if (inEJBRef |
| || (parseState != STATE_IN_ENTITY |
| && parseState != STATE_IN_SESSION |
| && parseState != STATE_IN_MESSAGE)) { |
| return; |
| } |
| |
| if (currentElement.equals(HOME_INTERFACE) |
| || currentElement.equals(REMOTE_INTERFACE) |
| || currentElement.equals(LOCAL_INTERFACE) |
| || currentElement.equals(LOCAL_HOME_INTERFACE) |
| || currentElement.equals(BEAN_CLASS) |
| || currentElement.equals(PK_CLASS)) { |
| |
| // Get the filename into a String object |
| File classFile = null; |
| String className = currentText.trim(); |
| |
| // If it's a primitive wrapper then we shouldn't try and put |
| // it into the jar, so ignore it. |
| if (!className.startsWith("java.") |
| && !className.startsWith("javax.")) { |
| // Translate periods into path separators, add .class to the |
| // name, create the File object and add it to the Hashtable. |
| className = className.replace('.', File.separatorChar); |
| className += ".class"; |
| classFile = new File(srcDir, className); |
| ejbFiles.put(className, classFile); |
| } |
| } |
| |
| // Get the value of the <ejb-name> tag. Only the first occurrence. |
| if (currentElement.equals(EJB_NAME)) { |
| if (ejbName == null) { |
| ejbName = currentText.trim(); |
| } |
| } |
| } |
| } |