KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > jdepend > JDependTask


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant.taskdefs.optional.jdepend;
20
21 import java.io.File JavaDoc;
22 import java.io.FileWriter JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.PrintWriter JavaDoc;
25 import java.lang.reflect.Constructor JavaDoc;
26 import java.lang.reflect.Method JavaDoc;
27 import java.util.Vector JavaDoc;
28 import java.util.Enumeration JavaDoc;
29 import org.apache.tools.ant.BuildException;
30 import org.apache.tools.ant.Project;
31 import org.apache.tools.ant.Task;
32 import org.apache.tools.ant.taskdefs.Execute;
33 import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
34 import org.apache.tools.ant.taskdefs.LogStreamHandler;
35 import org.apache.tools.ant.types.Commandline;
36 import org.apache.tools.ant.types.CommandlineJava;
37 import org.apache.tools.ant.types.EnumeratedAttribute;
38 import org.apache.tools.ant.types.Path;
39 import org.apache.tools.ant.types.PatternSet;
40 import org.apache.tools.ant.types.Reference;
41 import org.apache.tools.ant.util.FileUtils;
42 import org.apache.tools.ant.util.LoaderUtils;
43
44 /**
45  * Runs JDepend tests.
46  *
47  * <p>JDepend is a tool to generate design quality metrics for each Java package.
48  * It has been initially created by Mike Clark. JDepend can be found at <a
49  * HREF="http://www.clarkware.com/software/JDepend.html">http://www.clarkware.com/software/JDepend.html</a>.
50  *
51  * The current implementation spawn a new Java VM.
52  *
53  */

