View Javadoc

1   // Copyright (c) 2003, Chad Woolley, All rights reserved.
2   
3   package org.virtualmock.taskdef;
4   
5   import java.io.File;
6   import java.util.StringTokenizer;
7   import org.apache.tools.ant.BuildException;
8   import org.apache.tools.ant.Task;
9   import org.apache.tools.ant.taskdefs.Delete;
10  import org.apache.tools.ant.taskdefs.Java;
11  import org.apache.tools.ant.taskdefs.Zip;
12  import org.apache.tools.ant.types.Commandline.Argument;
13  import org.apache.tools.ant.types.CommandlineJava;
14  import org.apache.tools.ant.types.FileSet;
15  import org.apache.tools.ant.types.Path;
16  
17  
18  /***
19   * Task to perform transformation of class or jar files to be executed with
20   * VirtualMock.
21   *
22   * @author Chad Woolley
23   * @version $Revision: 1.18 $
24   */
25  public class VMTransformTask extends Task {
26      private CommandlineJava commandline = null;
27      private Path classpath = null;
28      private String aspectWerkzClassname =
29          "org.codehaus.aspectwerkz.compiler.AspectWerkzC";
30      private String aspectjClassname = "org.aspectj.tools.ajc.Main";
31      private String aspectpath = null;
32      private String aspectpathJarPrefix = "virtualmock-aspect-";
33      private String injars = null;
34      private String outjar = null;
35      private boolean useAspectJ = true;
36      private boolean useAspectWerkz = false;
37  
38      /***
39       * Creates a new VMTransformTask object.
40       */
41      public VMTransformTask() {
42          super();
43          this.setTaskName("vmtransform");
44          commandline = new CommandlineJava();
45      }
46  
47      /***
48       * Sets the classname for the AspectWerkz compiler.
49       *
50       * @param aspectWerkzClassname The aspectWerkzClassname to set.
51       */
52      public void setAspectWerkzClassname(String aspectWerkzClassname) {
53          this.aspectWerkzClassname = aspectWerkzClassname;
54      }
55  
56      /***
57       * Gets the classname for the AspectWerkz compiler.
58       *
59       * @return Returns the aspectWerkzClassname.
60       */
61      public String getAspectWerkzClassname() {
62          return aspectWerkzClassname;
63      }
64  
65      /***
66       * Sets the classname for the AspectJ compiler.
67       *
68       * @param aspectjClassname The aspectjClassname to set.
69       */
70      public void setAspectjClassname(String aspectjClassname) {
71          this.aspectjClassname = aspectjClassname;
72      }
73  
74      /***
75       * Gets the classname for the AspectJ compiler.
76       *
77       * @return Returns the aspectjClassname.
78       */
79      public String getAspectjClassname() {
80          return aspectjClassname;
81      }
82  
83      /***
84       * Sets jar containing VirtualMock aspect classes to weave.  If this is not
85       * specified, then the aspectpathJarPrefix will be used to search for the
86       * aspectjar on the classpath.
87       *
88       * @param aspectpath the aspectpath to set
89       */
90      public void setAspectpath(String aspectpath) {
91          this.aspectpath = aspectpath;
92      }
93  
94      /***
95       * Gets jar containing VirtualMock aspect classes to weave.  If this is not
96       * specified, then the aspectpathJarPrefix will be used to search for the
97       * aspectjar on the classpath.
98       *
99       * @return Returns the aspectpath.
100      */
101     public String getAspectpath() {
102         return aspectpath;
103     }
104 
105     /***
106      * Sets string which will be used to search the classpath for the
107      * aspectpath jar (using String.indexOf(String)).  If this is not
108      * specified, it will default to "virtualmock-aspect-".
109      *
110      * @param aspectpathJarPrefix the aspectpathJarPrefix to set
111      */
112     public void setAspectpathJarPrefix(String aspectpathJarPrefix) {
113         this.aspectpathJarPrefix = aspectpathJarPrefix;
114     }
115 
116     /***
117      * Gets string which will be used to search the classpath for the
118      * aspectpath jar (using String.indexOf(String)).  If this is not
119      * specified, it will default to "virtualmock-aspect-".
120      *
121      * @return Returns the aspectpathJarPrefix.
122      */
123     public String getAspectpathJarPrefix() {
124         return aspectpathJarPrefix;
125     }
126 
127     /***
128      * Sets the classpath to use.
129      *
130      * @param classpath The classpath to set.
131      */
132     public void setClasspath(Path classpath) {
133         this.classpath = classpath;
134     }
135 
136     /***
137      * Gets the currently set classpath.
138      *
139      * @return Returns the classpath.
140      */
141     public Path getClasspath() {
142         return classpath;
143     }
144 
145     /***
146      * Sets the command line.
147      *
148      * @param commandline The commandline to set.
149      */
150     public void setCommandline(CommandlineJava commandline) {
151         this.commandline = commandline;
152     }
153 
154     /***
155      * Gets the command line.
156      *
157      * @return Returns the commandline.
158      */
159     public CommandlineJava getCommandline() {
160         return commandline;
161     }
162 
163     /***
164      * Setter for the input jars.    Accept as source bytecode any .class files
165      * inside the specified .jar files. The output will include these classes,
166      * possibly as woven with any applicable aspects.  Like classpath, this is
167      * a single argument containing a list of paths to jar files, delimited by
168      * the platform- specific classpath delimiter.
169      *
170      * @param injars the injars to set
171      */
172     public void setInjars(String injars) {
173         this.injars = injars;
174     }
175 
176     /***
177      * The input jars which will be woven.
178      *
179      * @return Returns the injars.
180      */
181     public String getInjars() {
182         return injars;
183     }
184 
185     /***
186      * Sets the output jar.  This is the transformed jar which will be created
187      * by this task.
188      *
189      * @param outjar the outjar to set
190      */
191     public void setOutjar(String outjar) {
192         this.outjar = outjar;
193     }
194 
195     /***
196      * Gets the output jar.  This is the transformed jar which will be created
197      * by this task.
198      *
199      * @return Returns the outjar.
200      */
201     public String getOutjar() {
202         return outjar;
203     }
204 
205     /***
206      * Indicates whether AspectJ should be used as the Aspect-Oriented
207      * Programming (AOP) framework.
208      *
209      * @param useAspectJ flag indicating whether AspectJ should be used.
210      */
211     public void setUseAspectJ(boolean useAspectJ) {
212         this.useAspectJ = useAspectJ;
213         useAspectWerkz = !useAspectJ;
214     }
215 
216     /***
217      * Returns flag which indicates whether AspectJ should be used to weave.
218      *
219      * @return Returns the flag which indicates whether AspectJ should be used
220      *         to weave.
221      */
222     public boolean isUseAspectJ() {
223         return useAspectJ;
224     }
225 
226     /***
227      * Sets the flag which indicates whether AspectWerkz should be used to
228      * weave.
229      *
230      * @param useAspectWerkz flag indicating whether AspectWerkz should be used
231      *        to weave.
232      */
233     public void setUseAspectWerkz(boolean useAspectWerkz) {
234         this.useAspectWerkz = useAspectWerkz;
235         useAspectJ = !useAspectWerkz;
236     }
237 
238     /***
239      * Returns the flag which indicates whether AspectWerkz should be used to
240      * weave.
241      *
242      * @return Returns the flag indicating whether AspectWerkz should be used
243      *         to weave.
244      */
245     public boolean isUseAspectWerkz() {
246         return useAspectWerkz;
247     }
248 
249     /***
250      * Overridden method to handle classpath.  It will take the classpath that
251      * is currently specified, and  append all elements required to support
252      * the VirtualMock environment.
253      *
254      * @return the modified classpath
255      *
256      * @todo Do we need to add anything to the classpath?  If so, what?
257      * @todo What about forked mode?
258      */
259     public Path createClasspath() {
260         // get a local instance variable handle to the classpath
261         classpath = commandline.createClasspath(getProject()).createPath();
262 
263         return classpath;
264     }
265 
266     /***
267      * Perform execution of the task, first performing necessary setup to run
268      * VirtualMock unit tests.
269      *
270      * @throws BuildException DOCUMENT ME! (Constructor Exception)
271      */
272     public void execute() throws BuildException {
273         File outjarFile = new File(outjar);
274 
275         deleteOutjarFile(outjarFile);
276 
277         String className = null;
278         String args = null;
279 
280         if (useAspectJ) {
281             // set the class name
282             className = aspectjClassname;
283 
284             performAspectjPreProcessing();
285 
286             // define the command line args
287             args =
288                 "-outjar " + outjar + " -injars " + injars + " -aspectpath "
289                 + aspectpath;
290         }
291 
292         if (useAspectWerkz) {
293             // set the class name
294             className = aspectWerkzClassname;
295 
296             // define the command line args
297             args = "-haltOnError " + outjar;
298 
299             performAspectwerkzPreProcessing(outjarFile);
300         }
301 
302         // use the Ant Execute task to execute VirtualMock transformation
303         Java java = new Java();
304 
305         // use the VM launcher
306         java.setFork(false);
307 
308         // set the class name
309         java.setClassname(className);
310 
311         // fail on error
312         java.setFailonerror(true);
313 
314         Argument arg = java.createArg();
315         arg.setLine(args);
316 
317         java.setClasspath(classpath);
318 
319         // execute the command
320         int returnCode = -1;
321 
322         java.setProject(project);
323 
324         java.setFork(true);
325         returnCode = java.executeJava();
326 
327         if (returnCode != 0) {
328             throw new BuildException(
329                 "VirtualMock transformation failed, return code = "
330                 + returnCode + ".  Command executed was '" + className
331                 + "' with arguments '" + args + "'");
332         }
333     }
334 
335     /***
336      * If aspectpath was not set by user, search the classpath for the jar
337      * containing the virtualmock aspects and set it in the aspectpath
338      * variable.
339      */
340     protected void setDefaultAspectPath() {
341         String[] classpathElements = classpath.list();
342 
343         for (int i = 0; i < classpathElements.length; i++) {
344             if (classpathElements[i].indexOf(aspectpathJarPrefix) != -1) {
345                 aspectpath = classpathElements[i];
346             }
347         }
348     }
349 
350     /***
351      * Delete the output jar, if it exists.
352      *
353      * @param outjarFile the File object representing the outjar
354      */
355     protected void deleteOutjarFile(File outjarFile) {
356         // delete the output jar, if it exists
357         Delete delete = new Delete();
358         delete.setProject(project);
359 
360         delete.setFile(outjarFile);
361         delete.setFailOnError(false);
362         delete.execute();
363     }
364 
365     /***
366      * Performs preprocessing that is specific to AspectJ.
367      */
368     protected void performAspectjPreProcessing() {
369         // perform processing specific to AspectJ
370         if (aspectpath == null) {
371             setDefaultAspectPath();
372         }
373     }
374 
375     /***
376      * Performs preprocessing that is specific to AspectWerkz.
377      *
378      * @param outjarFile the File object represented by outjar
379      */
380     protected void performAspectwerkzPreProcessing(File outjarFile) {
381         // perform processing for AspectWerkz
382         // combine all injars into a single jar which will be transformed with AspectWerkz
383         Zip zip = new Zip();
384         zip.setProject(project);
385         zip.setDestFile(outjarFile);
386 
387         // get a tokenizer to split up the injars
388         StringTokenizer stringTokenizer =
389             new StringTokenizer(injars, File.pathSeparator);
390 
391         while (stringTokenizer.hasMoreTokens()) {
392             // split each injar into a dir and file, and add as an
393             // individual zipgroupfileset
394             String injarFileStringFull = stringTokenizer.nextToken();
395             File injarFile = new File(injarFileStringFull);
396             String parentDir = injarFile.getParent();
397             File parentDirFile = new File(parentDir);
398             String injarFileString =
399                 injarFileStringFull.substring(parentDir.length() + 1);
400 
401             FileSet fileSet = new FileSet();
402             fileSet.setIncludes(injarFileString);
403             fileSet.setDir(parentDirFile);
404             zip.addZipGroupFileset(fileSet);
405         }
406 
407         // don't add any duplicate files
408         Zip.Duplicate duplicate = new Zip.Duplicate();
409         duplicate.setValue("preserve");
410         zip.setDuplicate(duplicate);
411 
412         // make the zip
413         zip.execute();
414     }
415 }