1. /*
  2. * Copyright 2000-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional.ejb;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileNotFoundException;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.net.URL;
  24. import java.util.Hashtable;
  25. import org.apache.tools.ant.Project;
  26. import org.apache.tools.ant.Task;
  27. import org.xml.sax.AttributeList;
  28. import org.xml.sax.InputSource;
  29. import org.xml.sax.SAXException;
  30. /**
  31. * Inner class used by EjbJar to facilitate the parsing of deployment
  32. * descriptors and the capture of appropriate information. Extends
  33. * HandlerBase so it only implements the methods needed. During parsing
  34. * creates a hashtable consisting of entries mapping the name it should be
  35. * inserted into an EJB jar as to a File representing the file on disk. This
  36. * list can then be accessed through the getFiles() method.
  37. */
  38. public class DescriptorHandler extends org.xml.sax.HandlerBase {
  39. private static final int STATE_LOOKING_EJBJAR = 1;
  40. private static final int STATE_IN_EJBJAR = 2;
  41. private static final int STATE_IN_BEANS = 3;
  42. private static final int STATE_IN_SESSION = 4;
  43. private static final int STATE_IN_ENTITY = 5;
  44. private static final int STATE_IN_MESSAGE = 6;
  45. private Task owningTask;
  46. private String publicId = null;
  47. /**
  48. * Bunch of constants used for storing entries in a hashtable, and for
  49. * constructing the filenames of various parts of the ejb jar.
  50. */
  51. private static final String EJB_REF = "ejb-ref";
  52. private static final String EJB_LOCAL_REF = "ejb-local-ref";
  53. private static final String HOME_INTERFACE = "home";
  54. private static final String REMOTE_INTERFACE = "remote";
  55. private static final String LOCAL_HOME_INTERFACE = "local-home";
  56. private static final String LOCAL_INTERFACE = "local";
  57. private static final String BEAN_CLASS = "ejb-class";
  58. private static final String PK_CLASS = "prim-key-class";
  59. private static final String EJB_NAME = "ejb-name";
  60. private static final String EJB_JAR = "ejb-jar";
  61. private static final String ENTERPRISE_BEANS = "enterprise-beans";
  62. private static final String ENTITY_BEAN = "entity";
  63. private static final String SESSION_BEAN = "session";
  64. private static final String MESSAGE_BEAN = "message-driven";
  65. /**
  66. * The state of the parsing
  67. */
  68. private int parseState = STATE_LOOKING_EJBJAR;
  69. /**
  70. * Instance variable used to store the name of the current element being
  71. * processed by the SAX parser. Accessed by the SAX parser call-back methods
  72. * startElement() and endElement().
  73. */
  74. protected String currentElement = null;
  75. /**
  76. * The text of the current element
  77. */
  78. protected String currentText = null;
  79. /**
  80. * Instance variable that stores the names of the files as they will be
  81. * put into the jar file, mapped to File objects Accessed by the SAX
  82. * parser call-back method characters().
  83. */
  84. protected Hashtable ejbFiles = null;
  85. /**
  86. * Instance variable that stores the value found in the <ejb-name> element
  87. */
  88. protected String ejbName = null;
  89. private Hashtable fileDTDs = new Hashtable();
  90. private Hashtable resourceDTDs = new Hashtable();
  91. private boolean inEJBRef = false;
  92. private Hashtable urlDTDs = new Hashtable();
  93. /**
  94. * The directory containing the bean classes and interfaces. This is
  95. * used for performing dependency file lookups.
  96. */
  97. private File srcDir;
  98. public DescriptorHandler(Task task, File srcDir) {
  99. this.owningTask = task;
  100. this.srcDir = srcDir;
  101. }
  102. public void registerDTD(String publicId, String location) {
  103. if (location == null) {
  104. return;
  105. }
  106. File fileDTD = new File(location);
  107. if (!fileDTD.exists()) {
  108. // resolve relative to project basedir
  109. fileDTD = owningTask.getProject().resolveFile(location);
  110. }
  111. if (fileDTD.exists()) {
  112. if (publicId != null) {
  113. fileDTDs.put(publicId, fileDTD);
  114. owningTask.log("Mapped publicId " + publicId + " to file "
  115. + fileDTD, Project.MSG_VERBOSE);
  116. }
  117. return;
  118. }
  119. if (getClass().getResource(location) != null) {
  120. if (publicId != null) {
  121. resourceDTDs.put(publicId, location);
  122. owningTask.log("Mapped publicId " + publicId + " to resource "
  123. + location, Project.MSG_VERBOSE);
  124. }
  125. }
  126. try {
  127. if (publicId != null) {
  128. URL urldtd = new URL(location);
  129. urlDTDs.put(publicId, urldtd);
  130. }
  131. } catch (java.net.MalformedURLException e) {
  132. //ignored
  133. }
  134. }
  135. public InputSource resolveEntity(String publicId, String systemId)
  136. throws SAXException {
  137. this.publicId = publicId;
  138. File dtdFile = (File) fileDTDs.get(publicId);
  139. if (dtdFile != null) {
  140. try {
  141. owningTask.log("Resolved " + publicId + " to local file "
  142. + dtdFile, Project.MSG_VERBOSE);
  143. return new InputSource(new FileInputStream(dtdFile));
  144. } catch (FileNotFoundException ex) {
  145. // ignore
  146. }
  147. }
  148. String dtdResourceName = (String) resourceDTDs.get(publicId);
  149. if (dtdResourceName != null) {
  150. InputStream is = this.getClass().getResourceAsStream(dtdResourceName);
  151. if (is != null) {
  152. owningTask.log("Resolved " + publicId + " to local resource "
  153. + dtdResourceName, Project.MSG_VERBOSE);
  154. return new InputSource(is);
  155. }
  156. }
  157. URL dtdUrl = (URL) urlDTDs.get(publicId);
  158. if (dtdUrl != null) {
  159. try {
  160. InputStream is = dtdUrl.openStream();
  161. owningTask.log("Resolved " + publicId + " to url "
  162. + dtdUrl, Project.MSG_VERBOSE);
  163. return new InputSource(is);
  164. } catch (IOException ioe) {
  165. //ignore
  166. }
  167. }
  168. owningTask.log("Could not resolve ( publicId: " + publicId
  169. + ", systemId: " + systemId + ") to a local entity", Project.MSG_INFO);
  170. return null;
  171. }
  172. /**
  173. * Getter method that returns the set of files to include in the EJB jar.
  174. */
  175. public Hashtable getFiles() {
  176. return (ejbFiles == null) ? new Hashtable() : ejbFiles;
  177. }
  178. /**
  179. * Get the publicId of the DTD
  180. */
  181. public String getPublicId() {
  182. return publicId;
  183. }
  184. /**
  185. * Getter method that returns the value of the <ejb-name> element.
  186. */
  187. public String getEjbName() {
  188. return ejbName;
  189. }
  190. /**
  191. * SAX parser call-back method that is used to initialize the values of some
  192. * instance variables to ensure safe operation.
  193. */
  194. public void startDocument() throws SAXException {
  195. this.ejbFiles = new Hashtable(10, 1);
  196. this.currentElement = null;
  197. inEJBRef = false;
  198. }
  199. /**
  200. * SAX parser call-back method that is invoked when a new element is entered
  201. * into. Used to store the context (attribute name) in the currentAttribute
  202. * instance variable.
  203. * @param name The name of the element being entered.
  204. * @param attrs Attributes associated to the element.
  205. */
  206. public void startElement(String name, AttributeList attrs)
  207. throws SAXException {
  208. this.currentElement = name;
  209. currentText = "";
  210. if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
  211. inEJBRef = true;
  212. } else if (parseState == STATE_LOOKING_EJBJAR && name.equals(EJB_JAR)) {
  213. parseState = STATE_IN_EJBJAR;
  214. } else if (parseState == STATE_IN_EJBJAR && name.equals(ENTERPRISE_BEANS)) {
  215. parseState = STATE_IN_BEANS;
  216. } else if (parseState == STATE_IN_BEANS && name.equals(SESSION_BEAN)) {
  217. parseState = STATE_IN_SESSION;
  218. } else if (parseState == STATE_IN_BEANS && name.equals(ENTITY_BEAN)) {
  219. parseState = STATE_IN_ENTITY;
  220. } else if (parseState == STATE_IN_BEANS && name.equals(MESSAGE_BEAN)) {
  221. parseState = STATE_IN_MESSAGE;
  222. }
  223. }
  224. /**
  225. * SAX parser call-back method that is invoked when an element is exited.
  226. * Used to blank out (set to the empty string, not nullify) the name of
  227. * the currentAttribute. A better method would be to use a stack as an
  228. * instance variable, however since we are only interested in leaf-node
  229. * data this is a simpler and workable solution.
  230. * @param name The name of the attribute being exited. Ignored
  231. * in this implementation.
  232. */
  233. public void endElement(String name) throws SAXException {
  234. processElement();
  235. currentText = "";
  236. this.currentElement = "";
  237. if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
  238. inEJBRef = false;
  239. } else if (parseState == STATE_IN_ENTITY && name.equals(ENTITY_BEAN)) {
  240. parseState = STATE_IN_BEANS;
  241. } else if (parseState == STATE_IN_SESSION && name.equals(SESSION_BEAN)) {
  242. parseState = STATE_IN_BEANS;
  243. } else if (parseState == STATE_IN_MESSAGE && name.equals(MESSAGE_BEAN)) {
  244. parseState = STATE_IN_BEANS;
  245. } else if (parseState == STATE_IN_BEANS && name.equals(ENTERPRISE_BEANS)) {
  246. parseState = STATE_IN_EJBJAR;
  247. } else if (parseState == STATE_IN_EJBJAR && name.equals(EJB_JAR)) {
  248. parseState = STATE_LOOKING_EJBJAR;
  249. }
  250. }
  251. /**
  252. * SAX parser call-back method invoked whenever characters are located within
  253. * an element. currentAttribute (modified by startElement and endElement)
  254. * tells us whether we are in an interesting element (one of the up to four
  255. * classes of an EJB). If so then converts the classname from the format
  256. * org.apache.tools.ant.Parser to the convention for storing such a class,
  257. * org/apache/tools/ant/Parser.class. This is then resolved into a file
  258. * object under the srcdir which is stored in a Hashtable.
  259. * @param ch A character array containing all the characters in
  260. * the element, and maybe others that should be ignored.
  261. * @param start An integer marking the position in the char
  262. * array to start reading from.
  263. * @param length An integer representing an offset into the
  264. * char array where the current data terminates.
  265. */
  266. public void characters(char[] ch, int start, int length)
  267. throws SAXException {
  268. currentText += new String(ch, start, length);
  269. }
  270. protected void processElement() {
  271. if (inEJBRef
  272. || (parseState != STATE_IN_ENTITY
  273. && parseState != STATE_IN_SESSION
  274. && parseState != STATE_IN_MESSAGE)) {
  275. return;
  276. }
  277. if (currentElement.equals(HOME_INTERFACE)
  278. || currentElement.equals(REMOTE_INTERFACE)
  279. || currentElement.equals(LOCAL_INTERFACE)
  280. || currentElement.equals(LOCAL_HOME_INTERFACE)
  281. || currentElement.equals(BEAN_CLASS)
  282. || currentElement.equals(PK_CLASS)) {
  283. // Get the filename into a String object
  284. File classFile = null;
  285. String className = currentText.trim();
  286. // If it's a primitive wrapper then we shouldn't try and put
  287. // it into the jar, so ignore it.
  288. if (!className.startsWith("java.")
  289. && !className.startsWith("javax.")) {
  290. // Translate periods into path separators, add .class to the
  291. // name, create the File object and add it to the Hashtable.
  292. className = className.replace('.', File.separatorChar);
  293. className += ".class";
  294. classFile = new File(srcDir, className);
  295. ejbFiles.put(className, classFile);
  296. }
  297. }
  298. // Get the value of the <ejb-name> tag. Only the first occurrence.
  299. if (currentElement.equals(EJB_NAME)) {
  300. if (ejbName == null) {
  301. ejbName = currentText.trim();
  302. }
  303. }
  304. }
  305. }