1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional.metamata;
  18. import java.io.File;
  19. import java.io.FileWriter;
  20. import java.io.IOException;
  21. import java.io.PrintWriter;
  22. import java.util.Enumeration;
  23. import java.util.Hashtable;
  24. import java.util.Vector;
  25. import org.apache.tools.ant.BuildException;
  26. import org.apache.tools.ant.DirectoryScanner;
  27. import org.apache.tools.ant.Project;
  28. import org.apache.tools.ant.Task;
  29. import org.apache.tools.ant.taskdefs.Execute;
  30. import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
  31. import org.apache.tools.ant.types.Commandline;
  32. import org.apache.tools.ant.types.CommandlineJava;
  33. import org.apache.tools.ant.types.FileSet;
  34. import org.apache.tools.ant.types.Path;
  35. import org.apache.tools.ant.util.FileUtils;
  36. import org.apache.tools.ant.util.JavaEnvUtils;
  37. /**
  38. * Somewhat abstract framework to be used for other metama 2.0 tasks.
  39. * This should include, audit, metrics, cover and mparse.
  40. *
  41. * For more information, visit the website at
  42. * <a href="http://www.metamata.com">www.metamata.com</a>
  43. *
  44. */
  45. public abstract class AbstractMetamataTask extends Task {
  46. /**
  47. * The user classpath to be provided. It matches the -classpath of the
  48. * command line. The classpath must includes both the <tt>.class</tt> and the
  49. * <tt>.java</tt> files for accurate audit.
  50. */
  51. protected Path classPath = null;
  52. /** the path to the source file */
  53. protected Path sourcePath = null;
  54. /**
  55. * Metamata home directory. It will be passed as a <tt>metamata.home</tt> property
  56. * and should normally matches the environment property <tt>META_HOME</tt>
  57. * set by the Metamata installer.
  58. */
  59. protected File metamataHome = null;
  60. /** the command line used to run MAudit */
  61. protected CommandlineJava cmdl = new CommandlineJava();
  62. /** the set of files to be audited */
  63. protected Vector fileSets = new Vector();
  64. /** the options file where are stored the command line options */
  65. protected File optionsFile = null;
  66. // this is used to keep track of which files were included. It will
  67. // be set when calling scanFileSets();
  68. protected Hashtable includedFiles = null;
  69. public AbstractMetamataTask() {
  70. }
  71. /** initialize the task with the classname of the task to run */
  72. protected AbstractMetamataTask(String className) {
  73. cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
  74. cmdl.setClassname(className);
  75. }
  76. /**
  77. * the metamata.home property to run all tasks.
  78. * @ant.attribute ignore="true"
  79. */
  80. public void setHome(final File value) {
  81. this.metamataHome = value;
  82. }
  83. /**
  84. * The home directory containing the Metamata distribution; required
  85. */
  86. public void setMetamatahome(final File value) {
  87. setHome(value);
  88. }
  89. /**
  90. * Sets the class path (also source path unless one explicitly set).
  91. * Overrides METAPATH/CLASSPATH environment variables.
  92. */
  93. public Path createClasspath() {
  94. if (classPath == null) {
  95. classPath = new Path(getProject());
  96. }
  97. return classPath;
  98. }
  99. /**
  100. * Sets the source path.
  101. * Overrides the SOURCEPATH environment variable.
  102. */
  103. public Path createSourcepath() {
  104. if (sourcePath == null) {
  105. sourcePath = new Path(getProject());
  106. }
  107. return sourcePath;
  108. }
  109. /**
  110. * Additional optional parameters to pass to the JVM.
  111. * You can avoid using the <code><jvmarg></code> by adding these empty
  112. * entries to <code>metamata.properties</code> located at <code>${metamata.home}/bin</code>
  113. *
  114. * <pre>metamata.classpath=
  115. * metamata.sourcepath=
  116. * metamata.baseclasspath=
  117. * </pre>
  118. */
  119. public Commandline.Argument createJvmarg() {
  120. return cmdl.createVmArgument();
  121. }
  122. /**
  123. * Set the maximum memory for the JVM; optional.
  124. * -mx or -Xmx depending on VM version
  125. */
  126. public void setMaxmemory(String max) {
  127. cmdl.setMaxmemory(max);
  128. }
  129. /**
  130. * The java files or directory to audit.
  131. * Whatever the filter is, only the files that end
  132. * with .java will be included for processing.
  133. * Note that the base directory used for the fileset
  134. * MUST be the root of the source files otherwise package names
  135. * deduced from the file path will be incorrect.
  136. */
  137. public void addFileSet(FileSet fs) {
  138. fileSets.addElement(fs);
  139. }
  140. /** execute the command line */
  141. public void execute() throws BuildException {
  142. try {
  143. setUp();
  144. ExecuteStreamHandler handler = createStreamHandler();
  145. execute0(handler);
  146. } finally {
  147. cleanUp();
  148. }
  149. }
  150. /** check the options and build the command line */
  151. protected void setUp() throws BuildException {
  152. checkOptions();
  153. // set the classpath as the jar file
  154. File jar = getMetamataJar(metamataHome);
  155. final Path classPath = cmdl.createClasspath(getProject());
  156. classPath.createPathElement().setLocation(jar);
  157. // set the metamata.home property
  158. final Commandline.Argument vmArgs = cmdl.createVmArgument();
  159. vmArgs.setValue("-Dmetamata.home=" + metamataHome.getAbsolutePath());
  160. // retrieve all the files we want to scan
  161. includedFiles = scanSources(new Hashtable());
  162. //String[] entries = sourcePath.list();
  163. //includedFiles = scanSources(new Hashtable(), entries);
  164. log(includedFiles.size() + " files added for audit", Project.MSG_VERBOSE);
  165. // write all the options to a temp file and use it ro run the process
  166. Vector options = getOptions();
  167. optionsFile = createTmpFile();
  168. generateOptionsFile(optionsFile, options);
  169. Commandline.Argument args = cmdl.createArgument();
  170. args.setLine("-arguments " + optionsFile.getAbsolutePath());
  171. }
  172. /**
  173. * create a stream handler that will be used to get the output since
  174. * metamata tools do not report with convenient files such as XML.
  175. */
  176. protected abstract ExecuteStreamHandler createStreamHandler();
  177. /** execute the process with a specific handler */
  178. protected void execute0(ExecuteStreamHandler handler) throws BuildException {
  179. final Execute process = new Execute(handler);
  180. log(cmdl.describeCommand(), Project.MSG_VERBOSE);
  181. process.setCommandline(cmdl.getCommandline());
  182. try {
  183. if (process.execute() != 0) {
  184. throw new BuildException("Metamata task failed.");
  185. }
  186. } catch (IOException e) {
  187. throw new BuildException("Failed to launch Metamata task", e);
  188. }
  189. }
  190. /** clean up all the mess that we did with temporary objects */
  191. protected void cleanUp() {
  192. if (optionsFile != null) {
  193. optionsFile.delete();
  194. optionsFile = null;
  195. }
  196. }
  197. /** return the location of the jar file used to run */
  198. protected final File getMetamataJar(File home) {
  199. return new File(home, "lib/metamata.jar");
  200. }
  201. /** validate options set */
  202. protected void checkOptions() throws BuildException {
  203. // do some validation first
  204. if (metamataHome == null || !metamataHome.exists()) {
  205. throw new BuildException("'home' must point to Metamata home directory.");
  206. }
  207. File jar = getMetamataJar(metamataHome);
  208. if (!jar.exists()) {
  209. throw new BuildException(jar + " does not exist. Check your metamata installation.");
  210. }
  211. }
  212. /** return all options of the command line as string elements */
  213. protected abstract Vector getOptions();
  214. protected void generateOptionsFile(File tofile, Vector options) throws BuildException {
  215. FileWriter fw = null;
  216. try {
  217. fw = new FileWriter(tofile);
  218. PrintWriter pw = new PrintWriter(fw);
  219. final int size = options.size();
  220. for (int i = 0; i < size; i++) {
  221. pw.println(options.elementAt(i));
  222. }
  223. pw.flush();
  224. } catch (IOException e) {
  225. throw new BuildException("Error while writing options file " + tofile, e);
  226. } finally {
  227. if (fw != null) {
  228. try {
  229. fw.close();
  230. } catch (IOException ignored) {
  231. }
  232. }
  233. }
  234. }
  235. protected Hashtable getFileMapping() {
  236. return includedFiles;
  237. }
  238. /**
  239. * convenient method for JDK 1.1. Will copy all elements from src to dest
  240. */
  241. protected static final void addAllVector(Vector dest, Enumeration files) {
  242. while (files.hasMoreElements()) {
  243. dest.addElement(files.nextElement());
  244. }
  245. }
  246. protected final File createTmpFile() {
  247. File tmpFile = FileUtils.newFileUtils()
  248. .createTempFile("metamata", ".tmp", getProject().getBaseDir());
  249. tmpFile.deleteOnExit();
  250. return tmpFile;
  251. }
  252. /**
  253. * @return the list of .java files (as their absolute path) that should
  254. * be audited.
  255. */
  256. protected Hashtable scanSources(Hashtable map) {
  257. Hashtable files = new Hashtable();
  258. for (int i = 0; i < fileSets.size(); i++) {
  259. FileSet fs = (FileSet) fileSets.elementAt(i);
  260. DirectoryScanner ds = fs.getDirectoryScanner(getProject());
  261. ds.scan();
  262. String[] f = ds.getIncludedFiles();
  263. log(i + ") Adding " + f.length + " files from directory "
  264. + ds.getBasedir(), Project.MSG_VERBOSE);
  265. for (int j = 0; j < f.length; j++) {
  266. String pathname = f[j];
  267. if (pathname.endsWith(".java")) {
  268. File file = new File(ds.getBasedir(), pathname);
  269. // file = project.resolveFile(file.getAbsolutePath());
  270. String classname = pathname.substring(0, pathname.length() - ".java".length());
  271. classname = classname.replace(File.separatorChar, '.');
  272. files.put(file.getAbsolutePath(), classname); // it's a java file, add it.
  273. }
  274. }
  275. }
  276. return files;
  277. }
  278. protected Hashtable scanSources(final Hashtable mapping, final String[] entries) {
  279. final Vector javaFiles = new Vector(512);
  280. for (int i = 0; i < entries.length; i++) {
  281. final File f = new File(entries[i]);
  282. if (f.isDirectory()) {
  283. DirectoryScanner ds = new DirectoryScanner();
  284. ds.setBasedir(f);
  285. ds.setIncludes(new String[]{"**/*.java"});
  286. ds.scan();
  287. String[] included = ds.getIncludedFiles();
  288. for (int j = 0; j < included.length; j++) {
  289. javaFiles.addElement(new File(f, included[j]));
  290. }
  291. } else if (entries[i].endsWith(".java")) {
  292. javaFiles.addElement(f);
  293. }
  294. }
  295. // do the mapping paths/classname
  296. final int count = javaFiles.size();
  297. for (int i = 0; i < count; i++) {
  298. File file = (File) javaFiles.elementAt(i);
  299. String pathname = Path.translateFile(file.getAbsolutePath());
  300. String classname = pathname.substring(0, pathname.length() - ".java".length());
  301. classname = classname.replace(File.separatorChar, '.');
  302. mapping.put(pathname, classname);
  303. }
  304. return mapping;
  305. }
  306. }