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.

ProjectHelperRepository.java 13 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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;
  19. import java.io.BufferedReader;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.lang.reflect.Constructor;
  23. import java.net.URL;
  24. import java.util.ArrayList;
  25. import java.util.Enumeration;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import org.apache.tools.ant.helper.ProjectHelper2;
  29. import org.apache.tools.ant.types.Resource;
  30. import org.apache.tools.ant.util.LoaderUtils;
  31. /**
  32. * Repository of {@link ProjectHelper} found in the classpath or via
  33. * some System properties.
  34. *
  35. * <p>See the ProjectHelper documentation in the manual.</p>
  36. *
  37. * @since Ant 1.8.0
  38. */
  39. public class ProjectHelperRepository {
  40. private static final String DEBUG_PROJECT_HELPER_REPOSITORY =
  41. "ant.project-helper-repo.debug";
  42. // The message log level is not accessible here because everything
  43. // is instanciated statically
  44. private static final boolean DEBUG =
  45. "true".equals(System.getProperty(DEBUG_PROJECT_HELPER_REPOSITORY));
  46. private static ProjectHelperRepository instance =
  47. new ProjectHelperRepository();
  48. private List/* <Constructor> */ helpers = new ArrayList();
  49. private static final Class[] NO_CLASS = new Class[0];
  50. private static final Object[] NO_OBJECT = new Object[0];
  51. private static Constructor PROJECTHELPER2_CONSTRUCTOR;
  52. static {
  53. try {
  54. PROJECTHELPER2_CONSTRUCTOR = ProjectHelper2.class
  55. .getConstructor(NO_CLASS);
  56. } catch (Exception e) {
  57. // ProjectHelper2 must be available
  58. throw new RuntimeException(e);
  59. }
  60. }
  61. public static ProjectHelperRepository getInstance() {
  62. return instance;
  63. }
  64. private ProjectHelperRepository() {
  65. collectProjectHelpers();
  66. }
  67. private void collectProjectHelpers() {
  68. // First, try the system property
  69. Constructor projectHelper = getProjectHelperBySystemProperty();
  70. registerProjectHelper(projectHelper);
  71. // A JDK1.3 'service' ( like in JAXP ). That will plug a helper
  72. // automatically if in CLASSPATH, with the right META-INF/services.
  73. try {
  74. ClassLoader classLoader = LoaderUtils.getContextClassLoader();
  75. if (classLoader != null) {
  76. Enumeration resources =
  77. classLoader.getResources(ProjectHelper.SERVICE_ID);
  78. while (resources.hasMoreElements()) {
  79. URL resource = (URL) resources.nextElement();
  80. projectHelper =
  81. getProjectHelperByService(resource.openStream());
  82. registerProjectHelper(projectHelper);
  83. }
  84. }
  85. InputStream systemResource =
  86. ClassLoader.getSystemResourceAsStream(ProjectHelper.SERVICE_ID);
  87. if (systemResource != null) {
  88. projectHelper = getProjectHelperByService(systemResource);
  89. registerProjectHelper(projectHelper);
  90. }
  91. } catch (Exception e) {
  92. System.err.println("Unable to load ProjectHelper from service "
  93. + ProjectHelper.SERVICE_ID + " ("
  94. + e.getClass().getName()
  95. + ": " + e.getMessage() + ")");
  96. if (DEBUG) {
  97. e.printStackTrace(System.err);
  98. }
  99. }
  100. }
  101. /**
  102. * Register the specified project helper into the repository.
  103. * <p>
  104. * The helper will be added after all the already registered helpers, but
  105. * before the default one (ProjectHelper2)
  106. *
  107. * @param helperClassName
  108. * the fully qualified name of the helper
  109. * @throws BuildException
  110. * if the class cannot be loaded or if there is no constructor
  111. * with no argument
  112. * @since Ant 1.8.2
  113. */
  114. public void registerProjectHelper(String helperClassName)
  115. throws BuildException {
  116. registerProjectHelper(getHelperConstructor(helperClassName));
  117. }
  118. /**
  119. * Register the specified project helper into the repository.
  120. * <p>
  121. * The helper will be added after all the already registered helpers, but
  122. * before the default one (ProjectHelper2)
  123. *
  124. * @param helperClass
  125. * the class of the helper
  126. * @throws BuildException
  127. * if there is no constructor with no argument
  128. * @since Ant 1.8.2
  129. */
  130. public void registerProjectHelper(Class helperClass) throws BuildException {
  131. try {
  132. registerProjectHelper(helperClass.getConstructor(NO_CLASS));
  133. } catch (NoSuchMethodException e) {
  134. throw new BuildException("Couldn't find no-arg constructor in "
  135. + helperClass.getName());
  136. }
  137. }
  138. private void registerProjectHelper(Constructor helperConstructor) {
  139. if (helperConstructor == null) {
  140. return;
  141. }
  142. if (DEBUG) {
  143. System.out.println("ProjectHelper "
  144. + helperConstructor.getClass().getName() + " registered.");
  145. }
  146. helpers.add(helperConstructor);
  147. }
  148. private Constructor getProjectHelperBySystemProperty() {
  149. String helperClass = System.getProperty(ProjectHelper.HELPER_PROPERTY);
  150. try {
  151. if (helperClass != null) {
  152. return getHelperConstructor(helperClass);
  153. }
  154. } catch (SecurityException e) {
  155. System.err.println("Unable to load ProjectHelper class \""
  156. + helperClass + " specified in system property "
  157. + ProjectHelper.HELPER_PROPERTY + " ("
  158. + e.getMessage() + ")");
  159. if (DEBUG) {
  160. e.printStackTrace(System.err);
  161. }
  162. }
  163. return null;
  164. }
  165. private Constructor getProjectHelperByService(InputStream is) {
  166. try {
  167. // This code is needed by EBCDIC and other strange systems.
  168. // It's a fix for bugs reported in xerces
  169. InputStreamReader isr;
  170. try {
  171. isr = new InputStreamReader(is, "UTF-8");
  172. } catch (java.io.UnsupportedEncodingException e) {
  173. isr = new InputStreamReader(is);
  174. }
  175. BufferedReader rd = new BufferedReader(isr);
  176. String helperClassName = rd.readLine();
  177. rd.close();
  178. if (helperClassName != null && !"".equals(helperClassName)) {
  179. return getHelperConstructor(helperClassName);
  180. }
  181. } catch (Exception e) {
  182. System.out.println("Unable to load ProjectHelper from service "
  183. + ProjectHelper.SERVICE_ID + " (" + e.getMessage() + ")");
  184. if (DEBUG) {
  185. e.printStackTrace(System.err);
  186. }
  187. }
  188. return null;
  189. }
  190. /**
  191. * Get the constructor with not argument of an helper from its class name.
  192. * It'll first try the thread class loader, then Class.forName() will load
  193. * from the same loader that loaded this class.
  194. *
  195. * @param helperClass
  196. * The name of the class to create an instance of. Must not be
  197. * <code>null</code>.
  198. *
  199. * @return the constructor of the specified class.
  200. *
  201. * @exception BuildException
  202. * if the class cannot be found or if a constructor with no
  203. * argument cannot be found.
  204. */
  205. private Constructor getHelperConstructor(String helperClass) throws BuildException {
  206. ClassLoader classLoader = LoaderUtils.getContextClassLoader();
  207. try {
  208. Class clazz = null;
  209. if (classLoader != null) {
  210. try {
  211. clazz = classLoader.loadClass(helperClass);
  212. } catch (ClassNotFoundException ex) {
  213. // try next method
  214. }
  215. }
  216. if (clazz == null) {
  217. clazz = Class.forName(helperClass);
  218. }
  219. return clazz.getConstructor(NO_CLASS);
  220. } catch (Exception e) {
  221. throw new BuildException(e);
  222. }
  223. }
  224. /**
  225. * Get the helper that will be able to parse the specified build file. The helper
  226. * will be chosen among the ones found in the classpath
  227. *
  228. * @return the first ProjectHelper that fit the requirement (never <code>null</code>).
  229. */
  230. public ProjectHelper getProjectHelperForBuildFile(Resource buildFile) throws BuildException {
  231. Iterator it = getHelpers();
  232. while (it.hasNext()) {
  233. ProjectHelper helper = (ProjectHelper) it.next();
  234. if (helper.canParseBuildFile(buildFile)) {
  235. if (DEBUG) {
  236. System.out.println("ProjectHelper "
  237. + helper.getClass().getName()
  238. + " selected for the build file "
  239. + buildFile);
  240. }
  241. return helper;
  242. }
  243. }
  244. throw new RuntimeException("BUG: at least the ProjectHelper2 should "
  245. + "have supported the file " + buildFile);
  246. }
  247. /**
  248. * Get the helper that will be able to parse the specified antlib. The helper
  249. * will be chosen among the ones found in the classpath
  250. *
  251. * @return the first ProjectHelper that fit the requirement (never <code>null</code>).
  252. */
  253. public ProjectHelper getProjectHelperForAntlib(Resource antlib) throws BuildException {
  254. Iterator it = getHelpers();
  255. while (it.hasNext()) {
  256. ProjectHelper helper = (ProjectHelper) it.next();
  257. if (helper.canParseAntlibDescriptor(antlib)) {
  258. if (DEBUG) {
  259. System.out.println("ProjectHelper "
  260. + helper.getClass().getName()
  261. + " selected for the antlib "
  262. + antlib);
  263. }
  264. return helper;
  265. }
  266. }
  267. throw new RuntimeException("BUG: at least the ProjectHelper2 should "
  268. + "have supported the file " + antlib);
  269. }
  270. /**
  271. * Get an iterator on the list of project helpers configured. The iterator
  272. * will always return at least one element as there will always be the
  273. * default project helper configured.
  274. *
  275. * @return an iterator of {@link ProjectHelper}
  276. */
  277. public Iterator getHelpers() {
  278. return new ConstructingIterator(helpers.iterator());
  279. }
  280. private static class ConstructingIterator implements Iterator {
  281. private final Iterator nested;
  282. private boolean empty = false;
  283. ConstructingIterator(Iterator nested) {
  284. this.nested = nested;
  285. }
  286. public boolean hasNext() {
  287. return nested.hasNext() || !empty;
  288. }
  289. public Object next() {
  290. Constructor c;
  291. if (nested.hasNext()) {
  292. c = (Constructor) nested.next();
  293. } else {
  294. // last but not least, ant default project helper
  295. empty = true;
  296. c = PROJECTHELPER2_CONSTRUCTOR;
  297. }
  298. try {
  299. return c.newInstance(NO_OBJECT);
  300. } catch (Exception e) {
  301. throw new BuildException("Failed to invoke no-arg constructor"
  302. + " on " + c.getName());
  303. }
  304. }
  305. public void remove() {
  306. throw new UnsupportedOperationException("remove is not supported");
  307. }
  308. }
  309. }