You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Rmic.java 17 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 1999 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import org.apache.tools.ant.BuildException;
  56. import org.apache.tools.ant.DirectoryScanner;
  57. import org.apache.tools.ant.Project;
  58. import org.apache.tools.ant.types.Path;
  59. import org.apache.tools.ant.types.Reference;
  60. import java.io.*;
  61. import java.util.StringTokenizer;
  62. import java.util.Vector;
  63. import java.util.Date;
  64. /**
  65. * Task to compile RMI stubs and skeletons. This task can take the following
  66. * arguments:
  67. * <ul>
  68. * <li>base: The base directory for the compiled stubs and skeletons
  69. * <li>class: The name of the class to generate the stubs from
  70. * <li>stubVersion: The version of the stub prototol to use (1.1, 1.2, compat)
  71. * <li>sourceBase: The base directory for the generated stubs and skeletons
  72. * <li>classpath: Additional classpath, appended before the system classpath
  73. * </ul>
  74. * Of these arguments, <b>base</b> is required.
  75. * <p>
  76. * If classname is specified then only that classname will be compiled. If it
  77. * is absent, then <b>base</b> is traversed for classes according to patterns.
  78. * <p>
  79. *
  80. * @author duncan@x180.com
  81. * @author ludovic.claude@websitewatchers.co.uk
  82. * @author David Maclean <a href="mailto:david@cm.co.za">david@cm.co.za</a>
  83. */
  84. public class Rmic extends MatchingTask {
  85. private String base;
  86. private String classname;
  87. private String sourceBase;
  88. private String stubVersion;
  89. private Path compileClasspath;
  90. private boolean verify = false;
  91. private boolean filtering = false;
  92. private Vector compileList = new Vector();
  93. private Vector classpathReferences = new Vector();
  94. public void setBase(String base) {
  95. this.base = base;
  96. }
  97. public void XsetClass(String classname) {
  98. log("The class attribute is deprecated. " +
  99. "Please use the classname attribute.",
  100. Project.MSG_WARN);
  101. this.classname = classname;
  102. }
  103. public void setClassname(String classname) {
  104. this.classname = classname;
  105. }
  106. public void setSourceBase(String sourceBase) {
  107. this.sourceBase = sourceBase;
  108. }
  109. public void setStubVersion(String stubVersion) {
  110. this.stubVersion = stubVersion;
  111. }
  112. public void setFiltering(String filter) {
  113. filtering = Project.toBoolean(filter);
  114. }
  115. /**
  116. * Set the classpath to be used for this compilation.
  117. */
  118. public void setClasspath(Path classpath) {
  119. if (compileClasspath == null) {
  120. compileClasspath = classpath;
  121. } else {
  122. compileClasspath.append(classpath);
  123. }
  124. }
  125. /**
  126. * Maybe creates a nested classpath element.
  127. */
  128. public Path createClasspath() {
  129. if (compileClasspath == null) {
  130. compileClasspath = new Path(project);
  131. }
  132. return compileClasspath;
  133. }
  134. /**
  135. * Adds a reference to a CLASSPATH defined elsewhere - nested
  136. * <classpathref> element.
  137. */
  138. public void addClasspathRef(Reference r) {
  139. classpathReferences.addElement(r);
  140. }
  141. /**
  142. * Adds a reference to a CLASSPATH defined elsewhere - nested
  143. * <classpathref> element.
  144. */
  145. public void setClasspathRef(Reference r) {
  146. classpathReferences.addElement(r);
  147. }
  148. /**
  149. * Indicates that the classes found by the directory match should be
  150. * checked to see if they implement java.rmi.Remote.
  151. * This defaults to false if not set. */
  152. public void setVerify(String verify) {
  153. this.verify = Project.toBoolean(verify);
  154. }
  155. public void execute() throws BuildException {
  156. File baseDir = project.resolveFile(base);
  157. if (baseDir == null) {
  158. throw new BuildException("base attribute must be set!", location);
  159. }
  160. if (!baseDir.exists()) {
  161. throw new BuildException("base does not exist!", location);
  162. }
  163. if (verify) {
  164. log("Verify has been turned on.", Project.MSG_INFO);
  165. }
  166. File sourceBaseFile = null;
  167. if (null != sourceBase) {
  168. sourceBaseFile = project.resolveFile(sourceBase);
  169. }
  170. Path classpath = getCompileClasspath(baseDir);
  171. for (int i=0; i<classpathReferences.size(); i++) {
  172. Reference r = (Reference) classpathReferences.elementAt(i);
  173. Object o = r.getReferencedObject(project);
  174. if (o instanceof Path) {
  175. classpath.append((Path) o);
  176. } else {
  177. String msg = r.getRefId()+" doesn\'t denote a classpath";
  178. throw new BuildException(msg, location);
  179. }
  180. }
  181. // scan base dirs to build up compile lists only if a
  182. // specific classname is not given
  183. if (classname == null) {
  184. DirectoryScanner ds = this.getDirectoryScanner(baseDir);
  185. String[] files = ds.getIncludedFiles();
  186. scanDir(baseDir, files, verify);
  187. }
  188. // XXX
  189. // need to provide an input stream that we read in from!
  190. sun.rmi.rmic.Main compiler = new sun.rmi.rmic.Main(System.out, "rmic");
  191. int argCount = 5;
  192. int i = 0;
  193. if (null != stubVersion) argCount++;
  194. if (null != sourceBase) argCount++;
  195. if (compileList.size() > 0) argCount += compileList.size() - 1;
  196. String[] args = new String[argCount];
  197. args[i++] = "-d";
  198. args[i++] = baseDir.getAbsolutePath();
  199. args[i++] = "-classpath";
  200. args[i++] = classpath.toString();
  201. if (null != stubVersion) {
  202. if ("1.1".equals(stubVersion))
  203. args[i++] = "-v1.1";
  204. else if ("1.2".equals(stubVersion))
  205. args[i++] = "-v1.2";
  206. else
  207. args[i++] = "-vcompat";
  208. }
  209. if (null != sourceBase) args[i++] = "-keepgenerated";
  210. if (classname != null) {
  211. if (shouldCompile(new File(baseDir, classname.replace('.', File.separatorChar) + ".class"))) {
  212. args[i++] = classname;
  213. compiler.compile(args);
  214. }
  215. } else {
  216. if (compileList.size() > 0) {
  217. log("RMI Compiling " + compileList.size() +
  218. " classes to " + baseDir, Project.MSG_INFO);
  219. for (int j = 0; j < compileList.size(); j++) {
  220. args[i++] = (String) compileList.elementAt(j);
  221. }
  222. compiler.compile(args);
  223. }
  224. }
  225. // Move the generated source file to the base directory
  226. if (null != sourceBase) {
  227. if (classname != null) {
  228. moveGeneratedFile(baseDir, sourceBaseFile, classname);
  229. } else {
  230. for (int j = 0; j < compileList.size(); j++) {
  231. moveGeneratedFile(baseDir, sourceBaseFile, (String) compileList.elementAt(j));
  232. }
  233. }
  234. }
  235. }
  236. /**
  237. * Move the generated source file(s) to the base directory
  238. *
  239. * @exception org.apache.tools.ant.BuildException When error copying/removing files.
  240. */
  241. private void moveGeneratedFile (File baseDir, File sourceBaseFile, String classname)
  242. throws BuildException {
  243. String stubFileName = classname.replace('.', File.separatorChar) + "_Stub.java";
  244. File oldStubFile = new File(baseDir, stubFileName);
  245. File newStubFile = new File(sourceBaseFile, stubFileName);
  246. try {
  247. project.copyFile(oldStubFile, newStubFile, filtering);
  248. oldStubFile.delete();
  249. } catch (IOException ioe) {
  250. String msg = "Failed to copy " + oldStubFile + " to " +
  251. newStubFile + " due to " + ioe.getMessage();
  252. throw new BuildException(msg, ioe, location);
  253. }
  254. if (!"1.2".equals(stubVersion)) {
  255. String skelFileName = classname.replace('.', '/') + "_Skel.java";
  256. File oldSkelFile = new File(baseDir, skelFileName);
  257. File newSkelFile = new File(sourceBaseFile, skelFileName);
  258. try {
  259. project.copyFile(oldSkelFile, newSkelFile, filtering);
  260. oldSkelFile.delete();
  261. } catch (IOException ioe) {
  262. String msg = "Failed to copy " + oldSkelFile + " to " +
  263. newSkelFile + " due to " + ioe.getMessage();
  264. throw new BuildException(msg, ioe, location);
  265. }
  266. }
  267. }
  268. /**
  269. * Scans the directory looking for class files to be compiled.
  270. * The result is returned in the class variable compileList.
  271. */
  272. protected void scanDir(File baseDir, String files[], boolean shouldVerify) {
  273. compileList.removeAllElements();
  274. for (int i = 0; i < files.length; i++) {
  275. File baseFile = new File(baseDir, files[i]);
  276. if (files[i].endsWith(".class") &&
  277. !files[i].endsWith("_Stub.class") &&
  278. !files[i].endsWith("_Skel.class")) {
  279. if (shouldCompile(baseFile)) {
  280. String classname = files[i].replace(File.separatorChar, '.');
  281. classname = classname.substring(0, classname.indexOf(".class"));
  282. boolean shouldAdd = true;
  283. if (shouldVerify) {
  284. try {
  285. Class testClass = Class.forName(classname);
  286. // One cannot RMIC an interface
  287. if (testClass.isInterface() || !isValidRmiRemote(testClass)) {
  288. shouldAdd = false;
  289. }
  290. } catch (ClassNotFoundException e) {
  291. log("Unable to verify class " + classname +
  292. ". It could not be found.", Project.MSG_WARN);
  293. } catch (NoClassDefFoundError e) {
  294. log("Unable to verify class " + classname +
  295. ". It is not defined.", Project.MSG_WARN);
  296. }
  297. }
  298. if (shouldAdd) {
  299. log("Adding: " + classname + " to compile list",
  300. Project.MSG_VERBOSE);
  301. compileList.addElement(classname);
  302. }
  303. }
  304. }
  305. }
  306. }
  307. /**
  308. * Check to see if the class or superclasses/interfaces implement
  309. * java.rmi.Remote.
  310. */
  311. private boolean isValidRmiRemote (Class testClass) {
  312. Class rmiRemote = java.rmi.Remote.class;
  313. if (rmiRemote.equals(testClass)) {
  314. // This class is java.rmi.Remote
  315. return true;
  316. }
  317. Class [] interfaces = testClass.getInterfaces();
  318. if (interfaces != null) {
  319. for (int i = 0; i < interfaces.length; i++) {
  320. if (rmiRemote.equals(interfaces[i])) {
  321. // This class directly implements java.rmi.Remote
  322. return true;
  323. }
  324. if (isValidRmiRemote(interfaces[i])) {
  325. return true;
  326. }
  327. }
  328. }
  329. return false;
  330. }
  331. /**
  332. * Determine whether the class needs to be RMI compiled. It looks at the _Stub.class
  333. * and _Skel.class files' last modification date and compares it with the class' class file.
  334. */
  335. private boolean shouldCompile (File classFile) {
  336. long now = (new Date()).getTime();
  337. File stubFile = new File(classFile.getAbsolutePath().substring(0,
  338. classFile.getAbsolutePath().indexOf(".class")) + "_Stub.class");
  339. File skelFile = new File(classFile.getAbsolutePath().substring(0,
  340. classFile.getAbsolutePath().indexOf(".class")) + "_Skel.class");
  341. if (classFile.exists()) {
  342. if (classFile.lastModified() > now) {
  343. log("Warning: file modified in the future: " +
  344. classFile, Project.MSG_WARN);
  345. }
  346. if (classFile.lastModified() > stubFile.lastModified()) {
  347. return true;
  348. } else if (classFile.lastModified() > skelFile.lastModified()) {
  349. return true;
  350. } else {
  351. return false;
  352. }
  353. }
  354. return true;
  355. }
  356. /**
  357. * Builds the compilation classpath.
  358. */
  359. // XXX
  360. // we need a way to not use the current classpath.
  361. private Path getCompileClasspath(File baseFile) {
  362. // add dest dir to classpath so that previously compiled and
  363. // untouched classes are on classpath
  364. Path classpath = new Path(project, baseFile.getAbsolutePath());
  365. // add our classpath to the mix
  366. if (compileClasspath != null) {
  367. addExistingToClasspath(classpath,compileClasspath);
  368. }
  369. // add the system classpath
  370. addExistingToClasspath(classpath, Path.systemClasspath);
  371. // in jdk 1.2, the system classes are not on the visible classpath.
  372. if (Project.getJavaVersion().startsWith("1.2")) {
  373. String bootcp = System.getProperty("sun.boot.class.path");
  374. if (bootcp != null) {
  375. addExistingToClasspath(classpath, new Path(project, bootcp));
  376. }
  377. }
  378. return classpath;
  379. }
  380. /**
  381. * Takes a classpath-like string, and adds each element of
  382. * this string to a new classpath, if the components exist.
  383. * Components that don't exist, aren't added.
  384. * We do this, because jikes issues warnings for non-existant
  385. * files/dirs in his classpath, and these warnings are pretty
  386. * annoying.
  387. * @param target - target classpath
  388. * @param source - source classpath
  389. * to get file objects.
  390. */
  391. private void addExistingToClasspath(Path target, Path source) {
  392. String[] list = source.list();
  393. for (int i=0; i<list.length; i++) {
  394. File f = project.resolveFile(list[i]);
  395. if (f.exists()) {
  396. target.setLocation(f);
  397. } else {
  398. log("Dropping from classpath: "+
  399. f.getAbsolutePath(), Project.MSG_VERBOSE);
  400. }
  401. }
  402. }
  403. }