001 /*
002 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.xml.stream;
027
028 import java.io.InputStream;
029 import java.io.IOException;
030 import java.io.File;
031 import java.io.FileInputStream;
032
033 import java.util.Properties;
034 import java.io.BufferedReader;
035 import java.io.InputStreamReader;
036
037 /**
038 * <p>Implements pluggable Datatypes.</p>
039 *
040 * <p>This class is duplicated for each JAXP subpackage so keep it in
041 * sync. It is package private for secure class loading.</p>
042 *
043 * @author Santiago.PericasGeertsen@sun.com
044 */
045 class FactoryFinder {
046
047 /**
048 * Internal debug flag.
049 */
050 private static boolean debug = false;
051
052 /**
053 * Cache for properties in java.home/lib/jaxp.properties
054 */
055 static Properties cacheProps = new Properties();
056
057 /**
058 * Flag indicating if properties from java.home/lib/jaxp.properties
059 * have been cached.
060 */
061 static boolean firstTime = true;
062
063 /**
064 * Security support class use to check access control before
065 * getting certain system resources.
066 */
067 static SecuritySupport ss = new SecuritySupport();
068
069 // Define system property "jaxp.debug" to get output
070 static {
071 // Use try/catch block to support applets, which throws
072 // SecurityException out of this code.
073 try {
074 String val = ss.getSystemProperty("jaxp.debug");
075 // Allow simply setting the prop to turn on debug
076 debug = val != null && !"false".equals(val);
077 } catch (SecurityException se) {
078 debug = false;
079 }
080 }
081
082 private static void dPrint(String msg) {
083 if (debug) {
084 System.err.println("JAXP: " + msg);
085 }
086 }
087
088 /**
089 * Attempt to load a class using the class loader supplied. If that fails
090 * and fall back is enabled, the current (i.e. bootstrap) class loader is
091 * tried.
092 *
093 * If the class loader supplied is <code>null</code>, first try using the
094 * context class loader followed by the current (i.e. bootstrap) class
095 * loader.
096 */
097 static private Class getProviderClass(String className,
098 ClassLoader cl, boolean doFallback)
099 throws ClassNotFoundException {
100 try {
101 if (cl == null) {
102 cl = ss.getContextClassLoader();
103 if (cl == null) {
104 throw new ClassNotFoundException();
105 } else {
106 return cl.loadClass(className);
107 }
108 } else {
109 return cl.loadClass(className);
110 }
111 } catch (ClassNotFoundException e1) {
112 if (doFallback) {
113 // Use current class loader - should always be bootstrap CL
114 return Class.forName(className, true,
115 FactoryFinder.class.getClassLoader());
116 } else {
117 throw e1;
118 }
119 }
120 }
121
122 /**
123 * Create an instance of a class. Delegates to method
124 * <code>getProviderClass()</code> in order to load the class.
125 *
126 * @param className Name of the concrete class corresponding to the
127 * service provider
128 *
129 * @param cl ClassLoader to use to load the class, null means to use
130 * the bootstrap ClassLoader
131 *
132 * @param doFallback True if the current ClassLoader should be tried as
133 * a fallback if the class is not found using cl
134 */
135 static Object newInstance(String className, ClassLoader cl,
136 boolean doFallback) throws ConfigurationError {
137 try {
138 Class providerClass = getProviderClass(className, cl,
139 doFallback);
140 Object instance = providerClass.newInstance();
141 if (debug) { // Extra check to avoid computing cl strings
142 dPrint("created new instance of " + providerClass
143 + " using ClassLoader: " + cl);
144 }
145 return instance;
146 } catch (ClassNotFoundException x) {
147 throw new ConfigurationError("Provider " + className
148 + " not found", x);
149 } catch (Exception x) {
150 throw new ConfigurationError("Provider " + className
151 + " could not be instantiated: " + x, x);
152 }
153 }
154
155 /**
156 * Finds the implementation Class object in the specified order. Main
157 * entry point.
158 * @return Class object of factory, never null
159 *
160 * @param factoryId Name of the factory to find, same as
161 * a property name
162 * @param fallbackClassName Implementation class name, if nothing else
163 * is found. Use null to mean no fallback.
164 *
165 * Package private so this code can be shared.
166 */
167 static Object find(String factoryId, String fallbackClassName)
168 throws ConfigurationError {
169 dPrint("find factoryId =" + factoryId);
170
171 // Use the system property first
172 try {
173 String systemProp = ss.getSystemProperty(factoryId);
174 if (systemProp != null) {
175 dPrint("found system property, value=" + systemProp);
176 return newInstance(systemProp, null, true);
177 }
178 } catch (SecurityException se) {
179 if (debug)
180 se.printStackTrace();
181 }
182
183 // Try read $java.home/lib/stax.properties followed by
184 // $java.home/lib/jaxp.properties if former not present
185 String configFile = null;
186 try {
187 String factoryClassName = null;
188 if (firstTime) {
189 synchronized (cacheProps) {
190 if (firstTime) {
191 configFile = ss.getSystemProperty("java.home")
192 + File.separator + "lib"
193 + File.separator + "stax.properties";
194 File f = new File(configFile);
195 firstTime = false;
196 if (ss.doesFileExist(f)) {
197 dPrint("Read properties file " + f);
198 cacheProps.load(ss.getFileInputStream(f));
199 } else {
200 configFile = ss
201 .getSystemProperty("java.home")
202 + File.separator
203 + "lib"
204 + File.separator
205 + "jaxp.properties";
206 f = new File(configFile);
207 if (ss.doesFileExist(f)) {
208 dPrint("Read properties file " + f);
209 cacheProps.load(ss
210 .getFileInputStream(f));
211 }
212 }
213 }
214 }
215 }
216 factoryClassName = cacheProps.getProperty(factoryId);
217
218 if (factoryClassName != null) {
219 dPrint("found in " + configFile + " value="
220 + factoryClassName);
221 return newInstance(factoryClassName, null, true);
222 }
223 } catch (Exception ex) {
224 if (debug)
225 ex.printStackTrace();
226 }
227
228 // Try Jar Service Provider Mechanism
229 Object provider = findJarServiceProvider(factoryId);
230 if (provider != null) {
231 return provider;
232 }
233 if (fallbackClassName == null) {
234 throw new ConfigurationError("Provider for " + factoryId
235 + " cannot be found", null);
236 }
237
238 dPrint("loaded from fallback value: " + fallbackClassName);
239 return newInstance(fallbackClassName, null, true);
240 }
241
242 /*
243 * Try to find provider using Jar Service Provider Mechanism
244 *
245 * @return instance of provider class if found or null
246 */
247 private static Object findJarServiceProvider(String factoryId)
248 throws ConfigurationError {
249 String serviceId = "META-INF/services/" + factoryId;
250 InputStream is = null;
251
252 // First try the Context ClassLoader
253 ClassLoader cl = ss.getContextClassLoader();
254 if (cl != null) {
255 is = ss.getResourceAsStream(cl, serviceId);
256
257 // If no provider found then try the current ClassLoader
258 if (is == null) {
259 cl = FactoryFinder.class.getClassLoader();
260 is = ss.getResourceAsStream(cl, serviceId);
261 }
262 } else {
263 // No Context ClassLoader, try the current ClassLoader
264 cl = FactoryFinder.class.getClassLoader();
265 is = ss.getResourceAsStream(cl, serviceId);
266 }
267
268 if (is == null) {
269 // No provider found
270 return null;
271 }
272
273 if (debug) { // Extra check to avoid computing cl strings
274 dPrint("found jar resource=" + serviceId
275 + " using ClassLoader: " + cl);
276 }
277
278 BufferedReader rd;
279 try {
280 rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));
281 } catch (java.io.UnsupportedEncodingException e) {
282 rd = new BufferedReader(new InputStreamReader(is));
283 }
284
285 String factoryClassName = null;
286 try {
287 // XXX Does not handle all possible input as specified by the
288 // Jar Service Provider specification
289 factoryClassName = rd.readLine();
290 rd.close();
291 } catch (IOException x) {
292 // No provider found
293 return null;
294 }
295
296 if (factoryClassName != null && !"".equals(factoryClassName)) {
297 dPrint("found in resource, value=" + factoryClassName);
298
299 // Note: here we do not want to fall back to the current
300 // ClassLoader because we want to avoid the case where the
301 // resource file was found using one ClassLoader and the
302 // provider class was instantiated using a different one.
303 return newInstance(factoryClassName, cl, false);
304 }
305
306 // No provider found
307 return null;
308 }
309
310 static class ConfigurationError extends Error {
311 private Exception exception;
312
313 /**
314 * Construct a new instance with the specified detail string and
315 * exception.
316 */
317 ConfigurationError(String msg, Exception x) {
318 super (msg);
319 this .exception = x;
320 }
321
322 Exception getException() {
323 return exception;
324 }
325 }
326
327 }
|