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.

ComponentHelper.java 25 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2003 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 "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;
  55. import org.apache.tools.ant.util.LazyHashtable;
  56. import org.apache.tools.ant.util.WeakishReference;
  57. import java.util.Enumeration;
  58. import java.util.Hashtable;
  59. import java.util.Properties;
  60. import java.util.Vector;
  61. import java.io.InputStream;
  62. import java.io.IOException;
  63. import java.lang.reflect.Modifier;
  64. /**
  65. * Component creation and configuration.
  66. *
  67. * This is cut&paste from Project.java of everything related to
  68. * task/type management. Project will just delegate.
  69. *
  70. * A very simple hook mechnism is provided that allows users to plug
  71. * in custom code. It is also possible to replace the default behavior
  72. * ( for example in an app embeding ant )
  73. *
  74. * @author Costin Manolache
  75. * @since Ant1.6
  76. */
  77. public class ComponentHelper {
  78. /** Map from data type names to implementing classes (String to Class). */
  79. private Hashtable dataClassDefinitions;
  80. /** Map from task names to implementing classes (String to Class). */
  81. private Hashtable taskClassDefinitions;
  82. /**
  83. * Map from task names to vectors of created tasks
  84. * (String to Vector of Task). This is used to invalidate tasks if
  85. * the task definition changes.
  86. */
  87. private Hashtable createdTasks = new Hashtable();
  88. protected ComponentHelper next;
  89. protected Project project;
  90. /**
  91. */
  92. public static ComponentHelper getComponentHelper(Project project) {
  93. // Singleton for now, it may change ( per/classloader )
  94. ComponentHelper ph=(ComponentHelper)project.getReference( "ant.ComponentHelper" );
  95. if( ph!=null ) return ph;
  96. ph=new ComponentHelper();
  97. ph.setProject( project );
  98. project.addReference( "ant.ComponentHelper",ph );
  99. return ph;
  100. }
  101. protected ComponentHelper() {
  102. }
  103. public void setNext( ComponentHelper next ) {
  104. this.next=next;
  105. }
  106. public ComponentHelper getNext() {
  107. return next;
  108. }
  109. public void setProject(Project project) {
  110. this.project = project;
  111. dataClassDefinitions= new AntTaskTable(project, false);
  112. taskClassDefinitions= new AntTaskTable(project, true);
  113. }
  114. /** Creates an ant component..
  115. *
  116. * A factory may have knowledge about the tasks it creates. It can return
  117. * an object extending TaskAdapter that emulates Task/DataType. If null is returned,
  118. * the next helper is tried.
  119. *
  120. * @param ns namespace if a SAX2 parser is used, null for 'classical' ant
  121. * @param taskName the (local) name of the task.
  122. */
  123. public Object createComponent( String ns,
  124. String taskName )
  125. throws BuildException
  126. {
  127. if( getNext() != null ) {
  128. return getNext().createComponent( ns, taskName);
  129. }
  130. return null;
  131. // XXX class loader ? Can use the ns, but additional hints may be available in taskdef
  132. //
  133. }
  134. public Object createComponent( UnknownElement ue,
  135. String ns,
  136. String taskName )
  137. throws BuildException
  138. {
  139. Object component=null;
  140. // System.out.println("Fallback to project default " + taskName );
  141. // Can't create component. Default is to use the old methods in project.
  142. // This policy is taken from 1.5 ProjectHelper. In future the difference between
  143. // task and type should disapear.
  144. if( project.getDataTypeDefinitions().get(taskName) != null ) {
  145. // This is the original policy in ProjectHelper. The 1.5 version of UnkwnonwElement
  146. // used to try first to create a task, and if it failed tried a type. In 1.6 the diff
  147. // should disapear.
  148. component = project.createDataType(taskName);
  149. if( component!=null ) return component;
  150. }
  151. // from UnkwnonwElement.createTask. The 'top level' case is removed, we're
  152. // allways lazy
  153. component = project.createTask(taskName);
  154. return component;
  155. }
  156. public void initDefaultDefinitions() throws BuildException {
  157. String defs = "/org/apache/tools/ant/taskdefs/defaults.properties";
  158. try {
  159. Properties props = new Properties();
  160. InputStream in = this.getClass().getResourceAsStream(defs);
  161. if (in == null) {
  162. throw new BuildException("Can't load default task list");
  163. }
  164. props.load(in);
  165. in.close();
  166. ((AntTaskTable)taskClassDefinitions).addDefinitions( props );
  167. } catch (IOException ioe) {
  168. throw new BuildException("Can't load default task list");
  169. }
  170. String dataDefs = "/org/apache/tools/ant/types/defaults.properties";
  171. try {
  172. Properties props = new Properties();
  173. InputStream in = this.getClass().getResourceAsStream(dataDefs);
  174. if (in == null) {
  175. throw new BuildException("Can't load default datatype list");
  176. }
  177. props.load(in);
  178. in.close();
  179. ((AntTaskTable)dataClassDefinitions).addDefinitions(props);
  180. } catch (IOException ioe) {
  181. throw new BuildException("Can't load default datatype list");
  182. }
  183. }
  184. /**
  185. * Adds a new task definition to the project.
  186. * Attempting to override an existing definition with an
  187. * equivalent one (i.e. with the same classname) results in
  188. * a verbose log message. Attempting to override an existing definition
  189. * with a different one results in a warning log message and
  190. * invalidates any tasks which have already been created with the
  191. * old definition.
  192. *
  193. * @param taskName The name of the task to add.
  194. * Must not be <code>null</code>.
  195. * @param taskClass The full name of the class implementing the task.
  196. * Must not be <code>null</code>.
  197. *
  198. * @exception BuildException if the class is unsuitable for being an Ant
  199. * task. An error level message is logged before
  200. * this exception is thrown.
  201. *
  202. * @see #checkTaskClass(Class)
  203. - */
  204. public void addTaskDefinition(String taskName, Class taskClass)
  205. throws BuildException {
  206. Class old = (Class) taskClassDefinitions.get(taskName);
  207. if (null != old) {
  208. if (old.equals(taskClass)) {
  209. // project.log("Ignoring override for task " + taskName
  210. // + ", it is already defined by the same class.",
  211. // Project.MSG_VERBOSE);
  212. return;
  213. } else {
  214. int logLevel = Project.MSG_WARN;
  215. if (old.getName().equals(taskClass.getName())) {
  216. ClassLoader oldLoader = old.getClassLoader();
  217. ClassLoader newLoader = taskClass.getClassLoader();
  218. // system classloader on older JDKs can be null
  219. if (oldLoader != null
  220. && newLoader != null
  221. && oldLoader instanceof AntClassLoader
  222. && newLoader instanceof AntClassLoader
  223. && ((AntClassLoader) oldLoader).getClasspath()
  224. .equals(((AntClassLoader) newLoader).getClasspath())
  225. ) {
  226. // same classname loaded from the same
  227. // classpath components
  228. logLevel = Project.MSG_VERBOSE;
  229. }
  230. }
  231. project.log("Trying to override old definition of task " + taskName,
  232. logLevel);
  233. invalidateCreatedTasks(taskName);
  234. }
  235. }
  236. String msg = " +User task: " + taskName + " " + taskClass.getName();
  237. project.log(msg, Project.MSG_DEBUG);
  238. checkTaskClass(taskClass);
  239. taskClassDefinitions.put(taskName, taskClass);
  240. }
  241. /**
  242. * Checks whether or not a class is suitable for serving as Ant task.
  243. * Ant task implementation classes must be public, concrete, and have
  244. * a no-arg constructor.
  245. *
  246. * @param taskClass The class to be checked.
  247. * Must not be <code>null</code>.
  248. *
  249. * @exception BuildException if the class is unsuitable for being an Ant
  250. * task. An error level message is logged before
  251. * this exception is thrown.
  252. */
  253. public void checkTaskClass(final Class taskClass) throws BuildException {
  254. if (!Modifier.isPublic(taskClass.getModifiers())) {
  255. final String message = taskClass + " is not public";
  256. project.log(message, Project.MSG_ERR);
  257. throw new BuildException(message);
  258. }
  259. if (Modifier.isAbstract(taskClass.getModifiers())) {
  260. final String message = taskClass + " is abstract";
  261. project.log(message, Project.MSG_ERR);
  262. throw new BuildException(message);
  263. }
  264. try {
  265. taskClass.getConstructor(null);
  266. // don't have to check for public, since
  267. // getConstructor finds public constructors only.
  268. } catch (NoSuchMethodException e) {
  269. final String message = "No public no-arg constructor in "
  270. + taskClass;
  271. project.log(message, Project.MSG_ERR);
  272. throw new BuildException(message);
  273. }
  274. if (!Task.class.isAssignableFrom(taskClass)) {
  275. TaskAdapter.checkTaskClass(taskClass, project);
  276. }
  277. }
  278. /**
  279. * Returns the current task definition hashtable. The returned hashtable is
  280. * "live" and so should not be modified.
  281. *
  282. * @return a map of from task name to implementing class
  283. * (String to Class).
  284. */
  285. public Hashtable getTaskDefinitions() {
  286. return taskClassDefinitions;
  287. }
  288. /**
  289. * Adds a new datatype definition.
  290. * Attempting to override an existing definition with an
  291. * equivalent one (i.e. with the same classname) results in
  292. * a verbose log message. Attempting to override an existing definition
  293. * with a different one results in a warning log message, but the
  294. * definition is changed.
  295. *
  296. * @param typeName The name of the datatype.
  297. * Must not be <code>null</code>.
  298. * @param typeClass The full name of the class implementing the datatype.
  299. * Must not be <code>null</code>.
  300. */
  301. public void addDataTypeDefinition(String typeName, Class typeClass) {
  302. synchronized(dataClassDefinitions) {
  303. Class old = (Class) dataClassDefinitions.get(typeName);
  304. if (null != old) {
  305. if (old.equals(typeClass)) {
  306. // project.log("Ignoring override for datatype " + typeName
  307. // + ", it is already defined by the same class.",
  308. // Project.MSG_VERBOSE);
  309. return;
  310. } else {
  311. project.log("Trying to override old definition of datatype "
  312. + typeName, Project.MSG_WARN);
  313. }
  314. }
  315. dataClassDefinitions.put(typeName, typeClass);
  316. }
  317. String msg = " +User datatype: " + typeName + " "
  318. + typeClass.getName();
  319. project.log(msg, Project.MSG_DEBUG);
  320. }
  321. /**
  322. * Returns the current datatype definition hashtable. The returned
  323. * hashtable is "live" and so should not be modified.
  324. *
  325. * @return a map of from datatype name to implementing class
  326. * (String to Class).
  327. */
  328. public Hashtable getDataTypeDefinitions() {
  329. return dataClassDefinitions;
  330. }
  331. /**
  332. * Creates a new instance of a task, adding it to a list of
  333. * created tasks for later invalidation. This causes all tasks
  334. * to be remembered until the containing project is removed
  335. * @param taskType The name of the task to create an instance of.
  336. * Must not be <code>null</code>.
  337. *
  338. * @return an instance of the specified task, or <code>null</code> if
  339. * the task name is not recognised.
  340. *
  341. * @exception BuildException if the task name is recognised but task
  342. * creation fails.
  343. */
  344. public Task createTask(String taskType) throws BuildException {
  345. Task task=createNewTask(taskType);
  346. if(task!=null) {
  347. addCreatedTask(taskType, task);
  348. }
  349. return task;
  350. }
  351. /**
  352. * Creates a new instance of a task. This task is not
  353. * cached in the createdTasks list.
  354. * @since ant1.6
  355. * @param taskType The name of the task to create an instance of.
  356. * Must not be <code>null</code>.
  357. *
  358. * @return an instance of the specified task, or <code>null</code> if
  359. * the task name is not recognised.
  360. *
  361. * @exception BuildException if the task name is recognised but task
  362. * creation fails.
  363. */
  364. private Task createNewTask(String taskType) throws BuildException {
  365. Class c = (Class) taskClassDefinitions.get(taskType);
  366. if (c == null) {
  367. return null;
  368. }
  369. try {
  370. Object o = c.newInstance();
  371. Task task = null;
  372. if (o instanceof Task) {
  373. task = (Task) o;
  374. } else {
  375. // "Generic" Bean - use the setter pattern
  376. // and an Adapter
  377. TaskAdapter taskA = new TaskAdapter();
  378. taskA.setProxy(o);
  379. task = taskA;
  380. }
  381. task.setProject(project);
  382. task.setTaskType(taskType);
  383. // set default value, can be changed by the user
  384. task.setTaskName(taskType);
  385. String msg = " +Task: " + taskType;
  386. project.log (msg, Project.MSG_DEBUG);
  387. return task;
  388. } catch (Throwable t) {
  389. System.out.println("task CL=" + c.getClassLoader());
  390. String msg = "Could not create task of type: "
  391. + taskType + " due to " + t;
  392. throw new BuildException(msg, t);
  393. }
  394. }
  395. /**
  396. * Keeps a record of all tasks that have been created so that they
  397. * can be invalidated if a new task definition overrides the current one.
  398. *
  399. * @param type The name of the type of task which has been created.
  400. * Must not be <code>null</code>.
  401. *
  402. * @param task The freshly created task instance.
  403. * Must not be <code>null</code>.
  404. */
  405. private void addCreatedTask(String type, Task task) {
  406. synchronized (createdTasks) {
  407. Vector v = (Vector) createdTasks.get(type);
  408. if (v == null) {
  409. v = new Vector();
  410. createdTasks.put(type, v);
  411. }
  412. v.addElement(WeakishReference.createReference(task));
  413. }
  414. }
  415. /**
  416. * Mark tasks as invalid which no longer are of the correct type
  417. * for a given taskname.
  418. *
  419. * @param type The name of the type of task to invalidate.
  420. * Must not be <code>null</code>.
  421. */
  422. private void invalidateCreatedTasks(String type) {
  423. synchronized (createdTasks) {
  424. Vector v = (Vector) createdTasks.get(type);
  425. if (v != null) {
  426. Enumeration enum = v.elements();
  427. while (enum.hasMoreElements()) {
  428. WeakishReference ref=
  429. (WeakishReference) enum.nextElement();
  430. Task t = (Task) ref.get();
  431. //being a weak ref, it may be null by this point
  432. if(t!=null) {
  433. t.markInvalid();
  434. }
  435. }
  436. v.removeAllElements();
  437. createdTasks.remove(type);
  438. }
  439. }
  440. }
  441. /**
  442. * Creates a new instance of a data type.
  443. *
  444. * @param typeName The name of the data type to create an instance of.
  445. * Must not be <code>null</code>.
  446. *
  447. * @return an instance of the specified data type, or <code>null</code> if
  448. * the data type name is not recognised.
  449. *
  450. * @exception BuildException if the data type name is recognised but
  451. * instance creation fails.
  452. */
  453. public Object createDataType(String typeName) throws BuildException {
  454. Class c = (Class) dataClassDefinitions.get(typeName);
  455. if (c == null) {
  456. return null;
  457. }
  458. try {
  459. java.lang.reflect.Constructor ctor = null;
  460. boolean noArg = false;
  461. // DataType can have a "no arg" constructor or take a single
  462. // Project argument.
  463. try {
  464. ctor = c.getConstructor(new Class[0]);
  465. noArg = true;
  466. } catch (NoSuchMethodException nse) {
  467. ctor = c.getConstructor(new Class[] {Project.class});
  468. noArg = false;
  469. }
  470. Object o = null;
  471. if (noArg) {
  472. o = ctor.newInstance(new Object[0]);
  473. } else {
  474. o = ctor.newInstance(new Object[] {this});
  475. }
  476. if (o instanceof ProjectComponent) {
  477. ((ProjectComponent) o).setProject(project);
  478. }
  479. String msg = " +DataType: " + typeName;
  480. project.log(msg, Project.MSG_DEBUG);
  481. return o;
  482. } catch (java.lang.reflect.InvocationTargetException ite) {
  483. Throwable t = ite.getTargetException();
  484. String msg = "Could not create datatype of type: "
  485. + typeName + " due to " + t;
  486. throw new BuildException(msg, t);
  487. } catch (Throwable t) {
  488. String msg = "Could not create datatype of type: "
  489. + typeName + " due to " + t;
  490. throw new BuildException(msg, t);
  491. }
  492. }
  493. /**
  494. * Returns a description of the type of the given element, with
  495. * special handling for instances of tasks and data types.
  496. * <p>
  497. * This is useful for logging purposes.
  498. *
  499. * @param element The element to describe.
  500. * Must not be <code>null</code>.
  501. *
  502. * @return a description of the element type
  503. *
  504. * @since Ant 1.6
  505. */
  506. public String getElementName(Object element) {
  507. Hashtable elements = taskClassDefinitions;
  508. Class elementClass = element.getClass();
  509. String typeName = "task";
  510. if (!elements.contains(elementClass)) {
  511. elements = dataClassDefinitions;
  512. typeName = "data type";
  513. if (!elements.contains(elementClass)) {
  514. elements = null;
  515. }
  516. }
  517. if (elements != null) {
  518. Enumeration e = elements.keys();
  519. while (e.hasMoreElements()) {
  520. String name = (String) e.nextElement();
  521. Class clazz = (Class) elements.get(name);
  522. if (elementClass.equals(clazz)) {
  523. return "The <" + name + "> " + typeName;
  524. }
  525. }
  526. }
  527. return "Class " + elementClass.getName();
  528. }
  529. private static class AntTaskTable extends LazyHashtable {
  530. Project project;
  531. Properties props;
  532. boolean tasks=false;
  533. public AntTaskTable( Project p, boolean tasks ) {
  534. this.project=p;
  535. this.tasks=tasks;
  536. }
  537. public void addDefinitions( Properties props ) {
  538. this.props=props;
  539. }
  540. protected void initAll( ) {
  541. if( initAllDone ) return;
  542. project.log("InitAll", Project.MSG_DEBUG);
  543. if( props==null ) return;
  544. Enumeration enum = props.propertyNames();
  545. while (enum.hasMoreElements()) {
  546. String key = (String) enum.nextElement();
  547. Class taskClass=getTask( key );
  548. if( taskClass!=null ) {
  549. // This will call a get() and a put()
  550. if( tasks )
  551. project.addTaskDefinition(key, taskClass);
  552. else
  553. project.addDataTypeDefinition(key, taskClass );
  554. }
  555. }
  556. initAllDone=true;
  557. }
  558. protected Class getTask(String key) {
  559. if( props==null ) return null; // for tasks loaded before init()
  560. String value=props.getProperty(key);
  561. if( value==null) {
  562. //project.log( "No class name for " + key, Project.MSG_VERBOSE );
  563. return null;
  564. }
  565. try {
  566. Class taskClass=null;
  567. if( project.getCoreLoader() != null &&
  568. !("only".equals(project.getProperty("build.sysclasspath")))) {
  569. try {
  570. project.log("Loading with the core loader " + value,
  571. Project.MSG_DEBUG);
  572. taskClass=project.getCoreLoader().loadClass(value);
  573. if( taskClass != null ) return taskClass;
  574. } catch( Exception ex ) {
  575. }
  576. }
  577. taskClass = Class.forName(value);
  578. return taskClass;
  579. } catch (NoClassDefFoundError ncdfe) {
  580. project.log("Could not load a dependent class ("
  581. + ncdfe.getMessage() + ") for task " + key, Project.MSG_DEBUG);
  582. } catch (ClassNotFoundException cnfe) {
  583. project.log("Could not load class (" + value
  584. + ") for task " + key, Project.MSG_DEBUG);
  585. }
  586. return null;
  587. }
  588. // Hashtable implementation
  589. public Object get( Object key ) {
  590. Object orig=super.get( key );
  591. if( orig!= null ) return orig;
  592. if( ! (key instanceof String) ) return null;
  593. project.log("Get task " + key, Project.MSG_DEBUG );
  594. Object taskClass=getTask( (String) key);
  595. if( taskClass != null)
  596. super.put( key, taskClass );
  597. return taskClass;
  598. }
  599. public boolean contains( Object key ) {
  600. return get( key ) != null;
  601. }
  602. }
  603. }