1. /*
  2. * Copyright 2001-2002,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.IOException;
  20. import java.util.Hashtable;
  21. import javax.xml.parsers.SAXParser;
  22. import org.apache.tools.ant.BuildException;
  23. import org.apache.tools.ant.Project;
  24. import org.xml.sax.SAXException;
  25. /**
  26. * This class is used to generate iPlanet Application Server (iAS) 6.0 stubs and
  27. * skeletons and build an EJB Jar file. It is designed to be used with the Ant
  28. * <code>ejbjar</code> task. If only stubs and skeletons need to be generated
  29. * (in other words, if no JAR file needs to be created), refer to the
  30. * <code>iplanet-ejbc</code> task and the <code>IPlanetEjbcTask</code> class.
  31. * <p>
  32. * The following attributes may be specified by the user:
  33. * <ul>
  34. * <li><i>destdir</i> -- The base directory into which the generated JAR
  35. * files will be written. Each JAR file is written
  36. * in directories which correspond to their location
  37. * within the "descriptordir" namespace. This is a
  38. * required attribute.
  39. * <li><i>classpath</i> -- The classpath used when generating EJB stubs and
  40. * skeletons. This is an optional attribute (if
  41. * omitted, the classpath specified in the "ejbjar"
  42. * parent task will be used). If specified, the
  43. * classpath elements will be prepended to the
  44. * classpath specified in the parent "ejbjar" task.
  45. * Note that nested "classpath" elements may also be
  46. * used.
  47. * <li><i>keepgenerated</i> -- Indicates whether or not the Java source
  48. * files which are generated by ejbc will be
  49. * saved or automatically deleted. If "yes",
  50. * the source files will be retained. This is
  51. * an optional attribute (if omitted, it
  52. * defaults to "no").
  53. * <li><i>debug</i> -- Indicates whether or not the ejbc utility should
  54. * log additional debugging statements to the standard
  55. * output. If "yes", the additional debugging statements
  56. * will be generated (if omitted, it defaults to "no").
  57. * <li><i>iashome</i> -- May be used to specify the "home" directory for
  58. * this iPlanet Application server installation. This
  59. * is used to find the ejbc utility if it isn't
  60. * included in the user's system path. This is an
  61. * optional attribute (if specified, it should refer
  62. * to the <code>[install-location]/iplanet/ias6/ias
  63. * </code> directory). If omitted, the ejbc utility
  64. * must be on the user's system path.
  65. * <li><i>suffix</i> -- String value appended to the JAR filename when
  66. * creating each JAR. This attribute is not required
  67. * (if omitted, it defaults to ".jar").
  68. * </ul>
  69. * <p>
  70. * For each EJB descriptor found in the "ejbjar" parent task, this deployment
  71. * tool will locate the three classes that comprise the EJB. If these class
  72. * files cannot be located in the specified <code>srcdir</code> directory, the
  73. * task will fail. The task will also attempt to locate the EJB stubs and
  74. * skeletons in this directory. If found, the timestamps on the stubs and
  75. * skeletons will be checked to ensure they are up to date. Only if these files
  76. * cannot be found or if they are out of date will ejbc be called.
  77. *
  78. * @see IPlanetEjbc
  79. */
  80. public class IPlanetDeploymentTool extends GenericDeploymentTool {
  81. /* Attributes set by the Ant build file */
  82. private File iashome;
  83. private String jarSuffix = ".jar";
  84. private boolean keepgenerated = false;
  85. private boolean debug = false;
  86. /*
  87. * Filenames of the standard EJB descriptor (which is passed to this class
  88. * from the parent "ejbjar" task) and the iAS-specific EJB descriptor
  89. * (whose name is determined by this class). Both filenames are relative
  90. * to the directory specified by the "srcdir" attribute in the ejbjar task.
  91. */
  92. private String descriptorName;
  93. private String iasDescriptorName;
  94. /*
  95. * The displayName variable stores the value of the "display-name" element
  96. * from the standard EJB descriptor. As a future enhancement to this task,
  97. * we may determine the name of the EJB JAR file using this display-name,
  98. * but this has not be implemented yet.
  99. */
  100. private String displayName;
  101. /*
  102. * Regardless of the name of the iAS-specific EJB descriptor file, it will
  103. * written in the completed JAR file as "ias-ejb-jar.xml". This is the
  104. * naming convention implemented by iAS.
  105. */
  106. private static final String IAS_DD = "ias-ejb-jar.xml";
  107. /**
  108. * Setter method used to store the "home" directory of the user's iAS
  109. * installation. The directory specified should typically be
  110. * <code>[install-location]/iplanet/ias6/ias</code>.
  111. *
  112. * @param iashome The home directory for the user's iAS installation.
  113. */
  114. public void setIashome(File iashome) {
  115. this.iashome = iashome;
  116. }
  117. /**
  118. * Setter method used to specify whether the Java source files generated by
  119. * the ejbc utility should be saved or automatically deleted.
  120. *
  121. * @param keepgenerated boolean which, if <code>true</code>, indicates that
  122. * Java source files generated by ejbc for the stubs
  123. * and skeletons should be kept.
  124. */
  125. public void setKeepgenerated(boolean keepgenerated) {
  126. this.keepgenerated = keepgenerated;
  127. }
  128. /**
  129. * Sets whether or not debugging output will be generated when ejbc is
  130. * executed.
  131. *
  132. * @param debug A boolean indicating if debugging output should be generated
  133. */
  134. public void setDebug(boolean debug) {
  135. this.debug = debug;
  136. }
  137. /**
  138. * Setter method used to specify the filename suffix (for example, ".jar")
  139. * for the JAR files to be created.
  140. *
  141. * @param jarSuffix The string to use as the JAR filename suffix.
  142. */
  143. public void setSuffix(String jarSuffix) {
  144. this.jarSuffix = jarSuffix;
  145. }
  146. /**
  147. * Since iAS doesn't generate a "generic" JAR as part of its processing,
  148. * this attribute is ignored and a warning message is displayed to the user.
  149. *
  150. * @param inString the string to use as the suffix. This parameter is
  151. * ignored.
  152. */
  153. public void setGenericJarSuffix(String inString) {
  154. log("Since a generic JAR file is not created during processing, the "
  155. + "iPlanet Deployment Tool does not support the "
  156. + "\"genericjarsuffix\" attribute. It will be ignored.",
  157. Project.MSG_WARN);
  158. }
  159. public void processDescriptor(String descriptorName, SAXParser saxParser) {
  160. this.descriptorName = descriptorName;
  161. this.iasDescriptorName = null;
  162. log("iPlanet Deployment Tool processing: " + descriptorName + " (and "
  163. + getIasDescriptorName() + ")", Project.MSG_VERBOSE);
  164. super.processDescriptor(descriptorName, saxParser);
  165. }
  166. /**
  167. * Verifies that the user selections are valid.
  168. *
  169. * @param descriptorFileName String representing the file name of an EJB
  170. * descriptor to be processed
  171. * @param saxParser SAXParser which may be used to parse the XML
  172. * descriptor
  173. * @throws BuildException If the user selections are invalid.
  174. */
  175. protected void checkConfiguration(String descriptorFileName,
  176. SAXParser saxParser) throws BuildException {
  177. int startOfName = descriptorFileName.lastIndexOf(File.separatorChar) + 1;
  178. String stdXml = descriptorFileName.substring(startOfName);
  179. if (stdXml.equals(EJB_DD) && (getConfig().baseJarName == null)) {
  180. String msg = "No name specified for the completed JAR file. The EJB"
  181. + " descriptor should be prepended with the JAR "
  182. + "name or it should be specified using the "
  183. + "attribute \"basejarname\" in the \"ejbjar\" task.";
  184. throw new BuildException(msg, getLocation());
  185. }
  186. File iasDescriptor = new File(getConfig().descriptorDir,
  187. getIasDescriptorName());
  188. if ((!iasDescriptor.exists()) || (!iasDescriptor.isFile())) {
  189. String msg = "The iAS-specific EJB descriptor ("
  190. + iasDescriptor + ") was not found.";
  191. throw new BuildException(msg, getLocation());
  192. }
  193. if ((iashome != null) && (!iashome.isDirectory())) {
  194. String msg = "If \"iashome\" is specified, it must be a valid "
  195. + "directory (it was set to " + iashome + ").";
  196. throw new BuildException(msg, getLocation());
  197. }
  198. }
  199. /**
  200. * This method returns a list of EJB files found when the specified EJB
  201. * descriptor is parsed and processed.
  202. *
  203. * @param descriptorFileName String representing the file name of an EJB
  204. * descriptor to be processed
  205. * @param saxParser SAXParser which may be used to parse the XML
  206. * descriptor
  207. * @return Hashtable of EJB class (and other) files to be
  208. * added to the completed JAR file
  209. * @throws IOException An IOException from the parser, possibly from
  210. * the byte stream or character stream
  211. * @throws SAXException Any SAX exception, possibly wrapping another
  212. * exception
  213. */
  214. protected Hashtable parseEjbFiles(String descriptorFileName,
  215. SAXParser saxParser) throws IOException, SAXException {
  216. Hashtable files;
  217. /* Build and populate an instance of the ejbc utility */
  218. IPlanetEjbc ejbc = new IPlanetEjbc(
  219. new File(getConfig().descriptorDir,
  220. descriptorFileName),
  221. new File(getConfig().descriptorDir,
  222. getIasDescriptorName()),
  223. getConfig().srcDir,
  224. getCombinedClasspath().toString(),
  225. saxParser);
  226. ejbc.setRetainSource(keepgenerated);
  227. ejbc.setDebugOutput(debug);
  228. if (iashome != null) {
  229. ejbc.setIasHomeDir(iashome);
  230. }
  231. /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */
  232. try {
  233. ejbc.execute();
  234. } catch (IPlanetEjbc.EjbcException e) {
  235. throw new BuildException("An error has occurred while trying to "
  236. + "execute the iAS ejbc utility", e, getLocation());
  237. }
  238. displayName = ejbc.getDisplayName();
  239. files = ejbc.getEjbFiles();
  240. /* Add CMP descriptors to the list of EJB files */
  241. String[] cmpDescriptors = ejbc.getCmpDescriptors();
  242. if (cmpDescriptors.length > 0) {
  243. File baseDir = getConfig().descriptorDir;
  244. int endOfPath = descriptorFileName.lastIndexOf(File.separator);
  245. String relativePath = descriptorFileName.substring(0, endOfPath + 1);
  246. for (int i = 0; i < cmpDescriptors.length; i++) {
  247. int endOfCmp = cmpDescriptors[i].lastIndexOf('/');
  248. String cmpDescriptor = cmpDescriptors[i].substring(endOfCmp + 1);
  249. File cmpFile = new File(baseDir, relativePath + cmpDescriptor);
  250. if (!cmpFile.exists()) {
  251. throw new BuildException("The CMP descriptor file ("
  252. + cmpFile + ") could not be found.", getLocation());
  253. }
  254. files.put(cmpDescriptors[i], cmpFile);
  255. }
  256. }
  257. return files;
  258. }
  259. /**
  260. * Add the iAS-specific EJB descriptor to the list of files which will be
  261. * written to the JAR file.
  262. *
  263. * @param ejbFiles Hashtable of EJB class (and other) files to be added to
  264. * the completed JAR file.
  265. * @param ddPrefix not used
  266. */
  267. protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
  268. ejbFiles.put(META_DIR + IAS_DD, new File(getConfig().descriptorDir,
  269. getIasDescriptorName()));
  270. }
  271. /**
  272. * Get the name of the Jar that will be written. The modification date
  273. * of this jar will be checked against the dependent bean classes.
  274. *
  275. * @param baseName String name of the EJB JAR file to be written (without
  276. * a filename extension).
  277. *
  278. * @return File representing the JAR file which will be written.
  279. */
  280. File getVendorOutputJarFile(String baseName) {
  281. File jarFile = new File(getDestDir(), baseName + jarSuffix);
  282. log("JAR file name: " + jarFile.toString(), Project.MSG_VERBOSE);
  283. return jarFile;
  284. }
  285. /**
  286. * The iAS ejbc utility doesn't require the Public ID of the descriptor's
  287. * DTD for it to process correctly--this method always returns <code>null
  288. * </code>.
  289. *
  290. * @return <code>null</code>.
  291. */
  292. protected String getPublicId() {
  293. return null;
  294. }
  295. /**
  296. * Determines the name of the iAS-specific EJB descriptor using the
  297. * specified standard EJB descriptor name. In general, the standard
  298. * descriptor will be named "[basename]-ejb-jar.xml", and this method will
  299. * return "[basename]-ias-ejb-jar.xml".
  300. *
  301. * @return The name of the iAS-specific EJB descriptor file.
  302. */
  303. private String getIasDescriptorName() {
  304. /* Only calculate the descriptor name once */
  305. if (iasDescriptorName != null) {
  306. return iasDescriptorName;
  307. }
  308. String path = ""; // Directory path of the EJB descriptor
  309. String basename; // Filename appearing before name terminator
  310. String remainder; // Filename appearing after the name terminator
  311. /* Find the end of the standard descriptor's relative path */
  312. int startOfFileName = descriptorName.lastIndexOf(File.separatorChar);
  313. if (startOfFileName != -1) {
  314. path = descriptorName.substring(0, startOfFileName + 1);
  315. }
  316. /* Check to see if the standard name is used (there's no basename) */
  317. if (descriptorName.substring(startOfFileName + 1).equals(EJB_DD)) {
  318. basename = "";
  319. remainder = EJB_DD;
  320. } else {
  321. int endOfBaseName = descriptorName.indexOf(
  322. getConfig().baseNameTerminator,
  323. startOfFileName);
  324. /*
  325. * Check for the odd case where the terminator and/or filename
  326. * extension aren't found. These will ensure "ias-" appears at the
  327. * end of the name and before the '.' (if present).
  328. */
  329. if (endOfBaseName < 0) {
  330. endOfBaseName = descriptorName.lastIndexOf('.') - 1;
  331. if (endOfBaseName < 0) {
  332. endOfBaseName = descriptorName.length() - 1;
  333. }
  334. }
  335. basename = descriptorName.substring(startOfFileName + 1,
  336. endOfBaseName + 1);
  337. remainder = descriptorName.substring(endOfBaseName + 1);
  338. }
  339. iasDescriptorName = path + basename + "ias-" + remainder;
  340. return iasDescriptorName;
  341. }
  342. }