OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / javax / xml / datatype / FactoryFinder.java
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 // $Id: FactoryFinder.java 670432 2008-06-23 02:02:08Z mrglavas $
19
20 package javax.xml.datatype;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.InputStreamReader;
27 import java.net.URL;
28 import java.util.Properties;
29
30 /**
31  * <p>Implement pluggabile Datatypes.</p>
32  *
33  * <p>This class is duplicated for each JAXP subpackage so keep it in
34  * sync.  It is package private for secure class loading.</p>
35  *
36  * @author <a href="mailto:Jeff.Suttor@Sun.com">Jeff Suttor</a>
37  * @version $Revision: 670432 $, $Date: 2008-06-22 19:02:08 -0700 (Sun, 22 Jun 2008) $
38  * @since 1.5
39  */
40 final class FactoryFinder {
41
42     /**
43      * <p>Name of class to display in output messages.</p>
44      */
45     private static final String CLASS_NAME = "javax.xml.datatype.FactoryFinder";
46
47     /**
48      * <p>Debug flag to trace loading process.</p>
49      */
50     private static boolean debug = false;
51
52     /**
53      * <p>Cache properties for performance.</p>
54      */
55     private static Properties cacheProps = new Properties();
56
57     /**
58      * <p>First time requires initialization overhead.</p>
59      */
60     private static boolean firstTime = true;
61
62     /**
63      * Default columns per line.
64      */
65     private static final int DEFAULT_LINE_LENGTH = 80;
66
67     /**
68      * <p>Check to see if debugging enabled by property.</p>
69      *
70      * <p>Use try/catch block to support applets, which throws
71      * SecurityException out of this code.</p>
72      *
73      */
74     static {
75         try {
76             String val = SecuritySupport.getSystemProperty("jaxp.debug");
77             // Allow simply setting the prop to turn on debug
78             debug = val != null && (! "false".equals(val));
79         } catch (Exception x) {
80             debug = false;
81         }
82     }
83
84     private FactoryFinder() {}
85
86     /**
87      * <p>Output debugging messages.</p>
88      *
89      * @param msg <code>String</code> to print to <code>stderr</code>.
90      */
91     private static void debugPrintln(String msg) {
92         if (debug) {
93             System.err.println(
94                 CLASS_NAME
95                 + ":"
96                 + msg);
97         }
98     }
99
100     /**
101      * <p>Find the appropriate <code>ClassLoader</code> to use.</p>
102      *
103      * <p>The context ClassLoader is prefered.</p>
104      *
105      * @return <code>ClassLoader</code> to use.
106      *
107      * @throws ConfigurationError If a valid <code>ClassLoader</code> cannot be identified.
108      */
109     private static ClassLoader findClassLoader()
110         throws ConfigurationError {
111         ClassLoader classLoader;
112
113         // Figure out which ClassLoader to use for loading the provider
114         // class.  If there is a Context ClassLoader then use it.
115
116         classLoader = SecuritySupport.getContextClassLoader();
117
118         if (debug) debugPrintln(
119             "Using context class loader: "
120             + classLoader);
121
122         if (classLoader == null) {
123             // if we have no Context ClassLoader
124             // so use the current ClassLoader
125             classLoader = FactoryFinder.class.getClassLoader();
126             if (debug) debugPrintln(
127                 "Using the class loader of FactoryFinder: "
128                 + classLoader);
129         }
130
131         return classLoader;
132     }
133
134     /**
135      * <p>Create an instance of a class using the specified ClassLoader.</p>
136      *
137      * @param className Name of class to create.
138      * @param classLoader ClassLoader to use to create named class.
139      *
140      * @return New instance of specified class created using the specified ClassLoader.
141      *
142      * @throws ConfigurationError If class could not be created.
143      */
144     static Object newInstance(
145         String className,
146         ClassLoader classLoader)
147         throws ConfigurationError {
148
149         try {
150             Class spiClass;
151             if (classLoader == null) {
152                 spiClass = Class.forName(className);
153             } else {
154                 spiClass = classLoader.loadClass(className);
155             }
156
157             if (debug) {
158                 debugPrintln("Loaded " + className + " from " + which(spiClass));
159             }
160
161             return spiClass.newInstance();
162         } catch (ClassNotFoundException x) {
163             throw new ConfigurationError(
164                 "Provider " + className + " not found", x);
165         } catch (Exception x) {
166             throw new ConfigurationError(
167                 "Provider " + className + " could not be instantiated: " + x,
168                 x);
169         }
170     }
171
172     /**
173      * Finds the implementation Class object in the specified order.  Main
174      * entry point.
175      * Package private so this code can be shared.
176      *
177      * @param factoryId Name of the factory to find, same as a property name
178      * @param fallbackClassName Implementation class name, if nothing else is found.  Use null to mean no fallback.
179      *
180      * @return Class Object of factory, never null
181      *
182      * @throws ConfigurationError If Class cannot be found.
183      */
184     static Object find(String factoryId, String fallbackClassName)
185         throws ConfigurationError {
186
187         ClassLoader classLoader = findClassLoader();
188
189         // Use the system property first
190         try {
191             String systemProp = SecuritySupport.getSystemProperty(factoryId);
192             if (systemProp != null && systemProp.length() > 0) {
193                 if (debug) debugPrintln("found " + systemProp + " in the system property " + factoryId);
194                 return newInstance(systemProp, classLoader);
195             }
196         } catch (SecurityException se) {
197             ; // NOP, explicitly ignore SecurityException
198         }
199
200         // try to read from $java.home/lib/jaxp.properties
201         try {
202             String javah = SecuritySupport.getSystemProperty("java.home");
203             String configFile = javah + File.separator + "lib" + File.separator + "jaxp.properties";
204             String factoryClassName = null;
205             if (firstTime) {
206                 synchronized (cacheProps) {
207                     if (firstTime) {
208                         File f = new File(configFile);
209                         firstTime = false;
210                         if (SecuritySupport.doesFileExist(f)) {
211                             if (debug) debugPrintln("Read properties file " + f);
212                             cacheProps.load(SecuritySupport.getFileInputStream(f));
213                         }
214                     }
215                 }
216             }
217             factoryClassName = cacheProps.getProperty(factoryId);
218             if (debug) debugPrintln("found " + factoryClassName + " in $java.home/jaxp.properties");
219
220             if (factoryClassName != null) {
221                 return newInstance(factoryClassName, classLoader);
222             }
223         } catch (Exception ex) {
224             if (debug) {
225                 ex.printStackTrace();
226             }
227         }
228
229         // Try Jar Service Provider Mechanism
230         Object provider = findJarServiceProvider(factoryId);
231         if (provider != null) {
232             return provider;
233         }
234
235         if (fallbackClassName == null) {
236             throw new ConfigurationError(
237                 "Provider for " + factoryId + " cannot be found", null);
238         }
239
240         if (debug) debugPrintln("loaded from fallback value: " + fallbackClassName);
241         return newInstance(fallbackClassName, classLoader);
242     }
243
244     /*
245      * Try to find provider using Jar Service Provider Mechanism
246      *
247      * @return instance of provider class if found or null
248      */
249     private static Object findJarServiceProvider(String factoryId)
250         throws ConfigurationError
251     {
252
253         String serviceId = "META-INF/services/" + factoryId;
254         InputStream is = null;
255
256         // First try the Context ClassLoader
257         ClassLoader cl = SecuritySupport.getContextClassLoader();
258         if (cl != null) {
259             is = SecuritySupport.getResourceAsStream(cl, serviceId);
260
261             // If no provider found then try the current ClassLoader
262             if (is == null) {
263                 cl = FactoryFinder.class.getClassLoader();
264                 is = SecuritySupport.getResourceAsStream(cl, serviceId);
265             }
266         } else {
267             // No Context ClassLoader, try the current
268             // ClassLoader
269             cl = FactoryFinder.class.getClassLoader();
270             is = SecuritySupport.getResourceAsStream(cl, serviceId);
271         }
272
273         if (is == null) {
274             // No provider found
275             return null;
276         }
277
278         if (debug) debugPrintln("found jar resource=" + serviceId +
279                " using ClassLoader: " + cl);
280
281         BufferedReader rd;
282         try {
283             rd = new BufferedReader(new InputStreamReader(is, "UTF-8"), DEFAULT_LINE_LENGTH);
284         } catch (java.io.UnsupportedEncodingException e) {
285             rd = new BufferedReader(new InputStreamReader(is), DEFAULT_LINE_LENGTH);
286         }
287
288         String factoryClassName = null;
289         try {
290             // XXX Does not handle all possible input as specified by the
291             // Jar Service Provider specification
292             factoryClassName = rd.readLine();
293         }
294         catch (IOException x) {
295             // No provider found
296             return null;
297         }
298         finally {
299             try {
300                 // try to close the reader.
301                 rd.close();
302             }
303             // Ignore the exception.
304             catch (IOException exc) {}
305         }
306
307         if (factoryClassName != null &&
308             ! "".equals(factoryClassName)) {
309             if (debug) debugPrintln("found in resource, value="
310                    + factoryClassName);
311
312             return newInstance(factoryClassName, cl);
313         }
314
315         // No provider found
316         return null;
317     }
318
319     /**
320      * <p>Configuration Error.</p>
321      */
322     static class ConfigurationError extends Error {
323
324         private static final long serialVersionUID = -3644413026244211347L;
325
326         /**
327          * <p>Exception that caused the error.</p>
328          */
329         private Exception exception;
330
331         /**
332          * <p>Construct a new instance with the specified detail string and
333          * exception.</p>
334          *
335          * @param msg Detail message for this error.
336          * @param x Exception that caused the error.
337          */
338         ConfigurationError(String msg, Exception x) {
339             super(msg);
340             this.exception = x;
341         }
342
343         /**
344          * <p>Get the Exception that caused the error.</p>
345          *
346          * @return Exception that caused the error.
347          */
348         Exception getException() {
349             return exception;
350         }
351     }
352
353
354
355     /**
356      * Returns the location where the given Class is loaded from.
357      *
358      * @param clazz Class to find load location.
359      *
360      * @return Location where class would be loaded from.
361      */
362     private static String which(Class clazz) {
363         try {
364             String classnameAsResource = clazz.getName().replace('.', '/') + ".class";
365
366             ClassLoader loader = clazz.getClassLoader();
367
368             URL it;
369
370             if (loader != null) {
371                 it = loader.getResource(classnameAsResource);
372             } else {
373                 it = ClassLoader.getSystemResource(classnameAsResource);
374             }
375
376             if (it != null) {
377                 return it.toString();
378             }
379         }
380         // The VM ran out of memory or there was some other serious problem. Re-throw.
381         catch (VirtualMachineError vme) {
382             throw vme;
383         }
384         // ThreadDeath should always be re-thrown
385         catch (ThreadDeath td) {
386             throw td;
387         }
388         catch (Throwable t) {
389             // work defensively.
390             if (debug) {
391                 t.printStackTrace();
392             }
393         }
394         return "unknown location";
395     }
396 }