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. // Standard java imports
  19. import java.io.File;
  20. import java.util.ArrayList;
  21. import java.util.Iterator;
  22. import java.util.List;
  23. import javax.xml.parsers.ParserConfigurationException;
  24. import javax.xml.parsers.SAXParser;
  25. import javax.xml.parsers.SAXParserFactory;
  26. import org.apache.tools.ant.BuildException;
  27. import org.apache.tools.ant.DirectoryScanner;
  28. import org.apache.tools.ant.Project;
  29. import org.apache.tools.ant.taskdefs.MatchingTask;
  30. import org.apache.tools.ant.types.EnumeratedAttribute;
  31. import org.apache.tools.ant.types.FileSet;
  32. import org.apache.tools.ant.types.Path;
  33. import org.xml.sax.SAXException;
  34. /**
  35. * Provides automated EJB JAR file creation.
  36. * <p>
  37. * Extends the
  38. * MatchingTask class provided in the default ant distribution to provide a
  39. * directory scanning EJB jarfile generator.
  40. * </p>
  41. *
  42. * <p>
  43. * The task works by taking the deployment descriptors one at a time and
  44. * parsing them to locate the names of the classes which should be placed in
  45. * the jar. The classnames are translated to java.io.Files by replacing
  46. * periods with File.separatorChar and resolving the generated filename as a
  47. * relative path under the srcDir attribute. All necessary files are then
  48. * assembled into a jarfile. One jarfile is constructed for each deployment
  49. * descriptor found.
  50. * </p>
  51. *
  52. * */
  53. public class EjbJar extends MatchingTask {
  54. /**
  55. * Inner class used to record information about the location of a local DTD
  56. */
  57. public static class DTDLocation
  58. extends org.apache.tools.ant.types.DTDLocation {
  59. }
  60. /**
  61. * A class which contains the configuration state of the ejbjar task.
  62. * This state is passed to the deployment tools for configuration
  63. */
  64. static class Config {
  65. /**
  66. * Stores a handle to the directory under which to search for class
  67. * files
  68. */
  69. public File srcDir;
  70. /**
  71. * Stores a handle to the directory under which to search for
  72. * deployment descriptors
  73. */
  74. public File descriptorDir;
  75. /** Instance variable that marks the end of the 'basename' */
  76. public String baseNameTerminator = "-";
  77. /** Stores a handle to the destination EJB Jar file */
  78. public String baseJarName;
  79. /**
  80. * Instance variable that determines whether to use a package structure
  81. * of a flat directory as the destination for the jar files.
  82. */
  83. public boolean flatDestDir = false;
  84. /**
  85. * The classpath to use when loading classes
  86. */
  87. public Path classpath;
  88. /**
  89. * A Fileset of support classes
  90. */
  91. public List supportFileSets = new ArrayList();
  92. /**
  93. * The list of configured DTD locations
  94. */
  95. public ArrayList dtdLocations = new ArrayList();
  96. /**
  97. * The naming scheme used to determine the generated jar name
  98. * from the descriptor information
  99. */
  100. public NamingScheme namingScheme;
  101. /**
  102. * The Manifest file
  103. */
  104. public File manifest;
  105. /**
  106. * The dependency analyzer to use to add additional classes to the jar
  107. */
  108. public String analyzer;
  109. }
  110. /**
  111. * An EnumeratedAttribute class for handling different EJB jar naming
  112. * schemes
  113. */
  114. public static class NamingScheme extends EnumeratedAttribute {
  115. /**
  116. * Naming scheme where generated jar is determined from the ejb-name in
  117. * the deployment descripor
  118. */
  119. public static final String EJB_NAME = "ejb-name";
  120. /**
  121. * Naming scheme where the generated jar name is based on the
  122. * name of the directory containing the deployment descriptor
  123. */
  124. public static final String DIRECTORY = "directory";
  125. /**
  126. * Naming scheme where the generated jar name is based on the name of
  127. * the deployment descriptor file
  128. */
  129. public static final String DESCRIPTOR = "descriptor";
  130. /**
  131. * Naming scheme where the generated jar is named by the basejarname
  132. * attribute
  133. */
  134. public static final String BASEJARNAME = "basejarname";
  135. /**
  136. * Gets the values of the NamingScheme
  137. *
  138. * @return an array of the values of this attribute class.
  139. */
  140. public String[] getValues() {
  141. return new String[] {EJB_NAME, DIRECTORY, DESCRIPTOR, BASEJARNAME};
  142. }
  143. }
  144. /**
  145. * CMP versions supported
  146. * valid CMP versions are 1.0 and 2.0
  147. * @since ant 1.6
  148. */
  149. public static class CMPVersion extends EnumeratedAttribute {
  150. public static final String CMP1_0 = "1.0";
  151. public static final String CMP2_0 = "2.0";
  152. public String[] getValues() {
  153. return new String[]{
  154. CMP1_0,
  155. CMP2_0,
  156. };
  157. }
  158. }
  159. /**
  160. * The config which is built by this task and used by the various deployment
  161. * tools to access the configuration of the ejbjar task
  162. */
  163. private Config config = new Config();
  164. /**
  165. * Stores a handle to the directory to put the Jar files in. This is
  166. * only used by the generic deployment descriptor tool which is created
  167. * if no other deployment descriptor tools are provided. Normally each
  168. * deployment tool will specify the desitination dir itself.
  169. */
  170. private File destDir;
  171. /** Instance variable that stores the suffix for the generated jarfile. */
  172. private String genericJarSuffix = "-generic.jar";
  173. /** Instance variable that stores the CMP version for the jboss jarfile. */
  174. private String cmpVersion = CMPVersion.CMP1_0;
  175. /** The list of deployment tools we are going to run. */
  176. private ArrayList deploymentTools = new ArrayList();
  177. /**
  178. * Add a deployment tool to the list of deployment tools that will be
  179. * processed
  180. *
  181. * @param deploymentTool a deployment tool instance to which descriptors
  182. * will be passed for processing.
  183. */
  184. protected void addDeploymentTool(EJBDeploymentTool deploymentTool) {
  185. deploymentTool.setTask(this);
  186. deploymentTools.add(deploymentTool);
  187. }
  188. /**
  189. * Adds a deployment tool for Weblogic server.
  190. *
  191. * @return the deployment tool instance to be configured.
  192. */
  193. public WeblogicDeploymentTool createWeblogic() {
  194. WeblogicDeploymentTool tool = new WeblogicDeploymentTool();
  195. addDeploymentTool(tool);
  196. return tool;
  197. }
  198. /**
  199. * Adds a deployment tool for Websphere 4.0 server.
  200. *
  201. * @return the deployment tool instance to be configured.
  202. */
  203. public WebsphereDeploymentTool createWebsphere() {
  204. WebsphereDeploymentTool tool = new WebsphereDeploymentTool();
  205. addDeploymentTool(tool);
  206. return tool;
  207. }
  208. /**
  209. * Adds a deployment tool for Borland server.
  210. *
  211. * @return the deployment tool instance to be configured.
  212. */
  213. public BorlandDeploymentTool createBorland() {
  214. log("Borland deployment tools", Project.MSG_VERBOSE);
  215. BorlandDeploymentTool tool = new BorlandDeploymentTool();
  216. tool.setTask(this);
  217. deploymentTools.add(tool);
  218. return tool;
  219. }
  220. /**
  221. * Adds a deployment tool for iPlanet Application Server.
  222. *
  223. * @return the deployment tool instance to be configured.
  224. */
  225. public IPlanetDeploymentTool createIplanet() {
  226. log("iPlanet Application Server deployment tools", Project.MSG_VERBOSE);
  227. IPlanetDeploymentTool tool = new IPlanetDeploymentTool();
  228. addDeploymentTool(tool);
  229. return tool;
  230. }
  231. /**
  232. * Adds a deployment tool for JBoss server.
  233. *
  234. * @return the deployment tool instance to be configured.
  235. */
  236. public JbossDeploymentTool createJboss() {
  237. JbossDeploymentTool tool = new JbossDeploymentTool();
  238. addDeploymentTool(tool);
  239. return tool;
  240. }
  241. /**
  242. * Adds a deployment tool for JOnAS server.
  243. *
  244. * @return the deployment tool instance to be configured.
  245. */
  246. public JonasDeploymentTool createJonas() {
  247. log("JOnAS deployment tools", Project.MSG_VERBOSE);
  248. JonasDeploymentTool tool = new JonasDeploymentTool();
  249. addDeploymentTool(tool);
  250. return tool;
  251. }
  252. /**
  253. * Adds a deployment tool for Weblogic when using the Toplink
  254. * Object-Relational mapping.
  255. *
  256. * @return the deployment tool instance to be configured.
  257. */
  258. public WeblogicTOPLinkDeploymentTool createWeblogictoplink() {
  259. log("The <weblogictoplink> element is no longer required. Please use "
  260. + "the <weblogic> element and set newCMP=\"true\"",
  261. Project.MSG_INFO);
  262. WeblogicTOPLinkDeploymentTool tool
  263. = new WeblogicTOPLinkDeploymentTool();
  264. addDeploymentTool(tool);
  265. return tool;
  266. }
  267. /**
  268. * Adds to the classpath used to locate the super classes and
  269. * interfaces of the classes that will make up the EJB JAR.
  270. *
  271. * @return the path to be configured.
  272. */
  273. public Path createClasspath() {
  274. if (config.classpath == null) {
  275. config.classpath = new Path(getProject());
  276. }
  277. return config.classpath.createPath();
  278. }
  279. /**
  280. * Create a DTD location record. This stores the location of a DTD. The
  281. * DTD is identified by its public Id. The location may either be a file
  282. * location or a resource location.
  283. *
  284. * @return the DTD location object to be configured by Ant
  285. */
  286. public DTDLocation createDTD() {
  287. DTDLocation dtdLocation = new DTDLocation();
  288. config.dtdLocations.add(dtdLocation);
  289. return dtdLocation;
  290. }
  291. /**
  292. * Adds a fileset for support elements.
  293. *
  294. * @return a fileset which can be populated with support files.
  295. */
  296. public FileSet createSupport() {
  297. FileSet supportFileSet = new FileSet();
  298. config.supportFileSets.add(supportFileSet);
  299. return supportFileSet;
  300. }
  301. /**
  302. * Set the Manifest file to use when jarring. As of EJB 1.1, manifest
  303. * files are no longer used to configure the EJB. However, they still
  304. * have a vital importance if the EJB is intended to be packaged in an
  305. * EAR file. By adding "Class-Path" settings to a Manifest file, the EJB
  306. * can look for classes inside the EAR file itself, allowing for easier
  307. * deployment. This is outlined in the J2EE specification, and all J2EE
  308. * components are meant to support it.
  309. *
  310. * @param manifest the manifest to be used in the EJB jar
  311. */
  312. public void setManifest(File manifest) {
  313. config.manifest = manifest;
  314. }
  315. /**
  316. * Sets the source directory, which is the directory that
  317. * contains the classes that will be added to the EJB jar. Typically
  318. * this will include the home and remote interfaces and the bean class.
  319. *
  320. * @param inDir the source directory.
  321. */
  322. public void setSrcdir(File inDir) {
  323. config.srcDir = inDir;
  324. }
  325. /**
  326. * Set the descriptor directory. The descriptor directory contains the
  327. * EJB deployment descriptors. These are XML files that declare the
  328. * properties of a bean in a particular deployment scenario. Such
  329. * properties include, for example, the transactional nature of the bean
  330. * and the security access control to the bean's methods.
  331. *
  332. * @param inDir the directory containing the deployment descriptors.
  333. */
  334. public void setDescriptordir(File inDir) {
  335. config.descriptorDir = inDir;
  336. }
  337. /**
  338. * Set the analyzer to use when adding in dependencies to the JAR.
  339. *
  340. * @param analyzer the name of the dependency analyzer or a class.
  341. */
  342. public void setDependency(String analyzer) {
  343. config.analyzer = analyzer;
  344. }
  345. /**
  346. * Set the base name of the EJB JAR that is to be created if it is not
  347. * to be determined from the name of the deployment descriptor files.
  348. *
  349. * @param inValue the basename that will be used when writing the jar
  350. * file containing the EJB
  351. */
  352. public void setBasejarname(String inValue) {
  353. config.baseJarName = inValue;
  354. if (config.namingScheme == null) {
  355. config.namingScheme = new NamingScheme();
  356. config.namingScheme.setValue(NamingScheme.BASEJARNAME);
  357. } else if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)) {
  358. throw new BuildException("The basejarname attribute is not "
  359. + "compatible with the "
  360. + config.namingScheme.getValue() + " naming scheme");
  361. }
  362. }
  363. /**
  364. * Set the naming scheme used to determine the name of the generated jars
  365. * from the deployment descriptor
  366. *
  367. * @param namingScheme the naming scheme to be used
  368. */
  369. public void setNaming(NamingScheme namingScheme) {
  370. config.namingScheme = namingScheme;
  371. if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)
  372. && config.baseJarName != null) {
  373. throw new BuildException("The basejarname attribute is not "
  374. + "compatible with the "
  375. + config.namingScheme.getValue() + " naming scheme");
  376. }
  377. }
  378. /**
  379. * Gets the destination directory.
  380. *
  381. * @return destination directory
  382. * @since ant 1.6
  383. */
  384. public File getDestdir() {
  385. return this.destDir;
  386. }
  387. /**
  388. * Set the destination directory. The EJB jar files will be written into
  389. * this directory. The jar files that exist in this directory are also
  390. * used when determining if the contents of the jar file have changed.
  391. * Note that this parameter is only used if no deployment tools are
  392. * specified. Typically each deployment tool will specify its own
  393. * destination directory.
  394. *
  395. * @param inDir the destination directory in which to generate jars
  396. */
  397. public void setDestdir(File inDir) {
  398. this.destDir = inDir;
  399. }
  400. /**
  401. * Gets the CMP version.
  402. *
  403. * @return CMP version
  404. * @since ant 1.6
  405. */
  406. public String getCmpversion() {
  407. return this.cmpVersion;
  408. }
  409. /**
  410. * Sets the CMP version.
  411. *
  412. * @param version CMP version.
  413. * Must be either <code>1.0</code> or <code>2.0</code>.<br/>
  414. * Default is <code>1.0</code>.<br/>
  415. * Initially, only the JBoss implementation does something specific for CMP 2.0.<br/>
  416. * @since ant 1.6
  417. */
  418. public void setCmpversion(CMPVersion version) {
  419. this.cmpVersion = version.getValue();
  420. }
  421. /**
  422. * Set the classpath to use when resolving classes for inclusion in the jar.
  423. *
  424. * @param classpath the classpath to use.
  425. */
  426. public void setClasspath(Path classpath) {
  427. config.classpath = classpath;
  428. }
  429. /**
  430. * Controls whether the
  431. * destination JARs are written out in the destination directory with
  432. * the same hierarchical structure from which the deployment descriptors
  433. * have been read. If this is set to true the generated EJB jars are
  434. * written into the root of the destination directory, otherwise they
  435. * are written out in the same relative position as the deployment
  436. * descriptors in the descriptor directory.
  437. *
  438. * @param inValue the new value of the flatdestdir flag.
  439. */
  440. public void setFlatdestdir(boolean inValue) {
  441. config.flatDestDir = inValue;
  442. }
  443. /**
  444. * Set the suffix for the generated jar file. When generic jars are
  445. * generated, they have a suffix which is appended to the the bean name
  446. * to create the name of the jar file. Note that this suffix includes
  447. * the extension fo te jar file and should therefore end with an
  448. * appropriate extension such as .jar or .ear
  449. *
  450. * @param inString the string to use as the suffix.
  451. */
  452. public void setGenericjarsuffix(String inString) {
  453. this.genericJarSuffix = inString;
  454. }
  455. /**
  456. * The string which terminates the bean name.
  457. * The convention used by this task is
  458. * that bean descriptors are named as the BeanName with some suffix. The
  459. * baseNameTerminator string separates the bean name and the suffix and
  460. * is used to determine the bean name.
  461. *
  462. * @param inValue a string which marks the end of the basename.
  463. */
  464. public void setBasenameterminator(String inValue) {
  465. config.baseNameTerminator = inValue;
  466. }
  467. /**
  468. * Validate the config that has been configured from the build file
  469. *
  470. * @throws BuildException if the config is not valid
  471. */
  472. private void validateConfig() throws BuildException {
  473. if (config.srcDir == null) {
  474. throw new BuildException("The srcDir attribute must be specified");
  475. }
  476. if (config.descriptorDir == null) {
  477. config.descriptorDir = config.srcDir;
  478. }
  479. if (config.namingScheme == null) {
  480. config.namingScheme = new NamingScheme();
  481. config.namingScheme.setValue(NamingScheme.DESCRIPTOR);
  482. } else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)
  483. && config.baseJarName == null) {
  484. throw new BuildException("The basejarname attribute must "
  485. + "be specified with the basejarname naming scheme");
  486. }
  487. }
  488. /**
  489. * Invoked by Ant after the task is prepared, when it is ready to execute
  490. * this task.
  491. *
  492. * This will configure all of the nested deployment tools to allow them to
  493. * process the jar. If no deployment tools have been configured a generic
  494. * tool is created to handle the jar.
  495. *
  496. * A parser is configured and then each descriptor found is passed to all
  497. * the deployment tool elements for processing.
  498. *
  499. * @exception BuildException thrown whenever a problem is
  500. * encountered that cannot be recovered from, to signal to ant
  501. * that a major problem occurred within this task.
  502. */
  503. public void execute() throws BuildException {
  504. validateConfig();
  505. if (deploymentTools.size() == 0) {
  506. GenericDeploymentTool genericTool = new GenericDeploymentTool();
  507. genericTool.setTask(this);
  508. genericTool.setDestdir(destDir);
  509. genericTool.setGenericJarSuffix(genericJarSuffix);
  510. deploymentTools.add(genericTool);
  511. }
  512. for (Iterator i = deploymentTools.iterator(); i.hasNext();) {
  513. EJBDeploymentTool tool = (EJBDeploymentTool) i.next();
  514. tool.configure(config);
  515. tool.validateConfigured();
  516. }
  517. try {
  518. // Create the parser using whatever parser the system dictates
  519. SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
  520. saxParserFactory.setValidating(true);
  521. SAXParser saxParser = saxParserFactory.newSAXParser();
  522. DirectoryScanner ds = getDirectoryScanner(config.descriptorDir);
  523. ds.scan();
  524. String[] files = ds.getIncludedFiles();
  525. log(files.length + " deployment descriptors located.",
  526. Project.MSG_VERBOSE);
  527. // Loop through the files. Each file represents one deployment
  528. // descriptor, and hence one bean in our model.
  529. for (int index = 0; index < files.length; ++index) {
  530. // process the deployment descriptor in each tool
  531. for (Iterator i = deploymentTools.iterator(); i.hasNext();) {
  532. EJBDeploymentTool tool = (EJBDeploymentTool) i.next();
  533. tool.processDescriptor(files[index], saxParser);
  534. }
  535. }
  536. } catch (SAXException se) {
  537. String msg = "SAXException while creating parser."
  538. + " Details: "
  539. + se.getMessage();
  540. throw new BuildException(msg, se);
  541. } catch (ParserConfigurationException pce) {
  542. String msg = "ParserConfigurationException while creating parser. "
  543. + "Details: " + pce.getMessage();
  544. throw new BuildException(msg, pce);
  545. }
  546. } // end of execute()
  547. }