001 /*
002 * Copyright (c) 2004 World Wide Web Consortium,
003 *
004 * (Massachusetts Institute of Technology, European Research Consortium for
005 * Informatics and Mathematics, Keio University). All Rights Reserved. This
006 * work is distributed under the W3C(r) Software License [1] in the hope that
007 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
008 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
009 *
010 * [1] http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
011 */
012
013
014 package org.w3c.dom.bootstrap;
015
016 import java.util.StringTokenizer;
017 import java.util.Vector;
018 import org.w3c.dom.DOMImplementationSource;
019 import org.w3c.dom.DOMImplementationList;
020 import org.w3c.dom.DOMImplementation;
021 import java.io.InputStream;
022 import java.io.BufferedReader;
023 import java.io.InputStreamReader;
024 import java.security.AccessController;
025 import java.security.PrivilegedAction;
026
027 /**
028 * A factory that enables applications to obtain instances of
029 * <code>DOMImplementation</code>.
030 *
031 * <p>
032 * Example:
033 * </p>
034 *
035 * <pre class='example'>
036 * // get an instance of the DOMImplementation registry
037 * DOMImplementationRegistry registry =
038 * DOMImplementationRegistry.newInstance();
039 * // get a DOM implementation the Level 3 XML module
040 * DOMImplementation domImpl =
041 * registry.getDOMImplementation("XML 3.0");
042 * </pre>
043 *
044 * <p>
045 * This provides an application with an implementation-independent starting
046 * point. DOM implementations may modify this class to meet new security
047 * standards or to provide *additional* fallbacks for the list of
048 * DOMImplementationSources.
049 * </p>
050 *
051 * @see DOMImplementation
052 * @see DOMImplementationSource
053 * @since DOM Level 3
054 */
055 public final class DOMImplementationRegistry {
056 /**
057 * The system property to specify the
058 * DOMImplementationSource class names.
059 */
060 public static final String PROPERTY =
061 "org.w3c.dom.DOMImplementationSourceList";
062
063 /**
064 * Default columns per line.
065 */
066 private static final int DEFAULT_LINE_LENGTH = 80;
067
068 /**
069 * The list of DOMImplementationSources.
070 */
071 private Vector sources;
072
073 /**
074 * Private constructor.
075 * @param srcs Vector List of DOMImplementationSources
076 */
077 private DOMImplementationRegistry(final Vector srcs) {
078 sources = srcs;
079 }
080
081 /**
082 * Obtain a new instance of a <code>DOMImplementationRegistry</code>.
083 *
084
085 * The <code>DOMImplementationRegistry</code> is initialized by the
086 * application or the implementation, depending on the context, by
087 * first checking the value of the Java system property
088 * <code>org.w3c.dom.DOMImplementationSourceList</code> and
089 * the the service provider whose contents are at
090 * "<code>META_INF/services/org.w3c.dom.DOMImplementationSourceList</code>"
091 * The value of this property is a white-space separated list of
092 * names of availables classes implementing the
093 * <code>DOMImplementationSource</code> interface. Each class listed
094 * in the class name list is instantiated and any exceptions
095 * encountered are thrown to the application.
096 *
097 * @return an initialized instance of DOMImplementationRegistry
098 * @throws ClassNotFoundException
099 * If any specified class can not be found
100 * @throws InstantiationException
101 * If any specified class is an interface or abstract class
102 * @throws IllegalAccessException
103 * If the default constructor of a specified class is not accessible
104 * @throws ClassCastException
105 * If any specified class does not implement
106 * <code>DOMImplementationSource</code>
107 */
108 public static DOMImplementationRegistry newInstance()
109 throws
110 ClassNotFoundException,
111 InstantiationException,
112 IllegalAccessException,
113 ClassCastException {
114 Vector sources = new Vector();
115
116 ClassLoader classLoader = getClassLoader();
117 // fetch system property:
118 String p = getSystemProperty(PROPERTY);
119
120 //
121 // if property is not specified then use contents of
122 // META_INF/org.w3c.dom.DOMImplementationSourceList from classpath
123 if (p == null) {
124 p = getServiceValue(classLoader);
125 }
126 if (p == null) {
127 //
128 // DOM Implementations can modify here to add *additional* fallback
129 // mechanisms to access a list of default DOMImplementationSources.
130 p = "gnu.xml.dom.ImplementationSource";
131 }
132 if (p != null) {
133 StringTokenizer st = new StringTokenizer(p);
134 while (st.hasMoreTokens()) {
135 String sourceName = st.nextToken();
136 // Use context class loader, falling back to Class.forName
137 // if and only if this fails...
138 Class sourceClass = null;
139 if (classLoader != null) {
140 sourceClass = classLoader.loadClass(sourceName);
141 } else {
142 sourceClass = Class.forName(sourceName);
143 }
144 DOMImplementationSource source =
145 (DOMImplementationSource) sourceClass.newInstance();
146 sources.addElement(source);
147 }
148 }
149 return new DOMImplementationRegistry(sources);
150 }
151
152 /**
153 * Return the first implementation that has the desired
154 * features, or <code>null</code> if none is found.
155 *
156 * @param features
157 * A string that specifies which features are required. This is
158 * a space separated list in which each feature is specified by
159 * its name optionally followed by a space and a version number.
160 * This is something like: "XML 1.0 Traversal +Events 2.0"
161 * @return An implementation that has the desired features,
162 * or <code>null</code> if none found.
163 */
164 public DOMImplementation getDOMImplementation(final String features) {
165 int size = sources.size();
166 String name = null;
167 for (int i = 0; i < size; i++) {
168 DOMImplementationSource source =
169 (DOMImplementationSource) sources.elementAt(i);
170 DOMImplementation impl = source.getDOMImplementation(features);
171 if (impl != null) {
172 return impl;
173 }
174 }
175 return null;
176 }
177
178 /**
179 * Return a list of implementations that support the
180 * desired features.
181 *
182 * @param features
183 * A string that specifies which features are required. This is
184 * a space separated list in which each feature is specified by
185 * its name optionally followed by a space and a version number.
186 * This is something like: "XML 1.0 Traversal +Events 2.0"
187 * @return A list of DOMImplementations that support the desired features.
188 */
189 public DOMImplementationList getDOMImplementationList(final String features) {
190 final Vector implementations = new Vector();
191 int size = sources.size();
192 for (int i = 0; i < size; i++) {
193 DOMImplementationSource source =
194 (DOMImplementationSource) sources.elementAt(i);
195 DOMImplementationList impls =
196 source.getDOMImplementationList(features);
197 for (int j = 0; j < impls.getLength(); j++) {
198 DOMImplementation impl = impls.item(j);
199 implementations.addElement(impl);
200 }
201 }
202 return new DOMImplementationList() {
203 public DOMImplementation item(final int index) {
204 if (index >= 0 && index < implementations.size()) {
205 try {
206 return (DOMImplementation)
207 implementations.elementAt(index);
208 } catch (ArrayIndexOutOfBoundsException e) {
209 return null;
210 }
211 }
212 return null;
213 }
214
215 public int getLength() {
216 return implementations.size();
217 }
218 };
219 }
220
221 /**
222 * Register an implementation.
223 *
224 * @param s The source to be registered, may not be <code>null</code>
225 */
226 public void addSource(final DOMImplementationSource s) {
227 if (s == null) {
228 throw new NullPointerException();
229 }
230 if (!sources.contains(s)) {
231 sources.addElement(s);
232 }
233 }
234
235 /**
236 *
237 * Gets a class loader.
238 *
239 * @return A class loader, possibly <code>null</code>
240 */
241 private static ClassLoader getClassLoader() {
242 try {
243 ClassLoader contextClassLoader = getContextClassLoader();
244
245 if (contextClassLoader != null) {
246 return contextClassLoader;
247 }
248 } catch (Exception e) {
249 // Assume that the DOM application is in a JRE 1.1, use the
250 // current ClassLoader
251 return DOMImplementationRegistry.class.getClassLoader();
252 }
253 return DOMImplementationRegistry.class.getClassLoader();
254 }
255
256 /**
257 * This method attempts to return the first line of the resource
258 * META_INF/services/org.w3c.dom.DOMImplementationSourceList
259 * from the provided ClassLoader.
260 *
261 * @param classLoader classLoader, may not be <code>null</code>.
262 * @return first line of resource, or <code>null</code>
263 */
264 private static String getServiceValue(final ClassLoader classLoader) {
265 String serviceId = "META-INF/services/" + PROPERTY;
266 // try to find services in CLASSPATH
267 try {
268 InputStream is = getResourceAsStream(classLoader, serviceId);
269
270 if (is != null) {
271 BufferedReader rd;
272 try {
273 rd =
274 new BufferedReader(new InputStreamReader(is, "UTF-8"),
275 DEFAULT_LINE_LENGTH);
276 } catch (java.io.UnsupportedEncodingException e) {
277 rd =
278 new BufferedReader(new InputStreamReader(is),
279 DEFAULT_LINE_LENGTH);
280 }
281 String serviceValue = rd.readLine();
282 rd.close();
283 if (serviceValue != null && serviceValue.length() > 0) {
284 return serviceValue;
285 }
286 }
287 } catch (Exception ex) {
288 return null;
289 }
290 return null;
291 }
292
293 /**
294 * A simple JRE (Java Runtime Environment) 1.1 test
295 *
296 * @return <code>true</code> if JRE 1.1
297 */
298 private static boolean isJRE11() {
299 try {
300 Class c = Class.forName("java.security.AccessController");
301 // java.security.AccessController existed since 1.2 so, if no
302 // exception was thrown, the DOM application is running in a JRE
303 // 1.2 or higher
304 return false;
305 } catch (Exception ex) {
306 // ignore
307 }
308 return true;
309 }
310
311 /**
312 * This method returns the ContextClassLoader or <code>null</code> if
313 * running in a JRE 1.1
314 *
315 * @return The Context Classloader
316 */
317 private static ClassLoader getContextClassLoader() {
318 return isJRE11()
319 ? null
320 : (ClassLoader)
321 AccessController.doPrivileged(new PrivilegedAction() {
322 public Object run() {
323 ClassLoader classLoader = null;
324 try {
325 classLoader =
326 Thread.currentThread().getContextClassLoader();
327 } catch (SecurityException ex) {
328 }
329 return classLoader;
330 }
331 });
332 }
333
334 /**
335 * This method returns the system property indicated by the specified name
336 * after checking access control privileges. For a JRE 1.1, this check is
337 * not done.
338 *
339 * @param name the name of the system property
340 * @return the system property
341 */
342 private static String getSystemProperty(final String name) {
343 return isJRE11()
344 ? (String) System.getProperty(name)
345 : (String) AccessController.doPrivileged(new PrivilegedAction() {
346 public Object run() {
347 return System.getProperty(name);
348 }
349 });
350 }
351
352 /**
353 * This method returns an Inputstream for the reading resource
354 * META_INF/services/org.w3c.dom.DOMImplementationSourceList after checking
355 * access control privileges. For a JRE 1.1, this check is not done.
356 *
357 * @param classLoader classLoader
358 * @param name the resource
359 * @return an Inputstream for the resource specified
360 */
361 private static InputStream getResourceAsStream(final ClassLoader classLoader,
362 final String name) {
363 if (isJRE11()) {
364 InputStream ris;
365 if (classLoader == null) {
366 ris = ClassLoader.getSystemResourceAsStream(name);
367 } else {
368 ris = classLoader.getResourceAsStream(name);
369 }
370 return ris;
371 } else {
372 return (InputStream)
373 AccessController.doPrivileged(new PrivilegedAction() {
374 public Object run() {
375 InputStream ris;
376 if (classLoader == null) {
377 ris =
378 ClassLoader.getSystemResourceAsStream(name);
379 } else {
380 ris = classLoader.getResourceAsStream(name);
381 }
382 return ris;
383 }
384 });
385 }
386 }
387 }