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.

Definer.java 20 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  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. package org.apache.tools.ant.taskdefs;
  19. import java.io.File;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.net.URL;
  23. import java.util.Collections;
  24. import java.util.Enumeration;
  25. import java.util.HashMap;
  26. import java.util.Locale;
  27. import java.util.Map;
  28. import java.util.Properties;
  29. import org.apache.tools.ant.AntTypeDefinition;
  30. import org.apache.tools.ant.ComponentHelper;
  31. import org.apache.tools.ant.BuildException;
  32. import org.apache.tools.ant.Location;
  33. import org.apache.tools.ant.Project;
  34. import org.apache.tools.ant.ProjectHelper;
  35. import org.apache.tools.ant.MagicNames;
  36. import org.apache.tools.ant.util.FileUtils;
  37. import org.apache.tools.ant.types.EnumeratedAttribute;
  38. /**
  39. * Base class for Taskdef and Typedef - handles all
  40. * the attributes for Typedef. The uri and class
  41. * handling is handled by DefBase
  42. *
  43. * @since Ant 1.4
  44. */
  45. public abstract class Definer extends DefBase {
  46. /**
  47. * the extension of an antlib file for autoloading.
  48. * {@value[
  49. */
  50. private static final String ANTLIB_XML = "/antlib.xml";
  51. private static final ThreadLocal<Map<URL, Location>> RESOURCE_STACK = new ThreadLocal<Map<URL, Location>>() {
  52. protected Map<URL, Location> initialValue() {
  53. return new HashMap<URL, Location>();
  54. }
  55. };
  56. private String name;
  57. private String classname;
  58. private File file;
  59. private String resource;
  60. private boolean restrict = false;
  61. private int format = Format.PROPERTIES;
  62. private boolean definerSet = false;
  63. private int onError = OnError.FAIL;
  64. private String adapter;
  65. private String adaptTo;
  66. private Class<?> adapterClass;
  67. private Class<?> adaptToClass;
  68. /**
  69. * Enumerated type for onError attribute
  70. *
  71. * @see EnumeratedAttribute
  72. */
  73. public static class OnError extends EnumeratedAttribute {
  74. /** Enumerated values */
  75. public static final int FAIL = 0, REPORT = 1, IGNORE = 2, FAIL_ALL = 3;
  76. /**
  77. * text value of onerror option {@value}
  78. */
  79. public static final String POLICY_FAIL = "fail";
  80. /**
  81. * text value of onerror option {@value}
  82. */
  83. public static final String POLICY_REPORT = "report";
  84. /**
  85. * text value of onerror option {@value}
  86. */
  87. public static final String POLICY_IGNORE = "ignore";
  88. /**
  89. * text value of onerror option {@value}
  90. */
  91. public static final String POLICY_FAILALL = "failall";
  92. /**
  93. * Constructor
  94. */
  95. public OnError() {
  96. super();
  97. }
  98. /**
  99. * Constructor using a string.
  100. * @param value the value of the attribute
  101. */
  102. public OnError(String value) {
  103. setValue(value);
  104. }
  105. /**
  106. * get the values
  107. * @return an array of the allowed values for this attribute.
  108. */
  109. public String[] getValues() {
  110. return new String[] {POLICY_FAIL, POLICY_REPORT, POLICY_IGNORE, POLICY_FAILALL};
  111. }
  112. }
  113. /**
  114. * Enumerated type for format attribute
  115. *
  116. * @see EnumeratedAttribute
  117. */
  118. public static class Format extends EnumeratedAttribute {
  119. /** Enumerated values */
  120. public static final int PROPERTIES = 0, XML = 1;
  121. /**
  122. * get the values
  123. * @return an array of the allowed values for this attribute.
  124. */
  125. public String[] getValues() {
  126. return new String[] {"properties", "xml"};
  127. }
  128. }
  129. /**
  130. * The restrict attribute.
  131. * If this is true, only use this definition in add(X).
  132. * @param restrict the value to set.
  133. */
  134. protected void setRestrict(boolean restrict) {
  135. this.restrict = restrict;
  136. }
  137. /**
  138. * What to do if there is an error in loading the class.
  139. * <dl>
  140. * <li>error - throw build exception</li>
  141. * <li>report - output at warning level</li>
  142. * <li>ignore - output at debug level</li>
  143. * </dl>
  144. *
  145. * @param onError an <code>OnError</code> value
  146. */
  147. public void setOnError(OnError onError) {
  148. this.onError = onError.getIndex();
  149. }
  150. /**
  151. * Sets the format of the file or resource
  152. * @param format the enumerated value - xml or properties
  153. */
  154. public void setFormat(Format format) {
  155. this.format = format.getIndex();
  156. }
  157. /**
  158. * @return the name for this definition
  159. */
  160. public String getName() {
  161. return name;
  162. }
  163. /**
  164. * @return the file containing definitions
  165. */
  166. public File getFile() {
  167. return file;
  168. }
  169. /**
  170. * @return the resource containing definitions
  171. */
  172. public String getResource() {
  173. return resource;
  174. }
  175. /**
  176. * Run the definition.
  177. *
  178. * @exception BuildException if an error occurs
  179. */
  180. public void execute() throws BuildException {
  181. ClassLoader al = createLoader();
  182. if (!definerSet) {
  183. //we arent fully defined yet. this is an error unless
  184. //we are in an antlib, in which case the resource name is determined
  185. //automatically.
  186. //NB: URIs in the ant core package will be "" at this point.
  187. if (getURI() == null) {
  188. throw new BuildException(
  189. "name, file or resource attribute of "
  190. + getTaskName() + " is undefined",
  191. getLocation());
  192. }
  193. if (getURI().startsWith(MagicNames.ANTLIB_PREFIX)) {
  194. //convert the URI to a resource
  195. String uri1 = getURI();
  196. setResource(makeResourceFromURI(uri1));
  197. } else {
  198. throw new BuildException(
  199. "Only antlib URIs can be located from the URI alone,"
  200. + " not the URI '" + getURI() + "'");
  201. }
  202. }
  203. if (name != null) {
  204. if (classname == null) {
  205. throw new BuildException(
  206. "classname attribute of " + getTaskName() + " element "
  207. + "is undefined", getLocation());
  208. }
  209. addDefinition(al, name, classname);
  210. } else {
  211. if (classname != null) {
  212. String msg = "You must not specify classname "
  213. + "together with file or resource.";
  214. throw new BuildException(msg, getLocation());
  215. }
  216. final Enumeration<URL> urls;
  217. if (file == null) {
  218. urls = resourceToURLs(al);
  219. } else {
  220. final URL url = fileToURL();
  221. if (url == null) {
  222. return;
  223. }
  224. urls = Collections.enumeration(Collections.singleton(url));
  225. }
  226. while (urls.hasMoreElements()) {
  227. URL url = urls.nextElement();
  228. int fmt = this.format;
  229. if (url.toString().toLowerCase(Locale.ENGLISH).endsWith(".xml")) {
  230. fmt = Format.XML;
  231. }
  232. if (fmt == Format.PROPERTIES) {
  233. loadProperties(al, url);
  234. break;
  235. } else {
  236. if (RESOURCE_STACK.get().get(url) != null) {
  237. log("Warning: Recursive loading of " + url
  238. + " ignored"
  239. + " at " + getLocation()
  240. + " originally loaded at "
  241. + RESOURCE_STACK.get().get(url),
  242. Project.MSG_WARN);
  243. } else {
  244. try {
  245. RESOURCE_STACK.get().put(url, getLocation());
  246. loadAntlib(al, url);
  247. } finally {
  248. RESOURCE_STACK.get().remove(url);
  249. }
  250. }
  251. }
  252. }
  253. }
  254. }
  255. /**
  256. * This is where the logic to map from a URI to an antlib resource
  257. * is kept.
  258. * @param uri the xml namespace uri that to convert.
  259. * @return the name of a resource. It may not exist
  260. */
  261. public static String makeResourceFromURI(String uri) {
  262. String path = uri.substring(MagicNames.ANTLIB_PREFIX.length());
  263. String resource;
  264. if (path.startsWith("//")) {
  265. //handle new style full paths to an antlib, in which
  266. //all but the forward slashes are allowed.
  267. resource = path.substring("//".length());
  268. if (!resource.endsWith(".xml")) {
  269. //if we haven't already named an XML file, it gets antlib.xml
  270. resource = resource + ANTLIB_XML;
  271. }
  272. } else {
  273. //convert from a package to a path
  274. resource = path.replace('.', '/') + ANTLIB_XML;
  275. }
  276. return resource;
  277. }
  278. /**
  279. * Convert a file to a file: URL.
  280. *
  281. * @return the URL, or null if it isn't valid and the active error policy
  282. * is not to raise a fault
  283. * @throws BuildException if the file is missing/not a file and the
  284. * policy requires failure at this point.
  285. */
  286. private URL fileToURL() {
  287. String message = null;
  288. if (!(file.exists())) {
  289. message = "File " + file + " does not exist";
  290. }
  291. if (message == null && !(file.isFile())) {
  292. message = "File " + file + " is not a file";
  293. }
  294. if (message == null) {
  295. try {
  296. return FileUtils.getFileUtils().getFileURL(file);
  297. } catch (Exception ex) {
  298. message =
  299. "File " + file + " cannot use as URL: "
  300. + ex.toString();
  301. }
  302. }
  303. // Here if there is an error
  304. switch (onError) {
  305. case OnError.FAIL_ALL:
  306. throw new BuildException(message);
  307. case OnError.FAIL:
  308. // Fall Through
  309. case OnError.REPORT:
  310. log(message, Project.MSG_WARN);
  311. break;
  312. case OnError.IGNORE:
  313. // log at a lower level
  314. log(message, Project.MSG_VERBOSE);
  315. break;
  316. default:
  317. // Ignore the problem
  318. break;
  319. }
  320. return null;
  321. }
  322. private Enumeration<URL> resourceToURLs(ClassLoader classLoader) {
  323. Enumeration<URL> ret;
  324. try {
  325. ret = classLoader.getResources(resource);
  326. } catch (IOException e) {
  327. throw new BuildException(
  328. "Could not fetch resources named " + resource,
  329. e, getLocation());
  330. }
  331. if (!ret.hasMoreElements()) {
  332. String message = "Could not load definitions from resource "
  333. + resource + ". It could not be found.";
  334. switch (onError) {
  335. case OnError.FAIL_ALL:
  336. throw new BuildException(message);
  337. case OnError.FAIL:
  338. case OnError.REPORT:
  339. log(message, Project.MSG_WARN);
  340. break;
  341. case OnError.IGNORE:
  342. log(message, Project.MSG_VERBOSE);
  343. break;
  344. default:
  345. // Ignore the problem
  346. break;
  347. }
  348. }
  349. return ret;
  350. }
  351. /**
  352. * Load type definitions as properties from a URL.
  353. *
  354. * @param al the classloader to use
  355. * @param url the url to get the definitions from
  356. */
  357. protected void loadProperties(ClassLoader al, URL url) {
  358. InputStream is = null;
  359. try {
  360. is = url.openStream();
  361. if (is == null) {
  362. log("Could not load definitions from " + url,
  363. Project.MSG_WARN);
  364. return;
  365. }
  366. Properties props = new Properties();
  367. props.load(is);
  368. Enumeration<?> keys = props.keys();
  369. while (keys.hasMoreElements()) {
  370. name = ((String) keys.nextElement());
  371. classname = props.getProperty(name);
  372. addDefinition(al, name, classname);
  373. }
  374. } catch (IOException ex) {
  375. throw new BuildException(ex, getLocation());
  376. } finally {
  377. FileUtils.close(is);
  378. }
  379. }
  380. /**
  381. * Load an antlib from a URL.
  382. *
  383. * @param classLoader the classloader to use.
  384. * @param url the url to load the definitions from.
  385. */
  386. private void loadAntlib(ClassLoader classLoader, URL url) {
  387. try {
  388. Antlib antlib = Antlib.createAntlib(getProject(), url, getURI());
  389. antlib.setClassLoader(classLoader);
  390. antlib.setURI(getURI());
  391. antlib.execute();
  392. } catch (BuildException ex) {
  393. throw ProjectHelper.addLocationToBuildException(
  394. ex, getLocation());
  395. }
  396. }
  397. /**
  398. * Name of the property file to load
  399. * ant name/classname pairs from.
  400. * @param file the file
  401. */
  402. public void setFile(File file) {
  403. if (definerSet) {
  404. tooManyDefinitions();
  405. }
  406. definerSet = true;
  407. this.file = file;
  408. }
  409. /**
  410. * Name of the property resource to load
  411. * ant name/classname pairs from.
  412. * @param res the resource to use
  413. */
  414. public void setResource(String res) {
  415. if (definerSet) {
  416. tooManyDefinitions();
  417. }
  418. definerSet = true;
  419. this.resource = res;
  420. }
  421. /**
  422. * Antlib attribute, sets resource and uri.
  423. * uri is set the antlib value and, resource is set
  424. * to the antlib.xml resource in the classpath.
  425. * For example antlib="antlib:org.acme.bland.cola"
  426. * corresponds to uri="antlib:org.acme.bland.cola"
  427. * resource="org/acme/bland/cola/antlib.xml".
  428. * ASF Bugzilla Bug 31999
  429. * @param antlib the value to set.
  430. */
  431. public void setAntlib(String antlib) {
  432. if (definerSet) {
  433. tooManyDefinitions();
  434. }
  435. if (!antlib.startsWith("antlib:")) {
  436. throw new BuildException(
  437. "Invalid antlib attribute - it must start with antlib:");
  438. }
  439. setURI(antlib);
  440. this.resource = antlib.substring("antlib:".length()).replace('.', '/')
  441. + "/antlib.xml";
  442. definerSet = true;
  443. }
  444. /**
  445. * Name of the definition
  446. * @param name the name of the definition
  447. */
  448. public void setName(String name) {
  449. if (definerSet) {
  450. tooManyDefinitions();
  451. }
  452. definerSet = true;
  453. this.name = name;
  454. }
  455. /**
  456. * Returns the classname of the object we are defining.
  457. * May be <code>null</code>.
  458. * @return the class name
  459. */
  460. public String getClassname() {
  461. return classname;
  462. }
  463. /**
  464. * The full class name of the object being defined.
  465. * Required, unless file or resource have
  466. * been specified.
  467. * @param classname the name of the class
  468. */
  469. public void setClassname(String classname) {
  470. this.classname = classname;
  471. }
  472. /**
  473. * Set the class name of the adapter class.
  474. * An adapter class is used to proxy the
  475. * definition class. It is used if the
  476. * definition class is not assignable to
  477. * the adaptto class, or if the adaptto
  478. * class is not present.
  479. *
  480. * @param adapter the name of the adapter class
  481. */
  482. public void setAdapter(String adapter) {
  483. this.adapter = adapter;
  484. }
  485. /**
  486. * Set the adapter class.
  487. *
  488. * @param adapterClass the class to use to adapt the definition class
  489. */
  490. protected void setAdapterClass(Class<?> adapterClass) {
  491. this.adapterClass = adapterClass;
  492. }
  493. /**
  494. * Set the classname of the class that the definition
  495. * must be compatible with, either directly or
  496. * by use of the adapter class.
  497. *
  498. * @param adaptTo the name of the adaptto class
  499. */
  500. public void setAdaptTo(String adaptTo) {
  501. this.adaptTo = adaptTo;
  502. }
  503. /**
  504. * Set the class for adaptToClass, to be
  505. * used by derived classes, used instead of
  506. * the adaptTo attribute.
  507. *
  508. * @param adaptToClass the class for adaptor.
  509. */
  510. protected void setAdaptToClass(Class<?> adaptToClass) {
  511. this.adaptToClass = adaptToClass;
  512. }
  513. /**
  514. * Add a definition using the attributes of Definer
  515. *
  516. * @param al the ClassLoader to use
  517. * @param name the name of the definition
  518. * @param classname the classname of the definition
  519. * @exception BuildException if an error occurs
  520. */
  521. protected void addDefinition(ClassLoader al, String name, String classname)
  522. throws BuildException {
  523. Class<?> cl = null;
  524. try {
  525. try {
  526. name = ProjectHelper.genComponentName(getURI(), name);
  527. if (onError != OnError.IGNORE) {
  528. cl = Class.forName(classname, true, al);
  529. }
  530. if (adapter != null) {
  531. adapterClass = Class.forName(adapter, true, al);
  532. }
  533. if (adaptTo != null) {
  534. adaptToClass = Class.forName(adaptTo, true, al);
  535. }
  536. AntTypeDefinition def = new AntTypeDefinition();
  537. def.setName(name);
  538. def.setClassName(classname);
  539. def.setClass(cl);
  540. def.setAdapterClass(adapterClass);
  541. def.setAdaptToClass(adaptToClass);
  542. def.setRestrict(restrict);
  543. def.setClassLoader(al);
  544. if (cl != null) {
  545. def.checkClass(getProject());
  546. }
  547. ComponentHelper.getComponentHelper(getProject())
  548. .addDataTypeDefinition(def);
  549. } catch (ClassNotFoundException cnfe) {
  550. String msg = getTaskName() + " class " + classname
  551. + " cannot be found"
  552. + "\n using the classloader " + al;
  553. throw new BuildException(msg, cnfe, getLocation());
  554. } catch (NoClassDefFoundError ncdfe) {
  555. String msg = getTaskName() + " A class needed by class "
  556. + classname + " cannot be found: " + ncdfe.getMessage()
  557. + "\n using the classloader " + al;
  558. throw new BuildException(msg, ncdfe, getLocation());
  559. }
  560. } catch (BuildException ex) {
  561. switch (onError) {
  562. case OnError.FAIL_ALL:
  563. case OnError.FAIL:
  564. throw ex;
  565. case OnError.REPORT:
  566. log(ex.getLocation() + "Warning: " + ex.getMessage(),
  567. Project.MSG_WARN);
  568. break;
  569. default:
  570. log(ex.getLocation() + ex.getMessage(),
  571. Project.MSG_DEBUG);
  572. }
  573. }
  574. }
  575. /**
  576. * handle too many definitions by raising an exception.
  577. * @throws BuildException always.
  578. */
  579. private void tooManyDefinitions() {
  580. throw new BuildException(
  581. "Only one of the attributes name, file and resource"
  582. + " can be set", getLocation());
  583. }
  584. }