1. /*
  2. * Copyright (c) 2004 World Wide Web Consortium,
  3. *
  4. * (Massachusetts Institute of Technology, European Research Consortium for
  5. * Informatics and Mathematics, Keio University). All Rights Reserved. This
  6. * work is distributed under the W3C(r) Software License [1] in the hope that
  7. * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
  8. * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. *
  10. * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
  11. */
  12. package org.w3c.dom.bootstrap;
  13. import java.util.StringTokenizer;
  14. import java.util.Vector;
  15. import org.w3c.dom.DOMImplementationSource;
  16. import org.w3c.dom.DOMImplementationList;
  17. import org.w3c.dom.DOMImplementation;
  18. import java.io.InputStream;
  19. import java.io.BufferedReader;
  20. import java.io.InputStreamReader;
  21. import java.security.AccessController;
  22. import java.security.PrivilegedAction;
  23. /**
  24. * A factory that enables applications to obtain instances of
  25. * <code>DOMImplementation</code>.
  26. *
  27. * <p>
  28. * Example:
  29. * </p>
  30. *
  31. * <pre class='example'>
  32. * // get an instance of the DOMImplementation registry
  33. * DOMImplementationRegistry registry =
  34. * DOMImplementationRegistry.newInstance();
  35. * // get a DOM implementation the Level 3 XML module
  36. * DOMImplementation domImpl =
  37. * registry.getDOMImplementation("XML 3.0");
  38. * </pre>
  39. *
  40. * <p>
  41. * This provides an application with an implementation-independent starting
  42. * point. DOM implementations may modify this class to meet new security
  43. * standards or to provide *additional* fallbacks for the list of
  44. * DOMImplementationSources.
  45. * </p>
  46. *
  47. * @see DOMImplementation
  48. * @see DOMImplementationSource
  49. * @since DOM Level 3
  50. */
  51. public final class DOMImplementationRegistry {
  52. /**
  53. * The system property to specify the
  54. * DOMImplementationSource class names.
  55. */
  56. public static final String PROPERTY =
  57. "org.w3c.dom.DOMImplementationSourceList";
  58. /**
  59. * Default columns per line.
  60. */
  61. private static final int DEFAULT_LINE_LENGTH = 80;
  62. /**
  63. * The list of DOMImplementationSources.
  64. */
  65. private Vector sources;
  66. /**
  67. * Private constructor.
  68. * @param srcs Vector List of DOMImplementationSources
  69. */
  70. private DOMImplementationRegistry(final Vector srcs) {
  71. sources = srcs;
  72. }
  73. /**
  74. * Obtain a new instance of a <code>DOMImplementationRegistry</code>.
  75. *
  76. * The <code>DOMImplementationRegistry</code> is initialized by the
  77. * application or the implementation, depending on the context, by
  78. * first checking the value of the Java system property
  79. * <code>org.w3c.dom.DOMImplementationSourceList</code> and
  80. * the the service provider whose contents are at
  81. * "<code>META_INF/services/org.w3c.dom.DOMImplementationSourceList</code>"
  82. * The value of this property is a white-space separated list of
  83. * names of availables classes implementing the
  84. * <code>DOMImplementationSource</code> interface. Each class listed
  85. * in the class name list is instantiated and any exceptions
  86. * encountered are thrown to the application.
  87. *
  88. * @return an initialized instance of DOMImplementationRegistry
  89. * @throws ClassNotFoundException
  90. * If any specified class can not be found
  91. * @throws InstantiationException
  92. * If any specified class is an interface or abstract class
  93. * @throws IllegalAccessException
  94. * If the default constructor of a specified class is not accessible
  95. * @throws ClassCastException
  96. * If any specified class does not implement
  97. * <code>DOMImplementationSource</code>
  98. */
  99. public static DOMImplementationRegistry newInstance()
  100. throws
  101. ClassNotFoundException,
  102. InstantiationException,
  103. IllegalAccessException,
  104. ClassCastException {
  105. Vector sources = new Vector();
  106. ClassLoader classLoader = getClassLoader();
  107. // fetch system property:
  108. String p = getSystemProperty(PROPERTY);
  109. //
  110. // if property is not specified then use contents of
  111. // META_INF/org.w3c.dom.DOMImplementationSourceList from classpath
  112. if (p == null) {
  113. p = getServiceValue(classLoader);
  114. }
  115. if (p == null) {
  116. //
  117. // DOM Implementations can modify here to add *additional* fallback
  118. // mechanisms to access a list of default DOMImplementationSources.
  119. }
  120. if (p != null) {
  121. StringTokenizer st = new StringTokenizer(p);
  122. while (st.hasMoreTokens()) {
  123. String sourceName = st.nextToken();
  124. // Use context class loader, falling back to Class.forName
  125. // if and only if this fails...
  126. Class sourceClass = null;
  127. if (classLoader != null) {
  128. sourceClass = classLoader.loadClass(sourceName);
  129. } else {
  130. sourceClass = Class.forName(sourceName);
  131. }
  132. DOMImplementationSource source =
  133. (DOMImplementationSource) sourceClass.newInstance();
  134. sources.addElement(source);
  135. }
  136. }
  137. return new DOMImplementationRegistry(sources);
  138. }
  139. /**
  140. * Return the first implementation that has the desired
  141. * features, or <code>null</code> if none is found.
  142. *
  143. * @param features
  144. * A string that specifies which features are required. This is
  145. * a space separated list in which each feature is specified by
  146. * its name optionally followed by a space and a version number.
  147. * This is something like: "XML 1.0 Traversal +Events 2.0"
  148. * @return An implementation that has the desired features,
  149. * or <code>null</code> if none found.
  150. */
  151. public DOMImplementation getDOMImplementation(final String features) {
  152. int size = sources.size();
  153. String name = null;
  154. for (int i = 0; i < size; i++) {
  155. DOMImplementationSource source =
  156. (DOMImplementationSource) sources.elementAt(i);
  157. DOMImplementation impl = source.getDOMImplementation(features);
  158. if (impl != null) {
  159. return impl;
  160. }
  161. }
  162. return null;
  163. }
  164. /**
  165. * Return a list of implementations that support the
  166. * desired features.
  167. *
  168. * @param features
  169. * A string that specifies which features are required. This is
  170. * a space separated list in which each feature is specified by
  171. * its name optionally followed by a space and a version number.
  172. * This is something like: "XML 1.0 Traversal +Events 2.0"
  173. * @return A list of DOMImplementations that support the desired features.
  174. */
  175. public DOMImplementationList getDOMImplementationList(final String features) {
  176. final Vector implementations = new Vector();
  177. int size = sources.size();
  178. for (int i = 0; i < size; i++) {
  179. DOMImplementationSource source =
  180. (DOMImplementationSource) sources.elementAt(i);
  181. DOMImplementationList impls =
  182. source.getDOMImplementationList(features);
  183. for (int j = 0; j < impls.getLength(); j++) {
  184. DOMImplementation impl = impls.item(j);
  185. implementations.addElement(impl);
  186. }
  187. }
  188. return new DOMImplementationList() {
  189. public DOMImplementation item(final int index) {
  190. if (index >= 0 && index < implementations.size()) {
  191. try {
  192. return (DOMImplementation)
  193. implementations.elementAt(index);
  194. } catch (ArrayIndexOutOfBoundsException e) {
  195. return null;
  196. }
  197. }
  198. return null;
  199. }
  200. public int getLength() {
  201. return implementations.size();
  202. }
  203. };
  204. }
  205. /**
  206. * Register an implementation.
  207. *
  208. * @param s The source to be registered, may not be <code>null</code>
  209. */
  210. public void addSource(final DOMImplementationSource s) {
  211. if (s == null) {
  212. throw new NullPointerException();
  213. }
  214. if (!sources.contains(s)) {
  215. sources.addElement(s);
  216. }
  217. }
  218. /**
  219. *
  220. * Gets a class loader.
  221. *
  222. * @return A class loader, possibly <code>null</code>
  223. */
  224. private static ClassLoader getClassLoader() {
  225. try {
  226. ClassLoader contextClassLoader = getContextClassLoader();
  227. if (contextClassLoader != null) {
  228. return contextClassLoader;
  229. }
  230. } catch (Exception e) {
  231. // Assume that the DOM application is in a JRE 1.1, use the
  232. // current ClassLoader
  233. return DOMImplementationRegistry.class.getClassLoader();
  234. }
  235. return DOMImplementationRegistry.class.getClassLoader();
  236. }
  237. /**
  238. * This method attempts to return the first line of the resource
  239. * META_INF/services/org.w3c.dom.DOMImplementationSourceList
  240. * from the provided ClassLoader.
  241. *
  242. * @param classLoader classLoader, may not be <code>null</code>.
  243. * @return first line of resource, or <code>null</code>
  244. */
  245. private static String getServiceValue(final ClassLoader classLoader) {
  246. String serviceId = "META-INF/services/" + PROPERTY;
  247. // try to find services in CLASSPATH
  248. try {
  249. InputStream is = getResourceAsStream(classLoader, serviceId);
  250. if (is != null) {
  251. BufferedReader rd;
  252. try {
  253. rd =
  254. new BufferedReader(new InputStreamReader(is, "UTF-8"),
  255. DEFAULT_LINE_LENGTH);
  256. } catch (java.io.UnsupportedEncodingException e) {
  257. rd =
  258. new BufferedReader(new InputStreamReader(is),
  259. DEFAULT_LINE_LENGTH);
  260. }
  261. String serviceValue = rd.readLine();
  262. rd.close();
  263. if (serviceValue != null && serviceValue.length() > 0) {
  264. return serviceValue;
  265. }
  266. }
  267. } catch (Exception ex) {
  268. return null;
  269. }
  270. return null;
  271. }
  272. /**
  273. * A simple JRE (Java Runtime Environment) 1.1 test
  274. *
  275. * @return <code>true</code> if JRE 1.1
  276. */
  277. private static boolean isJRE11() {
  278. try {
  279. Class c = Class.forName("java.security.AccessController");
  280. // java.security.AccessController existed since 1.2 so, if no
  281. // exception was thrown, the DOM application is running in a JRE
  282. // 1.2 or higher
  283. return false;
  284. } catch (Exception ex) {
  285. // ignore
  286. }
  287. return true;
  288. }
  289. /**
  290. * This method returns the ContextClassLoader or <code>null</code> if
  291. * running in a JRE 1.1
  292. *
  293. * @return The Context Classloader
  294. */
  295. private static ClassLoader getContextClassLoader() {
  296. return isJRE11()
  297. ? null
  298. : (ClassLoader)
  299. AccessController.doPrivileged(new PrivilegedAction() {
  300. public Object run() {
  301. ClassLoader classLoader = null;
  302. try {
  303. classLoader =
  304. Thread.currentThread().getContextClassLoader();
  305. } catch (SecurityException ex) {
  306. }
  307. return classLoader;
  308. }
  309. });
  310. }
  311. /**
  312. * This method returns the system property indicated by the specified name
  313. * after checking access control privileges. For a JRE 1.1, this check is
  314. * not done.
  315. *
  316. * @param name the name of the system property
  317. * @return the system property
  318. */
  319. private static String getSystemProperty(final String name) {
  320. return isJRE11()
  321. ? (String) System.getProperty(name)
  322. : (String) AccessController.doPrivileged(new PrivilegedAction() {
  323. public Object run() {
  324. return System.getProperty(name);
  325. }
  326. });
  327. }
  328. /**
  329. * This method returns an Inputstream for the reading resource
  330. * META_INF/services/org.w3c.dom.DOMImplementationSourceList after checking
  331. * access control privileges. For a JRE 1.1, this check is not done.
  332. *
  333. * @param classLoader classLoader
  334. * @param name the resource
  335. * @return an Inputstream for the resource specified
  336. */
  337. private static InputStream getResourceAsStream(final ClassLoader classLoader,
  338. final String name) {
  339. if (isJRE11()) {
  340. InputStream ris;
  341. if (classLoader == null) {
  342. ris = ClassLoader.getSystemResourceAsStream(name);
  343. } else {
  344. ris = classLoader.getResourceAsStream(name);
  345. }
  346. return ris;
  347. } else {
  348. return (InputStream)
  349. AccessController.doPrivileged(new PrivilegedAction() {
  350. public Object run() {
  351. InputStream ris;
  352. if (classLoader == null) {
  353. ris =
  354. ClassLoader.getSystemResourceAsStream(name);
  355. } else {
  356. ris = classLoader.getResourceAsStream(name);
  357. }
  358. return ris;
  359. }
  360. });
  361. }
  362. }
  363. }