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: package org.apache.tools.ant.taskdefs.optional.ejb;
019:
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.FileOutputStream;
023: import java.io.IOException;
024: import java.io.InputStream;
025: import java.util.Enumeration;
026: import java.util.Hashtable;
027: import java.util.Iterator;
028: import java.util.Vector;
029: import java.util.jar.JarEntry;
030: import java.util.jar.JarFile;
031: import java.util.jar.JarOutputStream;
032: import javax.xml.parsers.SAXParser;
033: import javax.xml.parsers.SAXParserFactory;
034: import org.apache.tools.ant.AntClassLoader;
035: import org.apache.tools.ant.BuildException;
036: import org.apache.tools.ant.Project;
037: import org.apache.tools.ant.taskdefs.Java;
038: import org.apache.tools.ant.types.Environment;
039: import org.apache.tools.ant.types.Path;
040: import org.apache.tools.ant.util.FileUtils;
041: import org.xml.sax.InputSource;
042:
043: /**
044: The weblogic element is used to control the weblogic.ejbc compiler for
045: generating weblogic EJB jars. Prior to Ant 1.3, the method of locating CMP
046: descriptors was to use the ejbjar naming convention. So if your ejb-jar was
047: called, Customer-ejb-jar.xml, your weblogic descriptor was called Customer-
048: weblogic-ejb-jar.xml and your CMP descriptor had to be Customer-weblogic-cmp-
049: rdbms-jar.xml. In addition, the <type-storage> element in the weblogic
050: descriptor had to be set to the standard name META-INF/weblogic-cmp-rdbms-
051: jar.xml, as that is where the CMP descriptor was mapped to in the generated
052: jar.
053: */
054: public class WeblogicDeploymentTool extends GenericDeploymentTool {
055: /** EJB11 id */
056: public static final String PUBLICID_EJB11 = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
057: /** EJB20 id */
058: public static final String PUBLICID_EJB20 = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
059: /** Weblogic 5.1.0 id */
060: public static final String PUBLICID_WEBLOGIC_EJB510 = "-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EN";
061: /** Weblogic 6.0.0 id */
062: public static final String PUBLICID_WEBLOGIC_EJB600 = "-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN";
063: /** Weblogic 7.0.0 id */
064: public static final String PUBLICID_WEBLOGIC_EJB700 = "-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN";
065:
066: /** Weblogic 5.1 dtd location */
067: protected static final String DEFAULT_WL51_EJB11_DTD_LOCATION = "/weblogic/ejb/deployment/xml/ejb-jar.dtd";
068: /** Weblogic 6.0 ejb 1.1 dtd location */
069: protected static final String DEFAULT_WL60_EJB11_DTD_LOCATION = "/weblogic/ejb20/dd/xml/ejb11-jar.dtd";
070: /** Weblogic 6.0 ejb 2.0 dtd location */
071: protected static final String DEFAULT_WL60_EJB20_DTD_LOCATION = "/weblogic/ejb20/dd/xml/ejb20-jar.dtd";
072:
073: protected static final String DEFAULT_WL51_DTD_LOCATION = "/weblogic/ejb/deployment/xml/weblogic-ejb-jar.dtd";
074: protected static final String DEFAULT_WL60_51_DTD_LOCATION = "/weblogic/ejb20/dd/xml/weblogic510-ejb-jar.dtd";
075: protected static final String DEFAULT_WL60_DTD_LOCATION = "/weblogic/ejb20/dd/xml/weblogic600-ejb-jar.dtd";
076: protected static final String DEFAULT_WL70_DTD_LOCATION = "/weblogic/ejb20/dd/xml/weblogic700-ejb-jar.dtd";
077:
078: protected static final String DEFAULT_COMPILER = "default";
079:
080: protected static final String WL_DD = "weblogic-ejb-jar.xml";
081: protected static final String WL_CMP_DD = "weblogic-cmp-rdbms-jar.xml";
082:
083: protected static final String COMPILER_EJB11 = "weblogic.ejbc";
084: protected static final String COMPILER_EJB20 = "weblogic.ejbc20";
085:
086: /** File utilities instance for copying jars */
087: private static final FileUtils FILE_UTILS = FileUtils
088: .getFileUtils();
089:
090: /** Instance variable that stores the suffix for the weblogic jarfile. */
091: private String jarSuffix = ".jar";
092:
093: /** Instance variable that stores the location of the weblogic DTD file. */
094: private String weblogicDTD;
095:
096: /** Instance variable that stores the location of the ejb 1.1 DTD file. */
097: private String ejb11DTD;
098:
099: /** Instance variable that determines whether generic ejb jars are kept. */
100: private boolean keepgenerated = false;
101:
102: /**
103: * Instance variable that stores the fully qualified classname of the
104: * weblogic EJBC compiler
105: */
106: private String ejbcClass = null;
107:
108: private String additionalArgs = "";
109:
110: /**
111: * additional args to pass to the spawned jvm
112: */
113: private String additionalJvmArgs = "";
114:
115: private boolean keepGeneric = false;
116:
117: private String compiler = null;
118:
119: private boolean alwaysRebuild = true;
120:
121: /** controls whether ejbc is run on the generated jar */
122: private boolean noEJBC = false;
123:
124: /** Indicates if the old CMP location convention is to be used. */
125: private boolean newCMP = false;
126:
127: /** The classpath to the weblogic classes. */
128: private Path wlClasspath = null;
129:
130: /** System properties for the JVM. */
131: private Vector sysprops = new Vector();
132:
133: /**
134: * The weblogic.StdoutSeverityLevel to use when running the JVM that
135: * executes ejbc. Set to 16 to avoid the warnings about EJB Home and
136: * Remotes being in the classpath
137: */
138: private Integer jvmDebugLevel = null;
139:
140: private File outputDir;
141:
142: /**
143: * Add a nested sysproperty element.
144: * @param sysp the element to add.
145: */
146: public void addSysproperty(Environment.Variable sysp) {
147: sysprops.add(sysp);
148: }
149:
150: /**
151: * Get the classpath to the weblogic classpaths.
152: * @return the classpath to configure.
153: */
154: public Path createWLClasspath() {
155: if (wlClasspath == null) {
156: wlClasspath = new Path(getTask().getProject());
157: }
158: return wlClasspath.createPath();
159: }
160:
161: /**
162: * If set ejbc will use this directory as the output
163: * destination rather than a jar file. This allows for the
164: * generation of "exploded" jars.
165: * @param outputDir the directory to be used.
166: */
167: public void setOutputDir(File outputDir) {
168: this .outputDir = outputDir;
169: }
170:
171: /**
172: * Optional classpath to WL6.0.
173: * Weblogic 6.0 will give a warning if the home and remote interfaces
174: * of a bean are on the system classpath used to run weblogic.ejbc.
175: * In that case, the standard weblogic classes should be set with
176: * this attribute (or equivalent nested element) and the
177: * home and remote interfaces located with the standard classpath
178: * attribute.
179: * @param wlClasspath the path to be used.
180: */
181: public void setWLClasspath(Path wlClasspath) {
182: this .wlClasspath = wlClasspath;
183: }
184:
185: /**
186: * The compiler (switch <code>-compiler</code>) to use; optional.
187: * This allows for the selection of a different compiler
188: * to be used for the compilation of the generated Java
189: * files. This could be set, for example, to Jikes to
190: * compile with the Jikes compiler. If this is not set
191: * and the <code>build.compiler</code> property is set
192: * to jikes, the Jikes compiler will be used. If this
193: * is not desired, the value "<code>default</code>"
194: * may be given to use the default compiler.
195: * @param compiler the compiler to be used.
196: */
197: public void setCompiler(String compiler) {
198: this .compiler = compiler;
199: }
200:
201: /**
202: * Set the rebuild flag to false to only update changes in the jar rather
203: * than rerunning ejbc; optional, default true.
204: * This flag controls whether weblogic.ejbc is always
205: * invoked to build the jar file. In certain circumstances,
206: * such as when only a bean class has been changed, the jar
207: * can be generated by merely replacing the changed classes
208: * and not rerunning ejbc. Setting this to false will reduce
209: * the time to run ejbjar.
210: * @param rebuild a <code>boolean</code> value.
211: */
212: public void setRebuild(boolean rebuild) {
213: this .alwaysRebuild = rebuild;
214: }
215:
216: /**
217: * Sets the weblogic.StdoutSeverityLevel to use when running the JVM that
218: * executes ejbc; optional. Set to 16 to avoid the warnings about EJB Home and
219: * Remotes being in the classpath
220: * @param jvmDebugLevel the value to use.
221: */
222: public void setJvmDebugLevel(Integer jvmDebugLevel) {
223: this .jvmDebugLevel = jvmDebugLevel;
224: }
225:
226: /**
227: * Get the debug level.
228: * @return the jvm debug level (may be null).
229: */
230: public Integer getJvmDebugLevel() {
231: return jvmDebugLevel;
232: }
233:
234: /**
235: * Setter used to store the suffix for the generated weblogic jar file.
236: *
237: * @param inString the string to use as the suffix.
238: */
239: public void setSuffix(String inString) {
240: this .jarSuffix = inString;
241: }
242:
243: /**
244: * controls whether the generic file used as input to
245: * ejbc is retained; defaults to false
246: *
247: * @param inValue true for keep generic
248: */
249: public void setKeepgeneric(boolean inValue) {
250: this .keepGeneric = inValue;
251: }
252:
253: /**
254: * Controls whether weblogic will keep the generated Java
255: * files used to build the class files added to the
256: * jar. This can be useful when debugging; default is false.
257: *
258: * @param inValue either 'true' or 'false'
259: */
260: public void setKeepgenerated(String inValue) {
261: this .keepgenerated = Boolean.valueOf(inValue).booleanValue();
262: }
263:
264: /**
265: * Any optional extra arguments pass to the weblogic.ejbc
266: * tool.
267: * @param args extra arguments to pass to the ejbc tool.
268: */
269: public void setArgs(String args) {
270: this .additionalArgs = args;
271: }
272:
273: /**
274: * Set any additional arguments to pass to the weblogic JVM; optional.
275: * @param args the arguments to be passed to the JVM
276: */
277: public void setJvmargs(String args) {
278: this .additionalJvmArgs = args;
279: }
280:
281: /**
282: * Set the classname of the ejbc compiler; optional
283: * Normally ejbjar determines
284: * the appropriate class based on the DTD used for the EJB. The EJB 2.0 compiler
285: * featured in weblogic 6 has, however, been deprecated in version 7. When
286: * using with version 7 this attribute should be set to
287: * "weblogic.ejbc" to avoid the deprecation warning.
288: * @param ejbcClass the name of the class to use.
289: */
290: public void setEjbcClass(String ejbcClass) {
291: this .ejbcClass = ejbcClass;
292: }
293:
294: /**
295: * Get the ejbc compiler class.
296: * @return the name of the ejbc compiler class.
297: */
298: public String getEjbcClass() {
299: return ejbcClass;
300: }
301:
302: /**
303: * <b>Deprecated</b>. Defines the location of the ejb-jar DTD in
304: * the weblogic class hierarchy. Should not be needed, and the
305: * nested <dtd> element is recommended when it is.
306: *
307: * @param inString the string to use as the DTD location.
308: */
309: public void setWeblogicdtd(String inString) {
310: setEJBdtd(inString);
311: }
312:
313: /**
314: * <b>Deprecated</b>. Defines the location of weblogic DTD in
315: * the weblogic class hierarchy. Should not be needed, and the
316: * nested <dtd> element is recommended when it is.
317: *
318: * @param inString the string to use as the DTD location.
319: */
320: public void setWLdtd(String inString) {
321: this .weblogicDTD = inString;
322: }
323:
324: /**
325: * <b>Deprecated</b>. Defines the location of Sun's EJB DTD in
326: * the weblogic class hierarchy. Should not be needed, and the
327: * nested <dtd> element is recommended when it is.
328: *
329: * @param inString the string to use as the DTD location.
330: */
331: public void setEJBdtd(String inString) {
332: this .ejb11DTD = inString;
333: }
334:
335: /**
336: * Set the value of the oldCMP scheme. This is an antonym for newCMP
337: * @ant.attribute ignore="true'
338: * @param oldCMP a <code>boolean</code> value.
339: */
340: public void setOldCMP(boolean oldCMP) {
341: this .newCMP = !oldCMP;
342: }
343:
344: /**
345: * If this is set to true, the new method for locating
346: * CMP descriptors will be used; optional, default false.
347: * <P>
348: * The old CMP scheme locates the
349: * weblogic CMP descriptor based on the naming convention where the
350: * weblogic CMP file is expected to be named with the bean name as the
351: * prefix. Under this scheme the name of the CMP descriptor does not match
352: * the name actually used in the main weblogic EJB descriptor. Also,
353: * descriptors which contain multiple CMP references could not be used.
354: * @param newCMP a <code>boolean</code> value.
355: */
356: public void setNewCMP(boolean newCMP) {
357: this .newCMP = newCMP;
358: }
359:
360: /**
361: * Do not EJBC the jar after it has been put together;
362: * optional, default false
363: * @param noEJBC a <code>boolean</code> value.
364: */
365: public void setNoEJBC(boolean noEJBC) {
366: this .noEJBC = noEJBC;
367: }
368:
369: /**
370: * Register the DTDs.
371: * @param handler the handler to use.
372: */
373: protected void registerKnownDTDs(DescriptorHandler handler) {
374: // register all the known DTDs
375: handler.registerDTD(PUBLICID_EJB11,
376: DEFAULT_WL51_EJB11_DTD_LOCATION);
377: handler.registerDTD(PUBLICID_EJB11,
378: DEFAULT_WL60_EJB11_DTD_LOCATION);
379: handler.registerDTD(PUBLICID_EJB11, ejb11DTD);
380: handler.registerDTD(PUBLICID_EJB20,
381: DEFAULT_WL60_EJB20_DTD_LOCATION);
382: }
383:
384: /**
385: * Get the weblogic descriptor handler.
386: * @param srcDir the source directory.
387: * @return the descriptor.
388: */
389: protected DescriptorHandler getWeblogicDescriptorHandler(
390: final File srcDir) {
391: DescriptorHandler handler = new DescriptorHandler(getTask(),
392: srcDir) {
393: protected void processElement() {
394: if (currentElement.equals("type-storage")) {
395: // Get the filename of vendor specific descriptor
396: String fileNameWithMETA = currentText;
397: //trim the META_INF\ off of the file name
398: String fileName = fileNameWithMETA.substring(
399: META_DIR.length(), fileNameWithMETA
400: .length());
401: File descriptorFile = new File(srcDir, fileName);
402:
403: ejbFiles.put(fileNameWithMETA, descriptorFile);
404: }
405: }
406: };
407:
408: handler.registerDTD(PUBLICID_WEBLOGIC_EJB510,
409: DEFAULT_WL51_DTD_LOCATION);
410: handler.registerDTD(PUBLICID_WEBLOGIC_EJB510,
411: DEFAULT_WL60_51_DTD_LOCATION);
412: handler.registerDTD(PUBLICID_WEBLOGIC_EJB600,
413: DEFAULT_WL60_DTD_LOCATION);
414: handler.registerDTD(PUBLICID_WEBLOGIC_EJB700,
415: DEFAULT_WL70_DTD_LOCATION);
416: handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, weblogicDTD);
417: handler.registerDTD(PUBLICID_WEBLOGIC_EJB600, weblogicDTD);
418:
419: for (Iterator i = getConfig().dtdLocations.iterator(); i
420: .hasNext();) {
421: EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i
422: .next();
423:
424: handler.registerDTD(dtdLocation.getPublicId(), dtdLocation
425: .getLocation());
426: }
427: return handler;
428: }
429:
430: /**
431: * Add any vendor specific files which should be included in the EJB Jar.
432: * @param ejbFiles the hash table to be populated.
433: * @param ddPrefix the prefix to use.
434: */
435: protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
436: File weblogicDD = new File(getConfig().descriptorDir, ddPrefix
437: + WL_DD);
438:
439: if (weblogicDD.exists()) {
440: ejbFiles.put(META_DIR + WL_DD, weblogicDD);
441: } else {
442: log("Unable to locate weblogic deployment descriptor. "
443: + "It was expected to be in "
444: + weblogicDD.getPath(), Project.MSG_WARN);
445: return;
446: }
447:
448: if (!newCMP) {
449: log(
450: "The old method for locating CMP files has been DEPRECATED.",
451: Project.MSG_VERBOSE);
452: log("Please adjust your weblogic descriptor and set "
453: + "newCMP=\"true\" to use the new CMP descriptor "
454: + "inclusion mechanism. ", Project.MSG_VERBOSE);
455: // The the weblogic cmp deployment descriptor
456: File weblogicCMPDD = new File(getConfig().descriptorDir,
457: ddPrefix + WL_CMP_DD);
458:
459: if (weblogicCMPDD.exists()) {
460: ejbFiles.put(META_DIR + WL_CMP_DD, weblogicCMPDD);
461: }
462: } else {
463: // now that we have the weblogic descriptor, we parse the file
464: // to find other descriptors needed to deploy the bean.
465: // this could be the weblogic-cmp-rdbms.xml or any other O/R
466: // mapping tool descriptors.
467: try {
468: File ejbDescriptor = (File) ejbFiles.get(META_DIR
469: + EJB_DD);
470: SAXParserFactory saxParserFactory = SAXParserFactory
471: .newInstance();
472:
473: saxParserFactory.setValidating(true);
474:
475: SAXParser saxParser = saxParserFactory.newSAXParser();
476: DescriptorHandler handler = getWeblogicDescriptorHandler(ejbDescriptor
477: .getParentFile());
478:
479: saxParser.parse(new InputSource(new FileInputStream(
480: weblogicDD)), handler);
481:
482: Hashtable ht = handler.getFiles();
483: Enumeration e = ht.keys();
484:
485: while (e.hasMoreElements()) {
486: String key = (String) e.nextElement();
487:
488: ejbFiles.put(key, ht.get(key));
489: }
490: } catch (Exception e) {
491: String msg = "Exception while adding Vendor specific files: "
492: + e.toString();
493:
494: throw new BuildException(msg, e);
495: }
496: }
497: }
498:
499: /**
500: * Get the vendor specific name of the Jar that will be output. The
501: * modification date of this jar will be checked against the dependent
502: * bean classes.
503: */
504: File getVendorOutputJarFile(String baseName) {
505: return new File(getDestDir(), baseName + jarSuffix);
506: }
507:
508: /**
509: * Helper method invoked by execute() for each WebLogic jar to be built.
510: * Encapsulates the logic of constructing a java task for calling
511: * weblogic.ejbc and executing it.
512: *
513: * @param sourceJar java.io.File representing the source (EJB1.1) jarfile.
514: * @param destJar java.io.File representing the destination, WebLogic
515: * jarfile.
516: */
517: private void buildWeblogicJar(File sourceJar, File destJar,
518: String publicId) {
519: Java javaTask = null;
520:
521: if (noEJBC) {
522: try {
523: FILE_UTILS.copyFile(sourceJar, destJar);
524: if (!keepgenerated) {
525: sourceJar.delete();
526: }
527: return;
528: } catch (IOException e) {
529: throw new BuildException("Unable to write EJB jar", e);
530: }
531: }
532:
533: String ejbcClassName = ejbcClass;
534:
535: try {
536: javaTask = new Java(getTask());
537: javaTask.setTaskName("ejbc");
538:
539: javaTask.createJvmarg().setLine(additionalJvmArgs);
540: if (!(sysprops.isEmpty())) {
541: for (Enumeration en = sysprops.elements(); en
542: .hasMoreElements();) {
543: Environment.Variable entry = (Environment.Variable) en
544: .nextElement();
545: javaTask.addSysproperty(entry);
546: }
547: }
548:
549: if (getJvmDebugLevel() != null) {
550: javaTask.createJvmarg().setLine(
551: " -Dweblogic.StdoutSeverityLevel="
552: + jvmDebugLevel);
553: }
554:
555: if (ejbcClassName == null) {
556: // try to determine it from publicId
557: if (PUBLICID_EJB11.equals(publicId)) {
558: ejbcClassName = COMPILER_EJB11;
559: } else if (PUBLICID_EJB20.equals(publicId)) {
560: ejbcClassName = COMPILER_EJB20;
561: } else {
562: log("Unrecognized publicId " + publicId
563: + " - using EJB 1.1 compiler",
564: Project.MSG_WARN);
565: ejbcClassName = COMPILER_EJB11;
566: }
567: }
568:
569: javaTask.setClassname(ejbcClassName);
570: javaTask.createArg().setLine(additionalArgs);
571: if (keepgenerated) {
572: javaTask.createArg().setValue("-keepgenerated");
573: }
574: if (compiler == null) {
575: // try to use the compiler specified by build.compiler.
576: // Right now we are just going to allow Jikes
577: String buildCompiler = getTask().getProject()
578: .getProperty("build.compiler");
579:
580: if (buildCompiler != null
581: && buildCompiler.equals("jikes")) {
582: javaTask.createArg().setValue("-compiler");
583: javaTask.createArg().setValue("jikes");
584: }
585: } else {
586: if (!compiler.equals(DEFAULT_COMPILER)) {
587: javaTask.createArg().setValue("-compiler");
588: javaTask.createArg().setLine(compiler);
589: }
590: }
591:
592: Path combinedClasspath = getCombinedClasspath();
593: if (wlClasspath != null && combinedClasspath != null
594: && combinedClasspath.toString().trim().length() > 0) {
595: javaTask.createArg().setValue("-classpath");
596: javaTask.createArg().setPath(combinedClasspath);
597: }
598:
599: javaTask.createArg().setValue(sourceJar.getPath());
600: if (outputDir == null) {
601: javaTask.createArg().setValue(destJar.getPath());
602: } else {
603: javaTask.createArg().setValue(outputDir.getPath());
604: }
605:
606: Path classpath = wlClasspath;
607:
608: if (classpath == null) {
609: classpath = getCombinedClasspath();
610: }
611:
612: javaTask.setFork(true);
613: if (classpath != null) {
614: javaTask.setClasspath(classpath);
615: }
616:
617: log("Calling " + ejbcClassName + " for "
618: + sourceJar.toString(), Project.MSG_VERBOSE);
619:
620: if (javaTask.executeJava() != 0) {
621: throw new BuildException("Ejbc reported an error");
622: }
623: } catch (Exception e) {
624: // Have to catch this because of the semantics of calling main()
625: String msg = "Exception while calling " + ejbcClassName
626: + ". Details: " + e.toString();
627:
628: throw new BuildException(msg, e);
629: }
630: }
631:
632: /**
633: * Method used to encapsulate the writing of the JAR file. Iterates over
634: * the filenames/java.io.Files in the Hashtable stored on the instance
635: * variable ejbFiles.
636: * @param baseName the base name.
637: * @param jarFile the jar file to populate.
638: * @param files the hash table of files to write.
639: * @param publicId the id to use.
640: * @throws BuildException if there is a problem.
641: */
642: protected void writeJar(String baseName, File jarFile,
643: Hashtable files, String publicId) throws BuildException {
644: // need to create a generic jar first.
645: File genericJarFile = super .getVendorOutputJarFile(baseName);
646:
647: super .writeJar(baseName, genericJarFile, files, publicId);
648:
649: if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) {
650: buildWeblogicJar(genericJarFile, jarFile, publicId);
651: }
652: if (!keepGeneric) {
653: log("deleting generic jar " + genericJarFile.toString(),
654: Project.MSG_VERBOSE);
655: genericJarFile.delete();
656: }
657: }
658:
659: /**
660: * Called to validate that the tool parameters have been configured.
661: * @throws BuildException if there is an error.
662: */
663: public void validateConfigured() throws BuildException {
664: super .validateConfigured();
665: }
666:
667: /**
668: * Helper method to check to see if a weblogic EBJ1.1 jar needs to be
669: * rebuilt using ejbc. Called from writeJar it sees if the "Bean" classes
670: * are the only thing that needs to be updated and either updates the Jar
671: * with the Bean classfile or returns true, saying that the whole weblogic
672: * jar needs to be regened with ejbc. This allows faster build times for
673: * working developers. <p>
674: *
675: * The way weblogic ejbc works is it creates wrappers for the publicly
676: * defined methods as they are exposed in the remote interface. If the
677: * actual bean changes without changing the the method signatures then
678: * only the bean classfile needs to be updated and the rest of the
679: * weblogic jar file can remain the same. If the Interfaces, ie. the
680: * method signatures change or if the xml deployment descriptors changed,
681: * the whole jar needs to be rebuilt with ejbc. This is not strictly true
682: * for the xml files. If the JNDI name changes then the jar doesnt have to
683: * be rebuild, but if the resources references change then it does. At
684: * this point the weblogic jar gets rebuilt if the xml files change at
685: * all.
686: *
687: * @param genericJarFile java.io.File The generic jar file.
688: * @param weblogicJarFile java.io.File The weblogic jar file to check to
689: * see if it needs to be rebuilt.
690: * @return true if the jar needs to be rebuilt.
691: */
692: protected boolean isRebuildRequired(File genericJarFile,
693: File weblogicJarFile) {
694: boolean rebuild = false;
695:
696: JarFile genericJar = null;
697: JarFile wlJar = null;
698: File newWLJarFile = null;
699: JarOutputStream newJarStream = null;
700: ClassLoader genericLoader = null;
701:
702: try {
703: log("Checking if weblogic Jar needs to be rebuilt for jar "
704: + weblogicJarFile.getName(), Project.MSG_VERBOSE);
705: // Only go forward if the generic and the weblogic file both exist
706: if (genericJarFile.exists() && genericJarFile.isFile()
707: && weblogicJarFile.exists()
708: && weblogicJarFile.isFile()) {
709: //open jar files
710: genericJar = new JarFile(genericJarFile);
711: wlJar = new JarFile(weblogicJarFile);
712:
713: Hashtable genericEntries = new Hashtable();
714: Hashtable wlEntries = new Hashtable();
715: Hashtable replaceEntries = new Hashtable();
716:
717: //get the list of generic jar entries
718: for (Enumeration e = genericJar.entries(); e
719: .hasMoreElements();) {
720: JarEntry je = (JarEntry) e.nextElement();
721:
722: genericEntries.put(je.getName().replace('\\', '/'),
723: je);
724: }
725: //get the list of weblogic jar entries
726: for (Enumeration e = wlJar.entries(); e
727: .hasMoreElements();) {
728: JarEntry je = (JarEntry) e.nextElement();
729:
730: wlEntries.put(je.getName(), je);
731: }
732:
733: //Cycle Through generic and make sure its in weblogic
734: genericLoader = getClassLoaderFromJar(genericJarFile);
735:
736: for (Enumeration e = genericEntries.keys(); e
737: .hasMoreElements();) {
738: String filepath = (String) e.nextElement();
739:
740: if (wlEntries.containsKey(filepath)) {
741: // File name/path match
742:
743: // Check files see if same
744: JarEntry genericEntry = (JarEntry) genericEntries
745: .get(filepath);
746: JarEntry wlEntry = (JarEntry) wlEntries
747: .get(filepath);
748:
749: if ((genericEntry.getCrc() != wlEntry.getCrc())
750: || (genericEntry.getSize() != wlEntry
751: .getSize())) {
752:
753: if (genericEntry.getName().endsWith(
754: ".class")) {
755: //File are different see if its an object or an interface
756: String classname = genericEntry
757: .getName()
758: .replace(File.separatorChar,
759: '.');
760:
761: classname = classname
762: .substring(0, classname
763: .lastIndexOf(".class"));
764:
765: Class genclass = genericLoader
766: .loadClass(classname);
767:
768: if (genclass.isInterface()) {
769: //Interface changed rebuild jar.
770: log("Interface "
771: + genclass.getName()
772: + " has changed",
773: Project.MSG_VERBOSE);
774: rebuild = true;
775: break;
776: } else {
777: //Object class Changed update it.
778: replaceEntries.put(filepath,
779: genericEntry);
780: }
781: } else {
782: // is it the manifest. If so ignore it
783: if (!genericEntry.getName().equals(
784: "META-INF/MANIFEST.MF")) {
785: //File other then class changed rebuild
786: log("Non class file "
787: + genericEntry.getName()
788: + " has changed",
789: Project.MSG_VERBOSE);
790: rebuild = true;
791: break;
792: }
793: }
794: }
795: } else {
796: // a file doesnt exist rebuild
797:
798: log("File " + filepath
799: + " not present in weblogic jar",
800: Project.MSG_VERBOSE);
801: rebuild = true;
802: break;
803: }
804: }
805:
806: if (!rebuild) {
807: log("No rebuild needed - updating jar",
808: Project.MSG_VERBOSE);
809: newWLJarFile = new File(weblogicJarFile
810: .getAbsolutePath()
811: + ".temp");
812: if (newWLJarFile.exists()) {
813: newWLJarFile.delete();
814: }
815:
816: newJarStream = new JarOutputStream(
817: new FileOutputStream(newWLJarFile));
818: newJarStream.setLevel(0);
819:
820: //Copy files from old weblogic jar
821: for (Enumeration e = wlEntries.elements(); e
822: .hasMoreElements();) {
823: byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
824: int bytesRead;
825: InputStream is;
826: JarEntry je = (JarEntry) e.nextElement();
827:
828: if (je.getCompressedSize() == -1
829: || je.getCompressedSize() == je
830: .getSize()) {
831: newJarStream.setLevel(0);
832: } else {
833: newJarStream.setLevel(JAR_COMPRESS_LEVEL);
834: }
835:
836: // Update with changed Bean class
837: if (replaceEntries.containsKey(je.getName())) {
838: log("Updating Bean class from generic Jar "
839: + je.getName(), Project.MSG_VERBOSE);
840: // Use the entry from the generic jar
841: je = (JarEntry) replaceEntries.get(je
842: .getName());
843: is = genericJar.getInputStream(je);
844: } else {
845: //use fle from original weblogic jar
846:
847: is = wlJar.getInputStream(je);
848: }
849: newJarStream.putNextEntry(new JarEntry(je
850: .getName()));
851:
852: while ((bytesRead = is.read(buffer)) != -1) {
853: newJarStream.write(buffer, 0, bytesRead);
854: }
855: is.close();
856: }
857: } else {
858: log("Weblogic Jar rebuild needed due to changed "
859: + "interface or XML", Project.MSG_VERBOSE);
860: }
861: } else {
862: rebuild = true;
863: }
864: } catch (ClassNotFoundException cnfe) {
865: String cnfmsg = "ClassNotFoundException while processing ejb-jar file"
866: + ". Details: " + cnfe.getMessage();
867:
868: throw new BuildException(cnfmsg, cnfe);
869: } catch (IOException ioe) {
870: String msg = "IOException while processing ejb-jar file "
871: + ". Details: " + ioe.getMessage();
872:
873: throw new BuildException(msg, ioe);
874: } finally {
875: // need to close files and perhaps rename output
876: if (genericJar != null) {
877: try {
878: genericJar.close();
879: } catch (IOException closeException) {
880: // empty
881: }
882: }
883:
884: if (wlJar != null) {
885: try {
886: wlJar.close();
887: } catch (IOException closeException) {
888: // empty
889: }
890: }
891:
892: if (newJarStream != null) {
893: try {
894: newJarStream.close();
895: } catch (IOException closeException) {
896: // empty
897: }
898:
899: try {
900: FILE_UTILS.rename(newWLJarFile, weblogicJarFile);
901: } catch (IOException renameException) {
902: log(renameException.getMessage(), Project.MSG_WARN);
903: rebuild = true;
904: }
905: }
906: if (genericLoader != null
907: && genericLoader instanceof AntClassLoader) {
908: AntClassLoader loader = (AntClassLoader) genericLoader;
909: loader.cleanup();
910: }
911: }
912:
913: return rebuild;
914: }
915:
916: /**
917: * Helper method invoked by isRebuildRequired to get a ClassLoader for a
918: * Jar File passed to it.
919: *
920: * @param classjar java.io.File representing jar file to get classes from.
921: * @return the classloader for the jarfile.
922: * @throws IOException if there is a problem.
923: */
924: protected ClassLoader getClassLoaderFromJar(File classjar)
925: throws IOException {
926: Path lookupPath = new Path(getTask().getProject());
927:
928: lookupPath.setLocation(classjar);
929:
930: Path classpath = getCombinedClasspath();
931:
932: if (classpath != null) {
933: lookupPath.append(classpath);
934: }
935:
936: return getTask().getProject().createClassLoader(lookupPath);
937: }
938: }
|