001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs.optional.ejb;
020:
021: // Standard java imports
022: import java.io.File;
023: import java.util.ArrayList;
024: import java.util.Iterator;
025: import java.util.List;
026: import javax.xml.parsers.ParserConfigurationException;
027: import javax.xml.parsers.SAXParser;
028: import javax.xml.parsers.SAXParserFactory;
029: import org.apache.tools.ant.BuildException;
030: import org.apache.tools.ant.DirectoryScanner;
031: import org.apache.tools.ant.Project;
032: import org.apache.tools.ant.taskdefs.MatchingTask;
033: import org.apache.tools.ant.types.EnumeratedAttribute;
034: import org.apache.tools.ant.types.FileSet;
035: import org.apache.tools.ant.types.Path;
036: import org.xml.sax.SAXException;
037:
038: /**
039: * Provides automated EJB JAR file creation.
040: * <p>
041: * Extends the
042: * MatchingTask class provided in the default ant distribution to provide a
043: * directory scanning EJB jarfile generator.
044: * </p>
045: *
046: * <p>
047: * The task works by taking the deployment descriptors one at a time and
048: * parsing them to locate the names of the classes which should be placed in
049: * the jar. The classnames are translated to java.io.Files by replacing
050: * periods with File.separatorChar and resolving the generated filename as a
051: * relative path under the srcDir attribute. All necessary files are then
052: * assembled into a jarfile. One jarfile is constructed for each deployment
053: * descriptor found.
054: * </p>
055: *
056: * */
057: public class EjbJar extends MatchingTask {
058:
059: /**
060: * Inner class used to record information about the location of a local DTD
061: */
062: public static class DTDLocation extends
063: org.apache.tools.ant.types.DTDLocation {
064: }
065:
066: /**
067: * A class which contains the configuration state of the ejbjar task.
068: * This state is passed to the deployment tools for configuration
069: */
070: static class Config {
071: // CheckStyle:VisibilityModifier OFF - bc
072: /**
073: * Stores a handle to the directory under which to search for class
074: * files
075: */
076: public File srcDir;
077:
078: /**
079: * Stores a handle to the directory under which to search for
080: * deployment descriptors
081: */
082: public File descriptorDir;
083:
084: /** Instance variable that marks the end of the 'basename' */
085: public String baseNameTerminator = "-";
086:
087: /** Stores a handle to the destination EJB Jar file */
088: public String baseJarName;
089:
090: /**
091: * Instance variable that determines whether to use a package structure
092: * of a flat directory as the destination for the jar files.
093: */
094: public boolean flatDestDir = false;
095:
096: /**
097: * The classpath to use when loading classes
098: */
099: public Path classpath;
100:
101: /**
102: * A Fileset of support classes
103: */
104: public List supportFileSets = new ArrayList();
105:
106: /**
107: * The list of configured DTD locations
108: */
109: public ArrayList dtdLocations = new ArrayList();
110:
111: /**
112: * The naming scheme used to determine the generated jar name
113: * from the descriptor information
114: */
115: public NamingScheme namingScheme;
116:
117: /**
118: * The Manifest file
119: */
120: public File manifest;
121:
122: /**
123: * The dependency analyzer to use to add additional classes to the jar
124: */
125: public String analyzer;
126: // CheckStyle:VisibilityModifier ON
127: }
128:
129: /**
130: * An EnumeratedAttribute class for handling different EJB jar naming
131: * schemes
132: */
133: public static class NamingScheme extends EnumeratedAttribute {
134: /**
135: * Naming scheme where generated jar is determined from the ejb-name in
136: * the deployment descripor
137: */
138: public static final String EJB_NAME = "ejb-name";
139:
140: /**
141: * Naming scheme where the generated jar name is based on the
142: * name of the directory containing the deployment descriptor
143: */
144: public static final String DIRECTORY = "directory";
145:
146: /**
147: * Naming scheme where the generated jar name is based on the name of
148: * the deployment descriptor file
149: */
150: public static final String DESCRIPTOR = "descriptor";
151:
152: /**
153: * Naming scheme where the generated jar is named by the basejarname
154: * attribute
155: */
156: public static final String BASEJARNAME = "basejarname";
157:
158: /**
159: * Gets the values of the NamingScheme
160: *
161: * @return an array of the values of this attribute class.
162: */
163: public String[] getValues() {
164: return new String[] { EJB_NAME, DIRECTORY, DESCRIPTOR,
165: BASEJARNAME };
166: }
167: }
168:
169: /**
170: * CMP versions supported
171: * valid CMP versions are 1.0 and 2.0
172: * @since ant 1.6
173: */
174: public static class CMPVersion extends EnumeratedAttribute {
175: /** 1.0 value */
176: public static final String CMP1_0 = "1.0";
177: /** 2.0 value */
178: public static final String CMP2_0 = "2.0";
179:
180: /** {@inheritDoc}. */
181: public String[] getValues() {
182: return new String[] { CMP1_0, CMP2_0, };
183: }
184: }
185:
186: /**
187: * The config which is built by this task and used by the various deployment
188: * tools to access the configuration of the ejbjar task
189: */
190: private Config config = new Config();
191:
192: /**
193: * Stores a handle to the directory to put the Jar files in. This is
194: * only used by the generic deployment descriptor tool which is created
195: * if no other deployment descriptor tools are provided. Normally each
196: * deployment tool will specify the desitination dir itself.
197: */
198: private File destDir;
199:
200: /** Instance variable that stores the suffix for the generated jarfile. */
201: private String genericJarSuffix = "-generic.jar";
202:
203: /** Instance variable that stores the CMP version for the jboss jarfile. */
204: private String cmpVersion = CMPVersion.CMP1_0;
205:
206: /** The list of deployment tools we are going to run. */
207: private ArrayList deploymentTools = new ArrayList();
208:
209: /**
210: * Add a deployment tool to the list of deployment tools that will be
211: * processed
212: *
213: * @param deploymentTool a deployment tool instance to which descriptors
214: * will be passed for processing.
215: */
216: protected void addDeploymentTool(EJBDeploymentTool deploymentTool) {
217: deploymentTool.setTask(this );
218: deploymentTools.add(deploymentTool);
219: }
220:
221: /**
222: * Adds a deployment tool for Weblogic server.
223: *
224: * @return the deployment tool instance to be configured.
225: */
226: public WeblogicDeploymentTool createWeblogic() {
227: WeblogicDeploymentTool tool = new WeblogicDeploymentTool();
228: addDeploymentTool(tool);
229: return tool;
230: }
231:
232: /**
233: * Adds a deployment tool for Websphere 4.0 server.
234: *
235: * @return the deployment tool instance to be configured.
236: */
237: public WebsphereDeploymentTool createWebsphere() {
238: WebsphereDeploymentTool tool = new WebsphereDeploymentTool();
239: addDeploymentTool(tool);
240: return tool;
241: }
242:
243: /**
244: * Adds a deployment tool for Borland server.
245: *
246: * @return the deployment tool instance to be configured.
247: */
248: public BorlandDeploymentTool createBorland() {
249: log("Borland deployment tools", Project.MSG_VERBOSE);
250:
251: BorlandDeploymentTool tool = new BorlandDeploymentTool();
252: tool.setTask(this );
253: deploymentTools.add(tool);
254: return tool;
255: }
256:
257: /**
258: * Adds a deployment tool for iPlanet Application Server.
259: *
260: * @return the deployment tool instance to be configured.
261: */
262: public IPlanetDeploymentTool createIplanet() {
263: log("iPlanet Application Server deployment tools",
264: Project.MSG_VERBOSE);
265:
266: IPlanetDeploymentTool tool = new IPlanetDeploymentTool();
267: addDeploymentTool(tool);
268: return tool;
269: }
270:
271: /**
272: * Adds a deployment tool for JBoss server.
273: *
274: * @return the deployment tool instance to be configured.
275: */
276: public JbossDeploymentTool createJboss() {
277: JbossDeploymentTool tool = new JbossDeploymentTool();
278: addDeploymentTool(tool);
279: return tool;
280: }
281:
282: /**
283: * Adds a deployment tool for JOnAS server.
284: *
285: * @return the deployment tool instance to be configured.
286: */
287: public JonasDeploymentTool createJonas() {
288: log("JOnAS deployment tools", Project.MSG_VERBOSE);
289:
290: JonasDeploymentTool tool = new JonasDeploymentTool();
291: addDeploymentTool(tool);
292: return tool;
293: }
294:
295: /**
296: * Adds a deployment tool for Weblogic when using the Toplink
297: * Object-Relational mapping.
298: *
299: * @return the deployment tool instance to be configured.
300: */
301: public WeblogicTOPLinkDeploymentTool createWeblogictoplink() {
302: log(
303: "The <weblogictoplink> element is no longer required. Please use "
304: + "the <weblogic> element and set newCMP=\"true\"",
305: Project.MSG_INFO);
306: WeblogicTOPLinkDeploymentTool tool = new WeblogicTOPLinkDeploymentTool();
307: addDeploymentTool(tool);
308: return tool;
309: }
310:
311: /**
312: * Adds to the classpath used to locate the super classes and
313: * interfaces of the classes that will make up the EJB JAR.
314: *
315: * @return the path to be configured.
316: */
317: public Path createClasspath() {
318: if (config.classpath == null) {
319: config.classpath = new Path(getProject());
320: }
321: return config.classpath.createPath();
322: }
323:
324: /**
325: * Create a DTD location record. This stores the location of a DTD. The
326: * DTD is identified by its public Id. The location may either be a file
327: * location or a resource location.
328: *
329: * @return the DTD location object to be configured by Ant
330: */
331: public DTDLocation createDTD() {
332: DTDLocation dtdLocation = new DTDLocation();
333: config.dtdLocations.add(dtdLocation);
334:
335: return dtdLocation;
336: }
337:
338: /**
339: * Adds a fileset for support elements.
340: *
341: * @return a fileset which can be populated with support files.
342: */
343: public FileSet createSupport() {
344: FileSet supportFileSet = new FileSet();
345: config.supportFileSets.add(supportFileSet);
346: return supportFileSet;
347: }
348:
349: /**
350: * Set the Manifest file to use when jarring. As of EJB 1.1, manifest
351: * files are no longer used to configure the EJB. However, they still
352: * have a vital importance if the EJB is intended to be packaged in an
353: * EAR file. By adding "Class-Path" settings to a Manifest file, the EJB
354: * can look for classes inside the EAR file itself, allowing for easier
355: * deployment. This is outlined in the J2EE specification, and all J2EE
356: * components are meant to support it.
357: *
358: * @param manifest the manifest to be used in the EJB jar
359: */
360: public void setManifest(File manifest) {
361: config.manifest = manifest;
362: }
363:
364: /**
365: * Sets the source directory, which is the directory that
366: * contains the classes that will be added to the EJB jar. Typically
367: * this will include the home and remote interfaces and the bean class.
368: *
369: * @param inDir the source directory.
370: */
371: public void setSrcdir(File inDir) {
372: config.srcDir = inDir;
373: }
374:
375: /**
376: * Set the descriptor directory. The descriptor directory contains the
377: * EJB deployment descriptors. These are XML files that declare the
378: * properties of a bean in a particular deployment scenario. Such
379: * properties include, for example, the transactional nature of the bean
380: * and the security access control to the bean's methods.
381: *
382: * @param inDir the directory containing the deployment descriptors.
383: */
384: public void setDescriptordir(File inDir) {
385: config.descriptorDir = inDir;
386: }
387:
388: /**
389: * Set the analyzer to use when adding in dependencies to the JAR.
390: *
391: * @param analyzer the name of the dependency analyzer or a class.
392: */
393: public void setDependency(String analyzer) {
394: config.analyzer = analyzer;
395: }
396:
397: /**
398: * Set the base name of the EJB JAR that is to be created if it is not
399: * to be determined from the name of the deployment descriptor files.
400: *
401: * @param inValue the basename that will be used when writing the jar
402: * file containing the EJB
403: */
404: public void setBasejarname(String inValue) {
405: config.baseJarName = inValue;
406: if (config.namingScheme == null) {
407: config.namingScheme = new NamingScheme();
408: config.namingScheme.setValue(NamingScheme.BASEJARNAME);
409: } else if (!config.namingScheme.getValue().equals(
410: NamingScheme.BASEJARNAME)) {
411: throw new BuildException(
412: "The basejarname attribute is not "
413: + "compatible with the "
414: + config.namingScheme.getValue()
415: + " naming scheme");
416: }
417: }
418:
419: /**
420: * Set the naming scheme used to determine the name of the generated jars
421: * from the deployment descriptor
422: *
423: * @param namingScheme the naming scheme to be used
424: */
425: public void setNaming(NamingScheme namingScheme) {
426: config.namingScheme = namingScheme;
427: if (!config.namingScheme.getValue().equals(
428: NamingScheme.BASEJARNAME)
429: && config.baseJarName != null) {
430: throw new BuildException(
431: "The basejarname attribute is not "
432: + "compatible with the "
433: + config.namingScheme.getValue()
434: + " naming scheme");
435: }
436: }
437:
438: /**
439: * Gets the destination directory.
440: *
441: * @return destination directory
442: * @since ant 1.6
443: */
444: public File getDestdir() {
445: return this .destDir;
446: }
447:
448: /**
449: * Set the destination directory. The EJB jar files will be written into
450: * this directory. The jar files that exist in this directory are also
451: * used when determining if the contents of the jar file have changed.
452: * Note that this parameter is only used if no deployment tools are
453: * specified. Typically each deployment tool will specify its own
454: * destination directory.
455: *
456: * @param inDir the destination directory in which to generate jars
457: */
458: public void setDestdir(File inDir) {
459: this .destDir = inDir;
460: }
461:
462: /**
463: * Gets the CMP version.
464: *
465: * @return CMP version
466: * @since ant 1.6
467: */
468: public String getCmpversion() {
469: return this .cmpVersion;
470: }
471:
472: /**
473: * Sets the CMP version.
474: *
475: * @param version CMP version.
476: * Must be either <code>1.0</code> or <code>2.0</code>.<br/>
477: * Default is <code>1.0</code>.<br/>
478: * Initially, only the JBoss implementation does something specific for CMP 2.0.<br/>
479: * @since ant 1.6
480: */
481: public void setCmpversion(CMPVersion version) {
482: this .cmpVersion = version.getValue();
483: }
484:
485: /**
486: * Set the classpath to use when resolving classes for inclusion in the jar.
487: *
488: * @param classpath the classpath to use.
489: */
490: public void setClasspath(Path classpath) {
491: config.classpath = classpath;
492: }
493:
494: /**
495: * Controls whether the
496: * destination JARs are written out in the destination directory with
497: * the same hierarchical structure from which the deployment descriptors
498: * have been read. If this is set to true the generated EJB jars are
499: * written into the root of the destination directory, otherwise they
500: * are written out in the same relative position as the deployment
501: * descriptors in the descriptor directory.
502: *
503: * @param inValue the new value of the flatdestdir flag.
504: */
505: public void setFlatdestdir(boolean inValue) {
506: config.flatDestDir = inValue;
507: }
508:
509: /**
510: * Set the suffix for the generated jar file. When generic jars are
511: * generated, they have a suffix which is appended to the the bean name
512: * to create the name of the jar file. Note that this suffix includes
513: * the extension fo te jar file and should therefore end with an
514: * appropriate extension such as .jar or .ear
515: *
516: * @param inString the string to use as the suffix.
517: */
518: public void setGenericjarsuffix(String inString) {
519: this .genericJarSuffix = inString;
520: }
521:
522: /**
523: * The string which terminates the bean name.
524: * The convention used by this task is
525: * that bean descriptors are named as the BeanName with some suffix. The
526: * baseNameTerminator string separates the bean name and the suffix and
527: * is used to determine the bean name.
528: *
529: * @param inValue a string which marks the end of the basename.
530: */
531: public void setBasenameterminator(String inValue) {
532: config.baseNameTerminator = inValue;
533: }
534:
535: /**
536: * Validate the config that has been configured from the build file
537: *
538: * @throws BuildException if the config is not valid
539: */
540: private void validateConfig() throws BuildException {
541: if (config.srcDir == null) {
542: throw new BuildException(
543: "The srcDir attribute must be specified");
544: }
545:
546: if (config.descriptorDir == null) {
547: config.descriptorDir = config.srcDir;
548: }
549:
550: if (config.namingScheme == null) {
551: config.namingScheme = new NamingScheme();
552: config.namingScheme.setValue(NamingScheme.DESCRIPTOR);
553: } else if (config.namingScheme.getValue().equals(
554: NamingScheme.BASEJARNAME)
555: && config.baseJarName == null) {
556: throw new BuildException("The basejarname attribute must "
557: + "be specified with the basejarname naming scheme");
558: }
559: }
560:
561: /**
562: * Invoked by Ant after the task is prepared, when it is ready to execute
563: * this task.
564: *
565: * This will configure all of the nested deployment tools to allow them to
566: * process the jar. If no deployment tools have been configured a generic
567: * tool is created to handle the jar.
568: *
569: * A parser is configured and then each descriptor found is passed to all
570: * the deployment tool elements for processing.
571: *
572: * @exception BuildException thrown whenever a problem is
573: * encountered that cannot be recovered from, to signal to ant
574: * that a major problem occurred within this task.
575: */
576: public void execute() throws BuildException {
577: validateConfig();
578:
579: if (deploymentTools.size() == 0) {
580: GenericDeploymentTool genericTool = new GenericDeploymentTool();
581: genericTool.setTask(this );
582: genericTool.setDestdir(destDir);
583: genericTool.setGenericJarSuffix(genericJarSuffix);
584: deploymentTools.add(genericTool);
585: }
586:
587: for (Iterator i = deploymentTools.iterator(); i.hasNext();) {
588: EJBDeploymentTool tool = (EJBDeploymentTool) i.next();
589: tool.configure(config);
590: tool.validateConfigured();
591: }
592:
593: try {
594: // Create the parser using whatever parser the system dictates
595: SAXParserFactory saxParserFactory = SAXParserFactory
596: .newInstance();
597: saxParserFactory.setValidating(true);
598: SAXParser saxParser = saxParserFactory.newSAXParser();
599:
600: DirectoryScanner ds = getDirectoryScanner(config.descriptorDir);
601: ds.scan();
602: String[] files = ds.getIncludedFiles();
603:
604: log(files.length + " deployment descriptors located.",
605: Project.MSG_VERBOSE);
606:
607: // Loop through the files. Each file represents one deployment
608: // descriptor, and hence one bean in our model.
609: for (int index = 0; index < files.length; ++index) {
610: // process the deployment descriptor in each tool
611: for (Iterator i = deploymentTools.iterator(); i
612: .hasNext();) {
613: EJBDeploymentTool tool = (EJBDeploymentTool) i
614: .next();
615: tool.processDescriptor(files[index], saxParser);
616: }
617: }
618: } catch (SAXException se) {
619: String msg = "SAXException while creating parser."
620: + " Details: " + se.getMessage();
621: throw new BuildException(msg, se);
622: } catch (ParserConfigurationException pce) {
623: String msg = "ParserConfigurationException while creating parser. "
624: + "Details: " + pce.getMessage();
625: throw new BuildException(msg, pce);
626: }
627: } // end of execute()
628:
629: }
|