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.

Antlib.java 21 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  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", "Ant", 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.*;
  56. import org.apache.tools.ant.types.*;
  57. import org.xml.sax.*;
  58. import javax.xml.parsers.*;
  59. import java.util.*;
  60. import java.util.zip.*;
  61. import java.io.*;
  62. /**
  63. * Make available the tasks and types from an Ant library. <pre>
  64. * &lt;antlib library="libname.jar" &gt;
  65. * &lt;alias name="nameOnLib" as="newName" /&gt;
  66. * &lt;/antlib&gt;
  67. *
  68. * &lt;antlib file="libname.jar" override="true" /&gt;
  69. * </pre>
  70. *
  71. * @author minor changes by steve loughran, steve_l@iseran.com
  72. * @author <a href="j_a_fernandez@yahoo.com">Jose Alberto Fernandez</a>
  73. * @since ant1.5
  74. */
  75. public class Antlib extends Task {
  76. /*
  77. * implements DeclaringTask
  78. */
  79. /**
  80. * library attribute
  81. */
  82. private String library = null;
  83. /**
  84. * file attribute
  85. */
  86. private File file = null;
  87. /**
  88. * override attribute
  89. */
  90. private boolean override = false;
  91. /**
  92. * attribute to control classloader use
  93. */
  94. private boolean useCurrentClassloader = false;
  95. /**
  96. * classpath to build up
  97. */
  98. private Path classpath = null;
  99. /**
  100. * our little xml parse
  101. */
  102. private SAXParserFactory saxFactory;
  103. /**
  104. * table of aliases
  105. */
  106. private Vector aliases = new Vector();
  107. /**
  108. * Location of descriptor in library
  109. */
  110. public static String ANT_DESCRIPTOR = "META-INF/antlib.xml";
  111. /**
  112. * Prefix name for DTD of descriptor
  113. */
  114. public static String ANTLIB_DTD_URL =
  115. "http://jakarta.apache.org/ant/";
  116. /**
  117. * prefix of the antlib
  118. */
  119. public static String ANTLIB_DTD_PREFIX = "Antlib-V";
  120. /**
  121. * version counter
  122. */
  123. public static String ANTLIB_DTD_VERSION = "1_0";
  124. /**
  125. * dtd file extension
  126. */
  127. public static String ANTLIB_DTD_EXT = ".dtd";
  128. /**
  129. * constructor creates a validating sax parser
  130. */
  131. public Antlib() {
  132. super();
  133. saxFactory = SAXParserFactory.newInstance();
  134. saxFactory.setValidating(true);
  135. }
  136. /**
  137. * constructor binds to a project as well as setting up internal state
  138. *
  139. * @param p Description of Parameter
  140. */
  141. public Antlib(Project p) {
  142. this();
  143. setProject(p);
  144. }
  145. /**
  146. * Set name of library to load. The library is located in $ANT_HOME/lib.
  147. *
  148. * @param lib the name of library relative to $ANT_HOME/lib.
  149. */
  150. public void setLibrary(String lib) {
  151. this.library = lib;
  152. }
  153. /**
  154. * Set file location of library to load.
  155. *
  156. * @param file the jar file for the library.
  157. */
  158. public void setFile(File file) {
  159. this.file = file;
  160. }
  161. /**
  162. * Set whether to override any existing definitions.
  163. *
  164. * @param override if true new definitions will replace existing ones.
  165. */
  166. public void setOverride(boolean override) {
  167. this.override = override;
  168. }
  169. /**
  170. * Set whether to use a new classloader or not. Default is <code>false</code>
  171. * . This property is mostly used by the core when loading core tasks.
  172. *
  173. * @param useCurrentClassloader if true the current classloader will
  174. * be used to load the definitions.
  175. */
  176. public void setUseCurrentClassloader(boolean useCurrentClassloader) {
  177. this.useCurrentClassloader = useCurrentClassloader;
  178. }
  179. /**
  180. * Create new Alias element.
  181. *
  182. * @return Description of the Returned Value
  183. */
  184. public Alias createAlias() {
  185. Alias als = new Alias();
  186. aliases.add(als);
  187. return als;
  188. }
  189. /**
  190. * Set the classpath to be used for this compilation
  191. *
  192. * @param cp The new Classpath value
  193. */
  194. public void setClasspath(Path cp) {
  195. if (classpath == null) {
  196. classpath = cp;
  197. }
  198. else {
  199. classpath.append(cp);
  200. }
  201. }
  202. /**
  203. * create a nested classpath element.
  204. *
  205. * @return classpath to use
  206. */
  207. public Path createClasspath() {
  208. if (classpath == null) {
  209. classpath = new Path(project);
  210. }
  211. return classpath.createPath();
  212. }
  213. /**
  214. * Adds a reference to a CLASSPATH defined elsewhere
  215. *
  216. * @param r The new ClasspathRef value
  217. */
  218. public void setClasspathRef(Reference r) {
  219. createClasspath().setRefid(r);
  220. }
  221. /**
  222. * actually do the work of loading the library
  223. *
  224. * @exception BuildException Description of Exception
  225. * @todo maybe have failonerror support for missing file?
  226. */
  227. public void execute()
  228. throws BuildException {
  229. File realFile = file;
  230. if (library != null) {
  231. if (file != null) {
  232. String msg = "You cannot specify both file and library.";
  233. throw new BuildException(msg, location);
  234. }
  235. // For the time being libraries live in $ANT_HOME/lib.
  236. // The idea being that we would not load all the jars there anymore
  237. String home = project.getProperty("ant.home");
  238. if (home == null) {
  239. throw new BuildException("ANT_HOME not set as required.");
  240. }
  241. realFile = new File(new File(home, "lib"), library);
  242. }
  243. else if (file == null) {
  244. String msg = "Must specify either library or file attribute.";
  245. throw new BuildException(msg, location);
  246. }
  247. if (!realFile.exists()) {
  248. String msg = "Cannot find library: " + realFile;
  249. throw new BuildException(msg, location);
  250. }
  251. //open the descriptor
  252. InputStream is = getDescriptor(realFile);
  253. if (is == null) {
  254. String msg = "Missing descriptor on library: " + realFile;
  255. throw new BuildException(msg, location);
  256. }
  257. ClassLoader classloader=null;
  258. if (useCurrentClassloader && classpath != null) {
  259. log("ignoring the useCurrentClassloader option as a classpath is defined",
  260. Project.MSG_WARN);
  261. useCurrentClassloader=false;
  262. }
  263. if (!useCurrentClassloader) {
  264. classloader = makeClassLoader(realFile);
  265. }
  266. //parse it and evaluate it.
  267. evaluateDescriptor(classloader, processAliases(), is);
  268. }
  269. /**
  270. * Load definitions directly from an external XML file.
  271. *
  272. * @param xmlfile XML file in the Antlib format.
  273. * @exception BuildException failure to open the file
  274. */
  275. public void loadDefinitions(File xmlfile)
  276. throws BuildException {
  277. try {
  278. InputStream is = new FileInputStream(xmlfile);
  279. loadDefinitions(is);
  280. }
  281. catch (IOException io) {
  282. throw new BuildException("Cannot read file: " + file, io);
  283. }
  284. }
  285. /**
  286. * Load definitions directly from InputStream.
  287. *
  288. * @param is InputStream for the Antlib descriptor.
  289. * @exception BuildException trouble
  290. */
  291. public void loadDefinitions(InputStream is)
  292. throws BuildException {
  293. evaluateDescriptor(null, processAliases(), is);
  294. }
  295. /**
  296. * get a descriptor from the library file
  297. *
  298. * @param file jarfile to open
  299. * @return input stream to the Descriptor
  300. * @exception BuildException io trouble, or it isnt a zipfile
  301. */
  302. private InputStream getDescriptor(File file)
  303. throws BuildException {
  304. try {
  305. final ZipFile zipfile = new ZipFile(file);
  306. ZipEntry entry = zipfile.getEntry(ANT_DESCRIPTOR);
  307. if (entry == null) {
  308. return null;
  309. }
  310. // Guarantee that when Entry is closed so does the zipfile instance.
  311. return
  312. new FilterInputStream(zipfile.getInputStream(entry)) {
  313. public void close()
  314. throws IOException {
  315. super.close();
  316. zipfile.close();
  317. }
  318. };
  319. }
  320. catch (ZipException ze) {
  321. throw new BuildException("Not a library file.", ze, location);
  322. }
  323. catch (IOException ioe) {
  324. throw new BuildException("Cannot read library content.",
  325. ioe, location);
  326. }
  327. }
  328. /**
  329. * turn the alias list to a property hashtable
  330. *
  331. * @return generated property hashtable
  332. */
  333. private Properties processAliases() {
  334. Properties p = new Properties();
  335. for (Enumeration e = aliases.elements(); e.hasMoreElements(); ) {
  336. Alias a = (Alias) e.nextElement();
  337. p.put(a.name, a.as);
  338. }
  339. return p;
  340. }
  341. /**
  342. * create the classpath for this library from the file passed in and
  343. * any classpath parameters
  344. *
  345. * @param file library file to use
  346. * @return classloader using te
  347. * @exception BuildException trouble creating the classloader
  348. */
  349. protected ClassLoader makeClassLoader(File file)
  350. throws BuildException {
  351. Path clspath = new Path(project);
  352. clspath.setLocation(file);
  353. //append any build supplied classpath
  354. if (classpath != null) {
  355. clspath.append(classpath);
  356. }
  357. AntClassLoader al = new AntClassLoader(project, clspath, true);
  358. return al;
  359. }
  360. /**
  361. * parse the antlib descriptor
  362. *
  363. * @param cl optional classloader
  364. * @param als alias list as property hashtable
  365. * @param is input stream to descriptor
  366. * @exception BuildException trouble
  367. */
  368. protected void evaluateDescriptor(ClassLoader cl,
  369. Properties als, InputStream is)
  370. throws BuildException {
  371. try {
  372. SAXParser saxParser = saxFactory.newSAXParser();
  373. Parser parser = saxParser.getParser();
  374. InputSource inputSource = new InputSource(is);
  375. //inputSource.setSystemId(uri); //URI is nasty for jar entries
  376. project.log("parsing descriptor for library: " + file,
  377. Project.MSG_VERBOSE);
  378. saxParser.parse(inputSource, new AntLibraryHandler(cl, als));
  379. }
  380. catch (ParserConfigurationException exc) {
  381. throw new BuildException("Parser has not been configured correctly", exc);
  382. }
  383. catch (SAXParseException exc) {
  384. Location location =
  385. new Location(ANT_DESCRIPTOR,
  386. exc.getLineNumber(), exc.getColumnNumber());
  387. Throwable t = exc.getException();
  388. if (t instanceof BuildException) {
  389. BuildException be = (BuildException) t;
  390. if (be.getLocation() == Location.UNKNOWN_LOCATION) {
  391. be.setLocation(location);
  392. }
  393. throw be;
  394. }
  395. throw new BuildException(exc.getMessage(), t, location);
  396. }
  397. catch (SAXException exc) {
  398. Throwable t = exc.getException();
  399. if (t instanceof BuildException) {
  400. throw (BuildException) t;
  401. }
  402. throw new BuildException(exc.getMessage(), t);
  403. }
  404. catch (IOException exc) {
  405. throw new BuildException("Error reading library descriptor", exc);
  406. }
  407. finally {
  408. if (is != null) {
  409. try {
  410. is.close();
  411. }
  412. catch (IOException ioe) {
  413. // ignore this
  414. }
  415. }
  416. }
  417. }
  418. /**
  419. * get a DTD URI from url, prefix and extension
  420. *
  421. * @return URI for this dtd version
  422. */
  423. public static String dtdVersion() {
  424. return ANTLIB_DTD_URL + ANTLIB_DTD_PREFIX +
  425. ANTLIB_DTD_VERSION + ANTLIB_DTD_EXT;
  426. }
  427. /**
  428. * compare system ID with the dtd string
  429. * -ignoring any version number
  430. * @param systemId Description of Parameter
  431. * @return true if this is a an ant library descriptor
  432. */
  433. public static boolean matchDtdId(String systemId) {
  434. return (systemId != null &&
  435. systemId.startsWith(ANTLIB_DTD_URL + ANTLIB_DTD_PREFIX) &&
  436. systemId.endsWith(ANTLIB_DTD_EXT));
  437. }
  438. /**
  439. * Parses the document describing the content of the
  440. * library. An inner class for access to Project.log
  441. */
  442. private class AntLibraryHandler extends HandlerBase {
  443. /**
  444. * our classloader
  445. */
  446. private final ClassLoader classloader;
  447. /**
  448. * the aliases
  449. */
  450. private final Properties aliasMap;
  451. /**
  452. * doc locator
  453. */
  454. private Locator locator = null;
  455. /**
  456. * Constructor for the AntLibraryHandler object
  457. *
  458. * @param cl optional classloader
  459. * @param als alias list
  460. */
  461. AntLibraryHandler(ClassLoader classloader, Properties als) {
  462. this.classloader = classloader;
  463. this.aliasMap = als;
  464. }
  465. /**
  466. * Sets the DocumentLocator attribute of the AntLibraryHandler
  467. * object
  468. *
  469. * @param locator The new DocumentLocator value
  470. */
  471. public void setDocumentLocator(Locator locator) {
  472. this.locator = locator;
  473. }
  474. /**
  475. * SAX callback handler
  476. *
  477. * @param tag XML tag
  478. * @param attrs attributes
  479. * @exception SAXParseException parse trouble
  480. */
  481. public void startElement(String tag, AttributeList attrs)
  482. throws SAXParseException {
  483. if ("antlib".equals(tag)) {
  484. // No attributes to worry about
  485. return;
  486. }
  487. if ("task".equals(tag) || "type".equals(tag)) {
  488. String name = null;
  489. String className = null;
  490. for (int i = 0, last = attrs.getLength(); i < last; i++) {
  491. String key = attrs.getName(i);
  492. String value = attrs.getValue(i);
  493. if (key.equals("name")) {
  494. name = value;
  495. }
  496. else if (key.equals("class")) {
  497. className = value;
  498. }
  499. else {
  500. throw new SAXParseException("Unexpected attribute \""
  501. + key + "\"", locator);
  502. }
  503. }
  504. if (name == null || className == null) {
  505. String msg = "Underspecified " + tag + " declaration.";
  506. throw new SAXParseException(msg, locator);
  507. }
  508. try {
  509. //check for name alias
  510. String alias = aliasMap.getProperty(name);
  511. if (alias != null) {
  512. name = alias;
  513. }
  514. //catch an attempted override of an existing name
  515. if (!override && inUse(name)) {
  516. String msg = "Cannot override " + tag + ": " + name;
  517. log(msg, Project.MSG_WARN);
  518. return;
  519. }
  520. //load the named class
  521. Class cls;
  522. if(classloader==null) {
  523. cls=Class.forName(className);
  524. }
  525. else {
  526. cls=classloader.loadClass(className);
  527. }
  528. //register it as a task or a datatype
  529. if (tag.equals("task")) {
  530. project.addTaskDefinition(name, cls);
  531. }
  532. else {
  533. project.addDataTypeDefinition(name, cls);
  534. }
  535. }
  536. catch (ClassNotFoundException cnfe) {
  537. String msg = "Class " + className +
  538. " cannot be found";
  539. throw new SAXParseException(msg, locator, cnfe);
  540. }
  541. catch (NoClassDefFoundError ncdfe) {
  542. String msg = "Class " + className +
  543. " cannot be found";
  544. throw new SAXParseException(msg, locator);
  545. }
  546. }
  547. else {
  548. throw new SAXParseException("Unexpected element \"" +
  549. tag + "\"",
  550. locator);
  551. }
  552. }
  553. /**
  554. * test for a name being in use already
  555. *
  556. * @param name the name to test
  557. * @return true if it is a task or a datatype
  558. */
  559. private boolean inUse(String name) {
  560. return (project.getTaskDefinitions().get(name) != null ||
  561. project.getDataTypeDefinitions().get(name) != null);
  562. }
  563. /**
  564. * Recognizes the DTD declaration for antlib and returns the corresponding
  565. * DTD definition from a resource. <P>
  566. *
  567. * To allow for future versions of the DTD format it will search
  568. * for any DTDs of the form "Antlib-V.*\.dtd".
  569. *
  570. * @param publicId public ID (ignored)
  571. * @param systemId system ID (matched against)
  572. * @return local DTD instance
  573. */
  574. public InputSource resolveEntity(String publicId,
  575. String systemId) {
  576. log("Looking for entiry with PublicID=" + publicId +
  577. " and SystemId=" + systemId, Project.MSG_VERBOSE);
  578. if (matchDtdId(systemId)) {
  579. String resId = systemId.substring(ANTLIB_DTD_URL.length());
  580. InputSource is =
  581. new InputSource(this.getClass().getResourceAsStream(resId));
  582. is.setSystemId(systemId);
  583. return is;
  584. }
  585. return null;
  586. }
  587. //end inner class AntLibraryHandler
  588. }
  589. /**
  590. * this class is used for alias elements
  591. *
  592. * @author slo
  593. * @created 11 November 2001
  594. */
  595. public static class Alias {
  596. /**
  597. * Description of the Field
  598. */
  599. private String name;
  600. /**
  601. * Description of the Field
  602. */
  603. private String as;
  604. /**
  605. * Sets the Name attribute of the Alias object
  606. *
  607. * @param name The new Name value
  608. */
  609. public void setName(String name) {
  610. this.name = name;
  611. }
  612. /**
  613. * Sets the As attribute of the Alias object
  614. *
  615. * @param as The new As value
  616. */
  617. public void setAs(String as) {
  618. this.as = as;
  619. }
  620. //end inner class alias
  621. }
  622. //end class Antlib
  623. }