View Javadoc
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