54 public class JDependTask extends Task {
55     //private CommandlineJava commandline = new CommandlineJava();
56

57     // required attributes
58
private Path sourcesPath; // Deprecated!
59
private Path classesPath; // Use this going forward
60

61     // optional attributes
62
private File JavaDoc outputFile;
63     private File JavaDoc dir;
64     private Path compileClasspath;
65     private boolean haltonerror = false;
66     private boolean fork = false;
67     private Long JavaDoc timeout = null;
68
69     private String JavaDoc jvm = null;
70     private String JavaDoc format = "text";
71     private PatternSet defaultPatterns = new PatternSet();
72
73     private static Constructor JavaDoc packageFilterC;
74     private static Method JavaDoc setFilter;
75
76     private boolean includeRuntime = false;
77     private Path runtimeClasses = null;
78
79     static {
80         try {
81             Class JavaDoc packageFilter =
82                 Class.forName("jdepend.framework.PackageFilter");
83             packageFilterC =
84                 packageFilter.getConstructor(new Class JavaDoc[] {java.util.Collection JavaDoc.class});
85             setFilter =
86                 jdepend.textui.JDepend.class.getDeclaredMethod("setFilter",
87                                                                new Class JavaDoc[] {packageFilter});
88         } catch (Throwable JavaDoc t) {
89             if (setFilter == null) {
90                 packageFilterC = null;
91             }
92         }
93     }
94
95     /**
96      * If true,
97      * include jdepend.jar in the forked VM.
98      *
99      * @param b include ant run time yes or no
100      * @since Ant 1.6
101      */

102     public void setIncluderuntime(boolean b) {
103         includeRuntime = b;
104     }
105
106     /**
107      * Set the timeout value (in milliseconds).
108      *
109      * <p>If the operation is running for more than this value, the jdepend
110      * will be canceled. (works only when in 'fork' mode).</p>
111      * @param value the maximum time (in milliseconds) allowed before
112      * declaring the test as 'timed-out'
113      * @see #setFork(boolean)
114      */

115     public void setTimeout(Long JavaDoc value) {
116         timeout = value;
117     }
118
119     /**
120      * @return the timeout value
121      */

122     public Long JavaDoc getTimeout() {
123         return timeout;
124     }
125
126     /**
127      * The output file name.
128      *
129      * @param outputFile the output file name
130      */

131     public void setOutputFile(File JavaDoc outputFile) {
132         this.outputFile = outputFile;
133     }
134
135     /**
136      * @return the output file name
137      */

138     public File JavaDoc getOutputFile() {
139         return outputFile;
140     }
141
142     /**
143      * Whether or not to halt on failure. Default: false.
144      * @param haltonerror the value to set
145      */

146     public void setHaltonerror(boolean haltonerror) {
147         this.haltonerror = haltonerror;
148     }
149
150     /**
151      * @return the value of the haltonerror attribute
152      */

153     public boolean getHaltonerror() {
154         return haltonerror;
155     }
156
157     /**
158      * If true, forks into a new JVM. Default: false.
159      *
160      * @param value <tt>true</tt> if a JVM should be forked,
161      * otherwise <tt>false<tt>
162      */

163     public void setFork(boolean value) {
164         fork = value;
165     }
166
167     /**
168      * @return the value of the fork attribute
169      */

170     public boolean getFork() {
171         return fork;
172     }
173
174     /**
175      * The command used to invoke a forked Java Virtual Machine.
176      *
177      * Default is <tt>java</tt>. Ignored if no JVM is forked.
178      * @param value the new VM to use instead of <tt>java</tt>
179      * @see #setFork(boolean)
180      */

181     public void setJvm(String JavaDoc value) {
182         jvm = value;
183
184     }
185
186     /**
187      * Adds a path to source code to analyze.
188      * @return a source path
189      * @deprecated since 1.6.x.
190      */

191     public Path createSourcespath() {
192         if (sourcesPath == null) {
193             sourcesPath = new Path(getProject());
194         }
195         return sourcesPath.createPath();
196     }
197
198     /**
199      * Gets the sourcepath.
200      * @return the sources path
201      * @deprecated since 1.6.x.
202      */

203     public Path getSourcespath() {
204         return sourcesPath;
205     }
206
207     /**
208      * Adds a path to class code to analyze.
209      * @return a classes path
210      */

211     public Path createClassespath() {
212         if (classesPath == null) {
213             classesPath = new Path(getProject());
214         }
215         return classesPath.createPath();
216     }
217
218     /**
219      * Gets the classespath.
220      * @return the classes path
221      */

222     public Path getClassespath() {
223         return classesPath;
224     }
225
226     /**
227      * The directory to invoke the VM in. Ignored if no JVM is forked.
228      * @param dir the directory to invoke the JVM from.
229      * @see #setFork(boolean)
230      */

231     public void setDir(File JavaDoc dir) {
232         this.dir = dir;
233     }
234
235     /**
236      * @return the dir attribute
237      */

238     public File JavaDoc getDir() {
239         return dir;
240     }
241
242     /**
243      * Set the classpath to be used for this compilation.
244      * @param classpath a class path to be used
245      */

246     public void setClasspath(Path classpath) {
247         if (compileClasspath == null) {
248             compileClasspath = classpath;
249         } else {
250             compileClasspath.append(classpath);
251         }
252     }
253
254     /**
255      * Gets the classpath to be used for this compilation.
256      * @return the class path used for compilation
257      */

258     public Path getClasspath() {
259         return compileClasspath;
260     }
261
262     /**
263      * Adds a path to the classpath.
264      * @return a classpath
265      */

266     public Path createClasspath() {
267         if (compileClasspath == null) {
268             compileClasspath = new Path(getProject());
269         }
270         return compileClasspath.createPath();
271     }
272
273     /**
274      * Create a new JVM argument. Ignored if no JVM is forked.
275      * @param commandline the commandline to create the argument on
276      * @return create a new JVM argument so that any argument can
277      * be passed to the JVM.
278      * @see #setFork(boolean)
279      */

280     public Commandline.Argument createJvmarg(CommandlineJava commandline) {
281         return commandline.createVmArgument();
282     }
283
284     /**
285      * Adds a reference to a classpath defined elsewhere.
286      * @param r a classpath reference
287      */

288     public void setClasspathRef(Reference r) {
289         createClasspath().setRefid(r);
290     }
291
292     /**
293      * add a name entry on the exclude list
294      * @return a pattern for the excludes
295      */

296     public PatternSet.NameEntry createExclude() {
297         return defaultPatterns.createExclude();
298     }
299
300     /**
301      * @return the excludes patterns
302      */

303     public PatternSet getExcludes() {
304         return defaultPatterns;
305     }
306
307     /**
308      * The format to write the output in, "xml" or "text".
309      *
310      * @param ea xml or text
311      */

312     public void setFormat(FormatAttribute ea) {
313         format = ea.getValue();
314     }
315
316     /**
317      * A class for the enumerated attribute format,
318      * values are xml and text.
319      * @see EnumeratedAttribute
320      */

321     public static class FormatAttribute extends EnumeratedAttribute {
322         private String JavaDoc [] formats = new String JavaDoc[]{"xml", "text"};
323
324         /**
325          * @return the enumerated values
326          */

327         public String JavaDoc[] getValues() {
328             return formats;
329         }
330     }
331
332     /**
333      * No problems with this test.
334      */

335     private static final int SUCCESS = 0;
336     /**
337      * An error occurred.
338      */

339     private static final int ERRORS = 1;
340
341     /**
342      * Search for the given resource and add the directory or archive
343      * that contains it to the classpath.
344      *
345      * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
346      * getResource doesn't contain the name of the archive.</p>
347      *
348      * @param resource resource that one wants to lookup
349      * @since Ant 1.6
350      */

351     private void addClasspathEntry(String JavaDoc resource) {
352         /*
353          * pre Ant 1.6 this method used to call getClass().getResource
354          * while Ant 1.6 will call ClassLoader.getResource().
355          *
356          * The difference is that Class.getResource expects a leading
357          * slash for "absolute" resources and will strip it before
358          * delegating to ClassLoader.getResource - so we now have to
359          * emulate Class's behavior.
360          */

361         if (resource.startsWith("/")) {
362             resource = resource.substring(1);
363         } else {
364             resource = "org/apache/tools/ant/taskdefs/optional/jdepend/"
365                 + resource;
366         }
367
368         File JavaDoc f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
369                                                resource);
370         if (f != null) {
371             log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
372             runtimeClasses.createPath().setLocation(f);
373         } else {
374             log("Couldn\'t find " + resource, Project.MSG_DEBUG);
375         }
376     }
377
378     /**
379      * execute the task
380      *
381      * @exception BuildException if an error occurs
382      */

