blob: e8392605b51fb2c4417eceac90e94436d2a1a0d9 [file] [log] [blame]
/*
* 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 &lt;ejb-name&gt; 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();
}
}
}
}