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