383     public void execute() throws BuildException {
384
385         CommandlineJava commandline = new CommandlineJava();
386
387         if ("text".equals(format)) {
388             commandline.setClassname("jdepend.textui.JDepend");
389         } else
390             if ("xml".equals(format)) {
391                 commandline.setClassname("jdepend.xmlui.JDepend");
392             }
393
394         if (jvm != null) {
395             commandline.setVm(jvm);
396         }
397         if (getSourcespath() == null && getClassespath() == null) {
398             throw new BuildException("Missing classespath required argument");
399         } else if (getClassespath() == null) {
400             String JavaDoc msg =
401                 "sourcespath is deprecated in JDepend >= 2.5 "
402                 + "- please convert to classespath";
403             log(msg);
404         }
405
406         // execute the test and get the return code
407
int exitValue = JDependTask.ERRORS;
408         boolean wasKilled = false;
409         if (!getFork()) {
410             exitValue = executeInVM(commandline);
411         } else {
412             ExecuteWatchdog watchdog = createWatchdog();
413             exitValue = executeAsForked(commandline, watchdog);
414             // null watchdog means no timeout, you'd better not check with null
415
if (watchdog != null) {
416                 wasKilled = watchdog.killedProcess();
417             }
418         }
419
420         // if there is an error/failure and that it should halt, stop
421
// everything otherwise just log a statement
422
boolean errorOccurred = exitValue == JDependTask.ERRORS || wasKilled;
423
424         if (errorOccurred) {
425             String JavaDoc errorMessage = "JDepend FAILED"
426                 + (wasKilled ? " - Timed out" : "");
427
428             if (getHaltonerror()) {
429                 throw new BuildException(errorMessage, getLocation());
430             } else {
431                 log(errorMessage, Project.MSG_ERR);
432             }
433         }
434     }
435
436     // this comment extract from JUnit Task may also apply here
437
// "in VM is not very nice since it could probably hang the
438
// whole build. IMHO this method should be avoided and it would be best
439
// to remove it in future versions. TBD. (SBa)"
440

441     /**
442      * Execute inside VM.
443      *
444      * @param commandline the command line
445      * @return the return value of the mvm
446      * @exception BuildException if an error occurs
447      */

