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.FileOutputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.util.Enumeration;
025: import java.util.Hashtable;
026: import java.util.Iterator;
027: import java.util.jar.JarEntry;
028: import java.util.jar.JarFile;
029: import java.util.jar.JarOutputStream;
030: import org.apache.tools.ant.BuildException;
031: import org.apache.tools.ant.Project;
032: import org.apache.tools.ant.taskdefs.Java;
033: import org.apache.tools.ant.types.Environment;
034: import org.apache.tools.ant.types.Path;
035: import org.apache.tools.ant.util.FileUtils;
036:
037: /**
038: * Websphere deployment tool that augments the ejbjar task.
039: * Searches for the websphere specific deployment descriptors and
040: * adds them to the final ejb jar file. Websphere has two specific descriptors for session
041: * beans:
042: * <ul>
043: * <li>ibm-ejb-jar-bnd.xmi</li>
044: * <li>ibm-ejb-jar-ext.xmi</li>
045: * </ul>
046: * and another two for container managed entity beans:
047: * <ul>
048: * <li>Map.mapxmi</li>
049: * <li>Schema.dbxmi</li>
050: * </ul>
051: * In terms of WebSphere, the generation of container code and stubs is
052: * called <code>deployment</code>. This step can be performed by the websphere
053: * element as part of the jar generation process. If the switch
054: * <code>ejbdeploy</code> is on, the ejbdeploy tool from the websphere toolset
055: * is called for every ejb-jar. Unfortunately, this step only works, if you
056: * use the ibm jdk. Otherwise, the rmic (called by ejbdeploy) throws a
057: * ClassFormatError. Be sure to switch ejbdeploy off, if run ant with
058: * sun jdk.
059: *
060: */
061: public class WebsphereDeploymentTool extends GenericDeploymentTool {
062:
063: /** ID for ejb 1.1 */
064: public static final String PUBLICID_EJB11 = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
065: /** ID for ejb 2.0 */
066: public static final String PUBLICID_EJB20 = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
067: /** Schema directory */
068: protected static final String SCHEMA_DIR = "Schema/";
069:
070: protected static final String WAS_EXT = "ibm-ejb-jar-ext.xmi";
071: protected static final String WAS_BND = "ibm-ejb-jar-bnd.xmi";
072: protected static final String WAS_CMP_MAP = "Map.mapxmi";
073: protected static final String WAS_CMP_SCHEMA = "Schema.dbxmi";
074:
075: private static final FileUtils FILE_UTILS = FileUtils
076: .getFileUtils();
077:
078: /** Instance variable that stores the suffix for the websphere jarfile. */
079: private String jarSuffix = ".jar";
080:
081: /** Instance variable that stores the location of the ejb 1.1 DTD file. */
082: private String ejb11DTD;
083:
084: /** Instance variable that determines whether generic ejb jars are kept. */
085:
086: private boolean keepGeneric = false;
087:
088: private boolean alwaysRebuild = true;
089:
090: private boolean ejbdeploy = true;
091:
092: /** Indicates if the old CMP location convention is to be used. */
093: private boolean newCMP = false;
094:
095: /** The classpath to the websphere classes. */
096: private Path wasClasspath = null;
097:
098: /** The DB Vendor name, the EJB is persisted against */
099: private String dbVendor;
100:
101: /** The name of the database to create. (For top-down mapping only) */
102: private String dbName;
103:
104: /** The name of the schema to create. (For top-down mappings only) */
105: private String dbSchema;
106:
107: /** true - Only generate the deployment code, do not run RMIC or Javac */
108: private boolean codegen;
109:
110: /** true - Only output error messages, suppress informational messages */
111: private boolean quiet = true;
112:
113: /** true - Disable the validation steps */
114: private boolean novalidate;
115:
116: /** true - Disable warning and informational messages */
117: private boolean nowarn;
118:
119: /** true - Disable informational messages */
120: private boolean noinform;
121:
122: /** true - Enable internal tracing */
123: private boolean trace;
124:
125: /** Additional options for RMIC */
126: private String rmicOptions;
127:
128: /** true- Use the WebSphere 3.5 compatible mapping rules */
129: private boolean use35MappingRules;
130:
131: /** the scratchdir for the ejbdeploy operation */
132: private String tempdir = "_ejbdeploy_temp";
133:
134: /** the home directory for websphere */
135: private File websphereHome;
136:
137: /**
138: * Get the classpath to the websphere classpaths.
139: * @return the websphere classpath.
140: */
141: public Path createWASClasspath() {
142: if (wasClasspath == null) {
143: wasClasspath = new Path(getTask().getProject());
144: }
145: return wasClasspath.createPath();
146: }
147:
148: /**
149: * Set the websphere classpath.
150: * @param wasClasspath the websphere classpath.
151: */
152: public void setWASClasspath(Path wasClasspath) {
153: this .wasClasspath = wasClasspath;
154: }
155:
156: /** Sets the DB Vendor for the Entity Bean mapping ; optional.
157: * <p>
158: * Valid options can be obtained by running the following command:
159: * <code>
160: * <WAS_HOME>/bin/EJBDeploy.[sh/bat] -help
161: * </code>
162: * </p>
163: * <p>
164: * This is also used to determine the name of the Map.mapxmi and
165: * Schema.dbxmi files, for example Account-DB2UDB_V81-Map.mapxmi
166: * and Account-DB2UDB_V81-Schema.dbxmi.
167: * </p>
168: *
169: * @param dbvendor database vendor type
170: */
171: public void setDbvendor(String dbvendor) {
172: this .dbVendor = dbvendor;
173: }
174:
175: /**
176: * Sets the name of the Database to create; optional.
177: *
178: * @param dbName name of the database
179: */
180: public void setDbname(String dbName) {
181: this .dbName = dbName;
182: }
183:
184: /**
185: * Sets the name of the schema to create; optional.
186: *
187: * @param dbSchema name of the schema
188: */
189: public void setDbschema(String dbSchema) {
190: this .dbSchema = dbSchema;
191: }
192:
193: /**
194: * Flag, default false, to only generate the deployment
195: * code, do not run RMIC or Javac
196: *
197: * @param codegen option
198: */
199: public void setCodegen(boolean codegen) {
200: this .codegen = codegen;
201: }
202:
203: /**
204: * Flag, default true, to only output error messages.
205: *
206: * @param quiet option
207: */
208: public void setQuiet(boolean quiet) {
209: this .quiet = quiet;
210: }
211:
212: /**
213: * Flag to disable the validation steps; optional, default false.
214: *
215: * @param novalidate option
216: */
217: public void setNovalidate(boolean novalidate) {
218: this .novalidate = novalidate;
219: }
220:
221: /**
222: * Flag to disable warning and informational messages; optional, default false.
223: *
224: * @param nowarn option
225: */
226: public void setNowarn(boolean nowarn) {
227: this .nowarn = nowarn;
228: }
229:
230: /**
231: * Flag to disable informational messages; optional, default false.
232: *
233: * @param noinform if true disables informational messages
234: */
235: public void setNoinform(boolean noinform) {
236: this .noinform = noinform;
237: }
238:
239: /**
240: * Flag to enable internal tracing when set, optional, default false.
241: *
242: * @param trace a <code>boolean</code> vaule.
243: */
244: public void setTrace(boolean trace) {
245: this .trace = trace;
246: }
247:
248: /**
249: * Set the rmic options.
250: *
251: * @param options the options to use.
252: */
253: public void setRmicoptions(String options) {
254: this .rmicOptions = options;
255: }
256:
257: /**
258: * Flag to use the WebSphere 3.5 compatible mapping rules ; optional, default false.
259: *
260: * @param attr a <code>boolean</code> value.
261: */
262: public void setUse35(boolean attr) {
263: use35MappingRules = attr;
264: }
265:
266: /**
267: * Set the rebuild flag to false to only update changes in the jar rather
268: * than rerunning ejbdeploy; optional, default true.
269: * @param rebuild a <code>boolean</code> value.
270: */
271: public void setRebuild(boolean rebuild) {
272: this .alwaysRebuild = rebuild;
273: }
274:
275: /**
276: * String value appended to the basename of the deployment
277: * descriptor to create the filename of the WebLogic EJB
278: * jar file. Optional, default '.jar'.
279: * @param inString the string to use as the suffix.
280: */
281: public void setSuffix(String inString) {
282: this .jarSuffix = inString;
283: }
284:
285: /**
286: * This controls whether the generic file used as input to
287: * ejbdeploy is retained; optional, default false.
288: * @param inValue either 'true' or 'false'.
289: */
290: public void setKeepgeneric(boolean inValue) {
291: this .keepGeneric = inValue;
292: }
293:
294: /**
295: * Decide, wether ejbdeploy should be called or not;
296: * optional, default true.
297: *
298: * @param ejbdeploy a <code>boolean</code> value.
299: */
300: public void setEjbdeploy(boolean ejbdeploy) {
301: this .ejbdeploy = ejbdeploy;
302: }
303:
304: /**
305: * Setter used to store the location of the Sun's Generic EJB DTD. This
306: * can be a file on the system or a resource on the classpath.
307: *
308: * @param inString the string to use as the DTD location.
309: */
310: public void setEJBdtd(String inString) {
311: this .ejb11DTD = inString;
312: }
313:
314: /**
315: * Set the value of the oldCMP scheme. This is an antonym for newCMP
316: * @ant.attribute ignore="true"
317: * @param oldCMP a <code>boolean</code> value.
318: */
319: public void setOldCMP(boolean oldCMP) {
320: this .newCMP = !oldCMP;
321: }
322:
323: /**
324: * Set the value of the newCMP scheme. The old CMP scheme locates the
325: * websphere CMP descriptor based on the naming convention where the
326: * websphere CMP file is expected to be named with the bean name as the
327: * prefix. Under this scheme the name of the CMP descriptor does not match
328: * the name actually used in the main websphere EJB descriptor. Also,
329: * descriptors which contain multiple CMP references could not be used.
330: * @param newCMP a <code>boolean</code> value.
331: */
332: public void setNewCMP(boolean newCMP) {
333: this .newCMP = newCMP;
334: }
335:
336: /**
337: * The directory, where ejbdeploy will write temporary files;
338: * optional, defaults to '_ejbdeploy_temp'.
339: * @param tempdir the directory name to use.
340: */
341: public void setTempdir(String tempdir) {
342: this .tempdir = tempdir;
343: }
344:
345: /** {@inheritDoc}. */
346: protected DescriptorHandler getDescriptorHandler(File srcDir) {
347: DescriptorHandler handler = new DescriptorHandler(getTask(),
348: srcDir);
349: // register all the DTDs, both the ones that are known and
350: // any supplied by the user
351: handler.registerDTD(PUBLICID_EJB11, ejb11DTD);
352:
353: for (Iterator i = getConfig().dtdLocations.iterator(); i
354: .hasNext();) {
355: EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i
356: .next();
357:
358: handler.registerDTD(dtdLocation.getPublicId(), dtdLocation
359: .getLocation());
360: }
361:
362: return handler;
363: }
364:
365: /**
366: * Get a description handler.
367: * @param srcDir the source directory.
368: * @return the handler.
369: */
370: protected DescriptorHandler getWebsphereDescriptorHandler(
371: final File srcDir) {
372: DescriptorHandler handler = new DescriptorHandler(getTask(),
373: srcDir) {
374: protected void processElement() {
375: }
376: };
377:
378: for (Iterator i = getConfig().dtdLocations.iterator(); i
379: .hasNext();) {
380: EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i
381: .next();
382:
383: handler.registerDTD(dtdLocation.getPublicId(), dtdLocation
384: .getLocation());
385: }
386: return handler;
387: }
388:
389: /**
390: * Add any vendor specific files which should be included in the EJB Jar.
391: * @param ejbFiles a hashtable entryname -> file.
392: * @param baseName a prefix to use.
393: */
394: protected void addVendorFiles(Hashtable ejbFiles, String baseName) {
395:
396: String ddPrefix = (usingBaseJarName() ? "" : baseName);
397: String dbPrefix = (dbVendor == null) ? "" : dbVendor + "-";
398:
399: // Get the Extensions document
400: File websphereEXT = new File(getConfig().descriptorDir,
401: ddPrefix + WAS_EXT);
402:
403: if (websphereEXT.exists()) {
404: ejbFiles.put(META_DIR + WAS_EXT, websphereEXT);
405: } else {
406: log("Unable to locate websphere extensions. "
407: + "It was expected to be in "
408: + websphereEXT.getPath(), Project.MSG_VERBOSE);
409: }
410:
411: File websphereBND = new File(getConfig().descriptorDir,
412: ddPrefix + WAS_BND);
413:
414: if (websphereBND.exists()) {
415: ejbFiles.put(META_DIR + WAS_BND, websphereBND);
416: } else {
417: log("Unable to locate websphere bindings. "
418: + "It was expected to be in "
419: + websphereBND.getPath(), Project.MSG_VERBOSE);
420: }
421:
422: if (!newCMP) {
423: log(
424: "The old method for locating CMP files has been DEPRECATED.",
425: Project.MSG_VERBOSE);
426: log("Please adjust your websphere descriptor and set "
427: + "newCMP=\"true\" to use the new CMP descriptor "
428: + "inclusion mechanism. ", Project.MSG_VERBOSE);
429: } else {
430: // We attempt to put in the MAP and Schema files of CMP beans
431: try {
432: // Add the Map file
433: File websphereMAP = new File(getConfig().descriptorDir,
434: ddPrefix + dbPrefix + WAS_CMP_MAP);
435:
436: if (websphereMAP.exists()) {
437: ejbFiles.put(META_DIR + WAS_CMP_MAP, websphereMAP);
438: } else {
439: log("Unable to locate the websphere Map: "
440: + websphereMAP.getPath(),
441: Project.MSG_VERBOSE);
442: }
443:
444: File websphereSchema = new File(
445: getConfig().descriptorDir, ddPrefix + dbPrefix
446: + WAS_CMP_SCHEMA);
447:
448: if (websphereSchema.exists()) {
449: ejbFiles.put(
450: META_DIR + SCHEMA_DIR + WAS_CMP_SCHEMA,
451: websphereSchema);
452: } else {
453: log("Unable to locate the websphere Schema: "
454: + websphereSchema.getPath(),
455: Project.MSG_VERBOSE);
456: }
457: // Theres nothing else to see here...keep moving sonny
458: } catch (Exception e) {
459: String msg = "Exception while adding Vendor specific files: "
460: + e.toString();
461:
462: throw new BuildException(msg, e);
463: }
464: }
465: }
466:
467: /**
468: * Get the vendor specific name of the Jar that will be output. The
469: * modification date of this jar will be checked against the dependent
470: * bean classes.
471: */
472: File getVendorOutputJarFile(String baseName) {
473: return new File(getDestDir(), baseName + jarSuffix);
474: }
475:
476: /**
477: * Gets the options for the EJB Deploy operation
478: *
479: * @return String
480: */
481: protected String getOptions() {
482: // Set the options
483: StringBuffer options = new StringBuffer();
484:
485: if (dbVendor != null) {
486: options.append(" -dbvendor ").append(dbVendor);
487: }
488: if (dbName != null) {
489: options.append(" -dbname \"").append(dbName).append("\"");
490: }
491:
492: if (dbSchema != null) {
493: options.append(" -dbschema \"").append(dbSchema).append(
494: "\"");
495: }
496:
497: if (codegen) {
498: options.append(" -codegen");
499: }
500:
501: if (quiet) {
502: options.append(" -quiet");
503: }
504:
505: if (novalidate) {
506: options.append(" -novalidate");
507: }
508:
509: if (nowarn) {
510: options.append(" -nowarn");
511: }
512:
513: if (noinform) {
514: options.append(" -noinform");
515: }
516:
517: if (trace) {
518: options.append(" -trace");
519: }
520:
521: if (use35MappingRules) {
522: options.append(" -35");
523: }
524:
525: if (rmicOptions != null) {
526: options.append(" -rmic \"").append(rmicOptions)
527: .append("\"");
528: }
529:
530: return options.toString();
531: }
532:
533: /**
534: * Helper method invoked by execute() for each websphere jar to be built.
535: * Encapsulates the logic of constructing a java task for calling
536: * websphere.ejbdeploy and executing it.
537: *
538: * @param sourceJar java.io.File representing the source (EJB1.1) jarfile.
539: * @param destJar java.io.File representing the destination, websphere
540: * jarfile.
541: */
542: private void buildWebsphereJar(File sourceJar, File destJar) {
543: try {
544: if (ejbdeploy) {
545: Java javaTask = new Java(getTask());
546: // Set the JvmArgs
547: javaTask.createJvmarg().setValue("-Xms64m");
548: javaTask.createJvmarg().setValue("-Xmx128m");
549:
550: // Set the Environment variable
551: Environment.Variable var = new Environment.Variable();
552:
553: var.setKey("websphere.lib.dir");
554: File libdir = new File(websphereHome, "lib");
555: var.setValue(libdir.getAbsolutePath());
556: javaTask.addSysproperty(var);
557:
558: // Set the working directory
559: javaTask.setDir(websphereHome);
560:
561: // Set the Java class name
562: javaTask.setTaskName("ejbdeploy");
563: javaTask
564: .setClassname("com.ibm.etools.ejbdeploy.EJBDeploy");
565:
566: javaTask.createArg().setValue(sourceJar.getPath());
567: javaTask.createArg().setValue(tempdir);
568: javaTask.createArg().setValue(destJar.getPath());
569: javaTask.createArg().setLine(getOptions());
570: if (getCombinedClasspath() != null
571: && getCombinedClasspath().toString().length() > 0) {
572: javaTask.createArg().setValue("-cp");
573: javaTask.createArg().setValue(
574: getCombinedClasspath().toString());
575: }
576:
577: Path classpath = wasClasspath;
578:
579: if (classpath == null) {
580: classpath = getCombinedClasspath();
581: }
582:
583: if (classpath != null) {
584: javaTask.setClasspath(classpath);
585: javaTask.setFork(true);
586: } else {
587: javaTask.setFork(true);
588: }
589:
590: log("Calling websphere.ejbdeploy for "
591: + sourceJar.toString(), Project.MSG_VERBOSE);
592:
593: javaTask.execute();
594: }
595: } catch (Exception e) {
596: // Have to catch this because of the semantics of calling main()
597: String msg = "Exception while calling ejbdeploy. Details: "
598: + e.toString();
599:
600: throw new BuildException(msg, e);
601: }
602: }
603:
604: /** {@inheritDoc}. */
605: protected void writeJar(String baseName, File jarFile,
606: Hashtable files, String publicId) throws BuildException {
607: if (ejbdeploy) {
608: // create the -generic.jar, if required
609: File genericJarFile = super
610: .getVendorOutputJarFile(baseName);
611:
612: super .writeJar(baseName, genericJarFile, files, publicId);
613:
614: // create the output .jar, if required
615: if (alwaysRebuild
616: || isRebuildRequired(genericJarFile, jarFile)) {
617: buildWebsphereJar(genericJarFile, jarFile);
618: }
619: if (!keepGeneric) {
620: log(
621: "deleting generic jar "
622: + genericJarFile.toString(),
623: Project.MSG_VERBOSE);
624: genericJarFile.delete();
625: }
626: } else {
627: // create the "undeployed" output .jar, if required
628: super .writeJar(baseName, jarFile, files, publicId);
629: }
630: }
631:
632: /**
633: * Called to validate that the tool parameters have been configured.
634: * @throws BuildException if there is an error.
635: */
636: public void validateConfigured() throws BuildException {
637: super .validateConfigured();
638: if (ejbdeploy) {
639: String home = getTask().getProject().getProperty(
640: "websphere.home");
641: if (home == null) {
642: throw new BuildException(
643: "The 'websphere.home' property must "
644: + "be set when 'ejbdeploy=true'");
645: }
646: websphereHome = getTask().getProject().resolveFile(home);
647: }
648: }
649:
650: /**
651: * Helper method to check to see if a websphere EBJ1.1 jar needs to be
652: * rebuilt using ejbdeploy. Called from writeJar it sees if the "Bean"
653: * classes are the only thing that needs to be updated and either updates
654: * the Jar with the Bean classfile or returns true, saying that the whole
655: * websphere jar needs to be regened with ejbdeploy. This allows faster
656: * build times for working developers. <p>
657: *
658: * The way websphere ejbdeploy works is it creates wrappers for the
659: * publicly defined methods as they are exposed in the remote interface.
660: * If the actual bean changes without changing the the method signatures
661: * then only the bean classfile needs to be updated and the rest of the
662: * websphere jar file can remain the same. If the Interfaces, ie. the
663: * method signatures change or if the xml deployment descriptors changed,
664: * the whole jar needs to be rebuilt with ejbdeploy. This is not strictly
665: * true for the xml files. If the JNDI name changes then the jar doesnt
666: * have to be rebuild, but if the resources references change then it
667: * does. At this point the websphere jar gets rebuilt if the xml files
668: * change at all.
669: *
670: * @param genericJarFile java.io.File The generic jar file.
671: * @param websphereJarFile java.io.File The websphere jar file to check to
672: * see if it needs to be rebuilt.
673: * @return true if a rebuild is required.
674: */
675: protected boolean isRebuildRequired(File genericJarFile,
676: File websphereJarFile) {
677: boolean rebuild = false;
678:
679: JarFile genericJar = null;
680: JarFile wasJar = null;
681: File newwasJarFile = null;
682: JarOutputStream newJarStream = null;
683:
684: try {
685: log(
686: "Checking if websphere Jar needs to be rebuilt for jar "
687: + websphereJarFile.getName(),
688: Project.MSG_VERBOSE);
689: // Only go forward if the generic and the websphere file both exist
690: if (genericJarFile.exists() && genericJarFile.isFile()
691: && websphereJarFile.exists()
692: && websphereJarFile.isFile()) {
693: //open jar files
694: genericJar = new JarFile(genericJarFile);
695: wasJar = new JarFile(websphereJarFile);
696:
697: Hashtable genericEntries = new Hashtable();
698: Hashtable wasEntries = new Hashtable();
699: Hashtable replaceEntries = new Hashtable();
700:
701: //get the list of generic jar entries
702: for (Enumeration e = genericJar.entries(); e
703: .hasMoreElements();) {
704: JarEntry je = (JarEntry) e.nextElement();
705:
706: genericEntries.put(je.getName().replace('\\', '/'),
707: je);
708: }
709: //get the list of websphere jar entries
710: for (Enumeration e = wasJar.entries(); e
711: .hasMoreElements();) {
712: JarEntry je = (JarEntry) e.nextElement();
713:
714: wasEntries.put(je.getName(), je);
715: }
716:
717: //Cycle Through generic and make sure its in websphere
718: ClassLoader genericLoader = getClassLoaderFromJar(genericJarFile);
719:
720: for (Enumeration e = genericEntries.keys(); e
721: .hasMoreElements();) {
722: String filepath = (String) e.nextElement();
723:
724: if (wasEntries.containsKey(filepath)) {
725: // File name/path match
726: // Check files see if same
727: JarEntry genericEntry = (JarEntry) genericEntries
728: .get(filepath);
729: JarEntry wasEntry = (JarEntry) wasEntries
730: .get(filepath);
731:
732: if ((genericEntry.getCrc() != wasEntry.getCrc())
733: || (genericEntry.getSize() != wasEntry
734: .getSize())) {
735:
736: if (genericEntry.getName().endsWith(
737: ".class")) {
738: //File are different see if its an object or an interface
739: String classname = genericEntry
740: .getName()
741: .replace(File.separatorChar,
742: '.');
743:
744: classname = classname
745: .substring(0, classname
746: .lastIndexOf(".class"));
747:
748: Class genclass = genericLoader
749: .loadClass(classname);
750:
751: if (genclass.isInterface()) {
752: //Interface changed rebuild jar.
753: log("Interface "
754: + genclass.getName()
755: + " has changed",
756: Project.MSG_VERBOSE);
757: rebuild = true;
758: break;
759: } else {
760: //Object class Changed update it.
761: replaceEntries.put(filepath,
762: genericEntry);
763: }
764: } else {
765: // is it the manifest. If so ignore it
766: if (!genericEntry.getName().equals(
767: "META-INF/MANIFEST.MF")) {
768: //File other then class changed rebuild
769: log("Non class file "
770: + genericEntry.getName()
771: + " has changed",
772: Project.MSG_VERBOSE);
773: rebuild = true;
774: }
775: break;
776: }
777: }
778: } else {
779: // a file doesn't exist rebuild
780:
781: log("File " + filepath
782: + " not present in websphere jar",
783: Project.MSG_VERBOSE);
784: rebuild = true;
785: break;
786: }
787: }
788:
789: if (!rebuild) {
790: log("No rebuild needed - updating jar",
791: Project.MSG_VERBOSE);
792: newwasJarFile = new File(websphereJarFile
793: .getAbsolutePath()
794: + ".temp");
795: if (newwasJarFile.exists()) {
796: newwasJarFile.delete();
797: }
798:
799: newJarStream = new JarOutputStream(
800: new FileOutputStream(newwasJarFile));
801: newJarStream.setLevel(0);
802:
803: //Copy files from old websphere jar
804: for (Enumeration e = wasEntries.elements(); e
805: .hasMoreElements();) {
806: byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
807: int bytesRead;
808: InputStream is;
809: JarEntry je = (JarEntry) e.nextElement();
810:
811: if (je.getCompressedSize() == -1
812: || je.getCompressedSize() == je
813: .getSize()) {
814: newJarStream.setLevel(0);
815: } else {
816: newJarStream.setLevel(JAR_COMPRESS_LEVEL);
817: }
818:
819: // Update with changed Bean class
820: if (replaceEntries.containsKey(je.getName())) {
821: log("Updating Bean class from generic Jar "
822: + je.getName(), Project.MSG_VERBOSE);
823: // Use the entry from the generic jar
824: je = (JarEntry) replaceEntries.get(je
825: .getName());
826: is = genericJar.getInputStream(je);
827: } else {
828: //use fle from original websphere jar
829:
830: is = wasJar.getInputStream(je);
831: }
832: newJarStream.putNextEntry(new JarEntry(je
833: .getName()));
834:
835: while ((bytesRead = is.read(buffer)) != -1) {
836: newJarStream.write(buffer, 0, bytesRead);
837: }
838: is.close();
839: }
840: } else {
841: log("websphere Jar rebuild needed due to changed "
842: + "interface or XML", Project.MSG_VERBOSE);
843: }
844: } else {
845: rebuild = true;
846: }
847: } catch (ClassNotFoundException cnfe) {
848: String cnfmsg = "ClassNotFoundException while processing ejb-jar file"
849: + ". Details: " + cnfe.getMessage();
850:
851: throw new BuildException(cnfmsg, cnfe);
852: } catch (IOException ioe) {
853: String msg = "IOException while processing ejb-jar file "
854: + ". Details: " + ioe.getMessage();
855:
856: throw new BuildException(msg, ioe);
857: } finally {
858: // need to close files and perhaps rename output
859: if (genericJar != null) {
860: try {
861: genericJar.close();
862: } catch (IOException closeException) {
863: // Ignore
864: }
865: }
866:
867: if (wasJar != null) {
868: try {
869: wasJar.close();
870: } catch (IOException closeException) {
871: // Ignore
872: }
873: }
874:
875: if (newJarStream != null) {
876: try {
877: newJarStream.close();
878: } catch (IOException closeException) {
879: // Ignore
880: }
881:
882: try {
883: FILE_UTILS.rename(newwasJarFile, websphereJarFile);
884: } catch (IOException renameException) {
885: log(renameException.getMessage(), Project.MSG_WARN);
886: rebuild = true;
887: }
888: }
889: }
890:
891: return rebuild;
892: }
893:
894: /**
895: * Helper method invoked by isRebuildRequired to get a ClassLoader for a
896: * Jar File passed to it.
897: *
898: * @param classjar java.io.File representing jar file to get classes from.
899: * @return a classloader for the jar file.
900: * @throws IOException if there is an error.
901: */
902: protected ClassLoader getClassLoaderFromJar(File classjar)
903: throws IOException {
904: Path lookupPath = new Path(getTask().getProject());
905:
906: lookupPath.setLocation(classjar);
907:
908: Path classpath = getCombinedClasspath();
909:
910: if (classpath != null) {
911: lookupPath.append(classpath);
912: }
913:
914: return getTask().getProject().createClassLoader(lookupPath);
915: }
916: }
|