1 package attrib4j;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.InputStreamReader;
7 import java.lang.reflect.InvocationTargetException;
8 import java.lang.reflect.Method;
9 import java.security.AccessController;
10 import java.security.PrivilegedAction;
11 import java.util.Hashtable;
12 import java.util.Properties;
13
14 /***
15 * <p>Factory for creating {@link AttributeCompiler} instances, with discovery and
16 * configuration features similar to that employed by standard Java APIs
17 * such as JAXP. Current implementations are based on Javadoc and ???
18 * libraries.</p>
19 *
20 * <p>Implementation taken from the jakarata commons logging package.</p>
21 *
22 * TODO Put common impl in a base class.
23 *
24 * @author <a href="mailto:mpollack@speakeasy.net">Mark Pollack</a>
25 * @version $Revision: 1.1 $ $Date: 2003/04/10 03:36:16 $
26 */
27
28 public abstract class AttributeCompilerFactory {
29
30 // ----------------------------------------------------- Manifest Constants
31
32 /***
33 * The name of the property used to identify the AttributeCompilerFactory
34 * implementation class name.
35 */
36 public static final String FACTORY_PROPERTY =
37 "attrib4j.AttributeCompilerFactory";
38
39 /***
40 * The fully qualified class name of the fallback
41 * <code>AttributeCompilerFactory</code>
42 * implementation class to use, if no other can be found.
43 */
44 public static final String FACTORY_DEFAULT =
45 "attrib4j.compiler.JavadocFactory";
46
47 /***
48 * The name of the properties file to search for.
49 */
50 public static final String FACTORY_PROPERTIES = "attrib4j.properties";
51
52 /***
53 * JDK1.3+ 'Service Provider' specification
54 * ( http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html )
55 */
56 protected static final String SERVICE_ID =
57 "META-INF/services/attrib4j.AttributeCompilerFactory";
58
59 // ----------------------------------------------------------- Constructors
60
61 /***
62 * Protected constructor that is not available for public use.
63 */
64 protected AttributeCompilerFactory() {
65 }
66
67 // --------------------------------------------------------- Public Methods
68
69 // ------------------------------------------------------- Static Variables
70
71 /***
72 * The previously constructed <code>AttributeCompilerFactory</code> instances,
73 * keyed by the <code>ClassLoader</code> with which it was created.
74 */
75 protected static Hashtable factories = new Hashtable();
76
77 // --------------------------------------------------------- Static Methods
78
79 /***
80 * <p>Construct (if necessary) and return a <code>AttributeCompilerFactory</code>
81 * instance, using the following ordered lookup procedure to determine
82 * the name of the implementation class to be loaded.</p>
83 * <ul>
84 * <li>The <code>attrib4j.AttributeCompilerFactory</code> system property.</li>
85 * <li>The JDK 1.3 Service Discovery mechanism</li>
86 * <li>Use the properties file <code>attrib4j.properties</code>
87 * file, if found in the class path of this class. The configuration
88 * file is in standard <code>java.util.Properties</code> format and
89 * contains the fully qualified name of the implementation class
90 * with the key being the system property defined above.</li>
91 * <li>Fall back to a default implementation class
92 * (<code>attrib4j.compiler.JavadocFactory</code>).</li>
93 * </ul>
94 *
95 *
96 * @exception AttributeException if the implementation class is not
97 * available or cannot be instantiated.
98 */
99 public static AttributeCompilerFactory getFactory()
100 throws AttributeException {
101
102 // Identify the class loader we will be using
103 ClassLoader contextClassLoader =
104 (
105 ClassLoader) AccessController
106 .doPrivileged(new PrivilegedAction() {
107 public Object run() {
108 ClassLoader cl = null;
109 try {
110 cl = getContextClassLoader();
111 } catch (AttributeException e) {
112 e.printStackTrace();
113 } finally {
114 return cl;
115 }
116 }
117 });
118
119 // Return any previously registered factory for this class loader
120 AttributeCompilerFactory factory = getCachedFactory(contextClassLoader);
121 if (factory != null) {
122 return factory;
123 }
124
125 // Load properties file..
126 // will be used one way or another in the end. (well maybe not now)
127
128 Properties props = null;
129 try {
130 InputStream stream =
131 (contextClassLoader == null
132 ? ClassLoader.getSystemResourceAsStream(FACTORY_PROPERTIES)
133 : contextClassLoader.getResourceAsStream(FACTORY_PROPERTIES));
134 if (stream != null) {
135 props = new Properties();
136 props.load(stream);
137 stream.close();
138 }
139 } catch (IOException e) {
140 } catch (SecurityException e) {
141 }
142
143 // First, try the system property
144 try {
145 String factoryClass = System.getProperty(FACTORY_PROPERTY);
146 if (factoryClass != null) {
147 factory = newFactory(factoryClass, contextClassLoader);
148 }
149 } catch (SecurityException e) {
150 ; // ignore
151 }
152
153 // Second, try to find a service by using the JDK1.3 jar
154 // discovery mechanism. This will allow users to plug a logger
155 // by just placing it in the lib/ directory of the webapp ( or in
156 // CLASSPATH or equivalent ). This is similar with the second
157 // step, except that it uses the (standard?) jdk1.3 location in the jar.
158
159 if (factory == null) {
160 try {
161 InputStream is =
162 (contextClassLoader == null
163 ? ClassLoader.getSystemResourceAsStream(SERVICE_ID)
164 : contextClassLoader.getResourceAsStream(SERVICE_ID));
165
166 if (is != null) {
167 // This code is needed by EBCDIC and other strange systems.
168 // It's a fix for bugs reported in xerces
169 BufferedReader rd;
170 try {
171 rd =
172 new BufferedReader(
173 new InputStreamReader(is, "UTF-8"));
174 } catch (java.io.UnsupportedEncodingException e) {
175 rd = new BufferedReader(new InputStreamReader(is));
176 }
177
178 String factoryClassName = rd.readLine();
179 rd.close();
180
181 if (factoryClassName != null
182 && !"".equals(factoryClassName)) {
183
184 factory =
185 newFactory(factoryClassName, contextClassLoader);
186 }
187 }
188 } catch (Exception ex) {
189 ;
190 }
191 }
192
193 // Third try a properties file.
194 // If the properties file exists, it'll be read and the properties
195 // used. IMHO ( costin ) System property and JDK1.3 jar service
196 // should be enough for detecting the class name. The properties
197 // should be used to set the attributes ( which may be specific to
198 // the webapp, even if a default logger is set at JVM level by a
199 // system property )
200
201 if (factory == null && props != null) {
202 String factoryClass = props.getProperty(FACTORY_PROPERTY);
203 if (factoryClass != null) {
204 factory = newFactory(factoryClass, contextClassLoader);
205 }
206 }
207
208 // Fourth, try the fallback implementation class
209
210 if (factory == null) {
211 Log.debug(
212 "AttributeCompilerFactory",
213 "using default factory: " + FACTORY_DEFAULT);
214 factory =
215 newFactory(
216 FACTORY_DEFAULT,
217 AttributeCompilerFactory.class.getClassLoader());
218 }
219
220 if (factory != null) {
221 /***
222 * Always cache using context class loader..
223 */
224 cacheFactory(contextClassLoader, factory);
225
226 /* TODO MLP not saving properties from attrib4j.properties
227 with the AttributeCompilerFactory - yet.
228 if( props!=null ) {
229 Enumeration names = props.propertyNames();
230 while (names.hasMoreElements()) {
231 String name = (String) names.nextElement();
232 String value = props.getProperty(name);
233 factory.setAttribute(name, value);
234 }
235 }
236 */
237 }
238
239 return factory;
240 }
241
242 /***
243 * Method to return a {@link AttributeCompiler}.
244 *
245 * @exception AttributeException if a suitable <code>AttributeCompiler</code>
246 * instance cannot be returned
247 */
248 public abstract AttributeCompiler getAttributeCompiler()
249 throws AttributeException;
250
251 // ------------------------------------------------------ Protected Methods
252
253 /***
254 * Return the thread context class loader if available.
255 * Otherwise return null.
256 *
257 * The thread context class loader is available for JDK 1.2
258 * or later, if certain security conditions are met.
259 *
260 * @exception AttributeException if a suitable class loader
261 * cannot be identified.
262 */
263 protected static ClassLoader getContextClassLoader()
264 throws AttributeException {
265 ClassLoader classLoader = null;
266
267 try {
268 // Are we running on a JDK 1.2 or later system?
269 Method method =
270 Thread.class.getMethod("getContextClassLoader", null);
271
272 // Get the thread context class loader (if there is one)
273 try {
274 classLoader =
275 (ClassLoader) method.invoke(Thread.currentThread(), null);
276 } catch (IllegalAccessException e) {
277 throw new AttributeException(
278 "Unexpected IllegalAccessException",
279 e);
280 } catch (InvocationTargetException e) {
281 /***
282 * InvocationTargetException is thrown by 'invoke' when
283 * the method being invoked (getContextClassLoader) throws
284 * an exception.
285 *
286 * getContextClassLoader() throws SecurityException when
287 * the context class loader isn't an ancestor of the
288 * calling class's class loader, or if security
289 * permissions are restricted.
290 *
291 * In the first case (not related), we want to ignore and
292 * keep going. We cannot help but also ignore the second
293 * with the logic below, but other calls elsewhere (to
294 * obtain a class loader) will trigger this exception where
295 * we can make a distinction.
296 */
297 if (e.getTargetException() instanceof SecurityException) {
298 ; // ignore
299 } else {
300 // Capture 'e.getTargetException()' exception for details
301 // alternate: log 'e.getTargetException()', and pass back 'e'.
302 throw new AttributeException(
303 "Unexpected InvocationTargetException",
304 e.getTargetException());
305 }
306 }
307 } catch (NoSuchMethodException e) {
308 // Assume we are running on JDK 1.1
309 classLoader = AttributeCompilerFactory.class.getClassLoader();
310 }
311
312 // Return the selected class loader
313 return classLoader;
314 }
315
316 /***
317 * Check cached factories (keyed by classLoader)
318 */
319 private static AttributeCompilerFactory getCachedFactory(ClassLoader contextClassLoader) {
320 AttributeCompilerFactory factory = null;
321
322 if (contextClassLoader != null) {
323 factory =
324 (AttributeCompilerFactory) factories.get(contextClassLoader);
325 }
326
327 return factory;
328 }
329
330 private static void cacheFactory(
331 ClassLoader classLoader,
332 AttributeCompilerFactory factory) {
333 if (classLoader != null && factory != null) {
334 factories.put(classLoader, factory);
335 }
336 }
337
338 /***
339 * Return a new instance of the specified <code>AttributeCompilerFactory</code>
340 * implementation class, loaded by the specified class loader.
341 * If that fails, try the class loader used to load this
342 * (abstract) AttributeCompilerFactory.
343 *
344 * @param factoryClass Fully qualified name of the
345 * <code>AttributeCompilerFactory</code> implementation class
346 * @param classLoader ClassLoader from which to load this class
347 *
348 * @exception AttributeException if a suitable instance
349 * cannot be created
350 */
351 protected static AttributeCompilerFactory newFactory(
352 String factoryClass,
353 ClassLoader classLoader)
354 throws AttributeException {
355
356 try {
357 if (classLoader != null) {
358 try {
359 // first the given class loader param (thread class loader)
360 return (AttributeCompilerFactory) classLoader
361 .loadClass(factoryClass)
362 .newInstance();
363 } catch (ClassNotFoundException ex) {
364 if (classLoader
365 == AttributeCompilerFactory.class.getClassLoader()) {
366 // Nothing more to try, onwards.
367 throw ex;
368 }
369 // ignore exception, continue
370 } catch (NoClassDefFoundError e) {
371 if (classLoader
372 == AttributeCompilerFactory.class.getClassLoader()) {
373 // Nothing more to try, onwards.
374 throw e;
375 }
376 // ignore exception, continue
377 }
378 }
379
380 /* At this point, either classLoader == null, OR
381 * classLoader was unable to load factoryClass..
382 * try the class loader that loaded this class:
383 * AttributeCompilerFactory.getClassLoader().
384 *
385 * Notes:
386 * a) AttributeCompilerFactory.class.getClassLoader() may return 'null'
387 * if AttributeCompilerFactory is loaded by the bootstrap classloader.
388 * b) The Java endorsed library mechanism is instead
389 * Class.forName(factoryClass);
390 */
391 return (AttributeCompilerFactory) Class
392 .forName(factoryClass)
393 .newInstance();
394 } catch (Exception e) {
395 throw new AttributeException(e);
396 }
397 }
398 }
This page was automatically generated by Maven