448     public int executeInVM(CommandlineJava commandline) throws BuildException {
449         jdepend.textui.JDepend jdepend;
450
451         if ("xml".equals(format)) {
452             jdepend = new jdepend.xmlui.JDepend();
453         } else {
454             jdepend = new jdepend.textui.JDepend();
455         }
456
457         FileWriter JavaDoc fw = null;
458         if (getOutputFile() != null) {
459             try {
460                 fw = new FileWriter JavaDoc(getOutputFile().getPath());
461             } catch (IOException JavaDoc e) {
462                 String JavaDoc msg = "JDepend Failed when creating the output file: "
463                     + e.getMessage();
464                 log(msg);
465                 throw new BuildException(msg);
466             }
467             jdepend.setWriter(new PrintWriter JavaDoc(fw));
468             log("Output to be stored in " + getOutputFile().getPath());
469         }
470
471
472         try {
473             if (getClassespath() != null) {
474                 // This is the new, better way - use classespath instead
475
// of sourcespath. The code is currently the same - you
476
// need class files in a directory to use this or jar files.
477
String JavaDoc[] cP = getClassespath().list();
478                 for (int i = 0; i < cP.length; i++) {
479                     File JavaDoc f = new File JavaDoc(cP[i]);
480                     // not necessary as JDepend would fail, but why loose
481
// some time?
482
if (!f.exists()) {
483                         String JavaDoc msg = "\""
484                             + f.getPath()
485                             + "\" does not represent a valid"
486                             + " file or directory. JDepend would fail.";
487                         log(msg);
488                         throw new BuildException(msg);
489                     }
490                     try {
491                         jdepend.addDirectory(f.getPath());
492                     } catch (IOException JavaDoc e) {
493                         String JavaDoc msg =
494                             "JDepend Failed when adding a class directory: "
495                             + e.getMessage();
496                         log(msg);
497                         throw new BuildException(msg);
498                     }
499                 }
500
501             } else if (getSourcespath() != null) {
502
503                 // This is the old way and is deprecated - classespath is
504
// the right way to do this and is above
505
String JavaDoc[] sP = getSourcespath().list();
506                 for (int i = 0; i < sP.length; i++) {
507                     File JavaDoc f = new File JavaDoc(sP[i]);
508
509                     // not necessary as JDepend would fail, but why loose
510
// some time?
511
if (!f.exists() || !f.isDirectory()) {
512                         String JavaDoc msg = "\""
513                             + f.getPath()
514                             + "\" does not represent a valid"
515                             + " directory. JDepend would fail.";
516                         log(msg);
517                         throw new BuildException(msg);
518                     }
519                     try {
520                         jdepend.addDirectory(f.getPath());
521                     } catch (IOException JavaDoc e) {
522                         String JavaDoc msg =
523                             "JDepend Failed when adding a source directory: "
524                             + e.getMessage();
525                         log(msg);
526                         throw new BuildException(msg);
527                     }
528                 }
529             }
530
531             // This bit turns <exclude> child tags into patters to ignore
532
String JavaDoc[] patterns = defaultPatterns.getExcludePatterns(getProject());
533             if (patterns != null && patterns.length > 0) {
534                 if (setFilter != null) {
535                     Vector JavaDoc v = new Vector JavaDoc();
536                     for (int i = 0; i < patterns.length; i++) {
537                         v.addElement(patterns[i]);
538                     }
539                     try {
540                         Object JavaDoc o = packageFilterC.newInstance(new Object JavaDoc[] {v});
541                         setFilter.invoke(jdepend, new Object JavaDoc[] {o});
542                     } catch (Throwable JavaDoc e) {
543                         log("excludes will be ignored as JDepend doesn't like me: "
544                             + e.getMessage(), Project.MSG_WARN);
545                     }
546                 } else {
547                     log("Sorry, your version of JDepend doesn't support excludes",
548                         Project.MSG_WARN);
549                 }
550             }
551
552             jdepend.analyze();
553         } finally {
554             FileUtils.close(fw);
555         }
556         return SUCCESS;
557     }
558
559
560     /**
561      * Execute the task by forking a new JVM. The command will block until
562      * it finishes. To know if the process was destroyed or not, use the
563      * <tt>killedProcess()</tt> method of the watchdog class.
564      * @param commandline the commandline for forked jvm
565      * @param watchdog the watchdog in charge of cancelling the test if it
566      * exceeds a certain amount of time. Can be <tt>null</tt>.
567      * @return the result of running the jdepend
568      * @throws BuildException in case of error
569      */

570     // JL: comment extracted from JUnitTask (and slightly modified)
571
public int executeAsForked(CommandlineJava commandline,
572                                ExecuteWatchdog watchdog) throws BuildException {
573         runtimeClasses = new Path(getProject());
574         addClasspathEntry("/jdepend/textui/JDepend.class");
575
576         // if not set, auto-create the ClassPath from the project
577
createClasspath();
578
579         // not sure whether this test is needed but cost nothing to put.
580
// hope it will be reviewed by anybody competent
581
if (getClasspath().toString().length() > 0) {
582             createJvmarg(commandline).setValue("-classpath");
583             createJvmarg(commandline).setValue(getClasspath().toString());
584         }
585
586         if (includeRuntime) {
587             Vector JavaDoc v = Execute.getProcEnvironment();
588             Enumeration JavaDoc e = v.elements();
589             while (e.hasMoreElements()) {
590                 String JavaDoc s = (String JavaDoc) e.nextElement();
591                 if (s.startsWith("CLASSPATH=")) {
592                     commandline.createClasspath(getProject()).createPath()
593                         .append(new Path(getProject(),
594                                          s.substring("CLASSPATH=".length()
595                                                      )));
596                 }
597             }
598             log("Implicitly adding " + runtimeClasses + " to CLASSPATH",
599                 Project.MSG_VERBOSE);
600             commandline.createClasspath(getProject()).createPath()
601                 .append(runtimeClasses);
602         }
603
604         if (getOutputFile() != null) {
605             // having a space between the file and its path causes commandline
606
// to add quotes around the argument thus making JDepend not taking
607
// it into account. Thus we split it in two
608
commandline.createArgument().setValue("-file");
609             commandline.createArgument().setValue(outputFile.getPath());
610             // we have to find a cleaner way to put this output
611
}
612
613         if (getSourcespath() != null) {
614             // This is deprecated - use classespath in the future
615
String JavaDoc[] sP = getSourcespath().list();
616             for (int i = 0; i < sP.length; i++) {
617                 File JavaDoc f = new File JavaDoc(sP[i]);
618
619                 // not necessary as JDepend would fail, but why loose
620
// some time?
621
if (!f.exists() || !f.isDirectory()) {
622                     throw new BuildException("\"" + f.getPath()
623                                              + "\" does not represent a valid"
624                                              + " directory. JDepend would"
625                                              + " fail.");
626                 }
627                 commandline.createArgument().setValue(f.getPath());
628             }
629         }
630
631         if (getClassespath() != null) {
632             // This is the new way - use classespath - code is the
633
// same for now
634
String JavaDoc[] cP = getClassespath().list();
635             for (int i = 0; i < cP.length; i++) {
636                 File JavaDoc f = new File JavaDoc(cP[i]);
637                 // not necessary as JDepend would fail, but why loose
638
// some time?
639
if (!f.exists()) {
640                     throw new BuildException("\"" + f.getPath()
641                                              + "\" does not represent a valid"
642                                              + " file or directory. JDepend would"
643                                              + " fail.");
644                 }
645                 commandline.createArgument().setValue(f.getPath());
646             }
647         }
648
649         Execute execute = new Execute(new LogStreamHandler(this,
650             Project.MSG_INFO, Project.MSG_WARN), watchdog);
651         execute.setCommandline(commandline.getCommandline());
652         if (getDir() != null) {
653             execute.setWorkingDirectory(getDir());
654             execute.setAntRun(getProject());
655         }
656
657         if (getOutputFile() != null) {
658             log("Output to be stored in " + getOutputFile().getPath());
659         }
660         log(commandline.describeCommand(), Project.MSG_VERBOSE);
661         try {
662             return execute.execute();
663         } catch (IOException JavaDoc e) {
664             throw new BuildException("Process fork failed.", e, getLocation());
665         }
666     }
667
668     /**
669      * @return <tt>null</tt> if there is a timeout value, otherwise the
670      * watchdog instance.
671      * @throws BuildException in case of error
672      */

673     protected ExecuteWatchdog createWatchdog() throws BuildException {
674         if (getTimeout() == null) {
675             return null;
676         }
677         return new ExecuteWatchdog(getTimeout().longValue());
678     }
679 }
680
Popular Tags