All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.owasp.esapi.util.ObjFactory Maven / Gradle / Ivy

/*
 * OWASP Enterprise Security API (ESAPI)
 * 
 * This file is part of the Open Web Application Security Project (OWASP)
 * Enterprise Security API (ESAPI) project. For details, please see
 * http://www.owasp.org/index.php/ESAPI.
 *
 * Copyright (c) 2009 - The OWASP Foundation
 */
package org.owasp.esapi.util;

import org.owasp.esapi.errors.ConfigurationException;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;

/**
 * A generic object factory to create an object of class T. T must be a concrete
 * class that has a no-argument public constructor or a implementor of the Singleton pattern
 * that has a no-arg static getInstance method. If the class being created has a getInstance
 * method, it will be used as a singleton and newInstance() will never be called on the
 * class no matter how many times it comes through this factory.
 *
 * 

* Typical use is something like: *

 * 		import com.example.interfaces.DrinkingEstablishment;
 * 		import com.example.interfaces.Beer;
 * 		...
 * 		// Typically these would be populated from some Java properties file
 * 		String barName = "com.example.foo.Bar";
 * 		String beerBrand = "com.example.brewery.Guinness";
 * 		...
 * 		DrinkingEstablishment bar = ObjFactory.make(barName, "DrinkingEstablishment");
 * 		Beer beer = ObjFactory.make(beerBrand, "Beer");
 *		bar.drink(beer);	// Drink a Guinness beer at the foo Bar. :)
 *		...
 * 
*

* Copyright (c) 2009 - The OWASP Foundation *

* @author [email protected] * @author Chris Schmidt ( chrisisbeef .at. gmail.com ) */ public class ObjFactory { private static final int CACHE_INITIAL_CAPACITY = 32; private static final float CACHE_LOAD_FACTOR = 0.75F; private static final ConcurrentHashMap> CLASSES_CACHE = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY, CACHE_LOAD_FACTOR); private static final ConcurrentHashMap METHODS_CACHE = new ConcurrentHashMap<>(CACHE_INITIAL_CAPACITY, CACHE_LOAD_FACTOR); private static boolean cacheEnabled = true; /** * Create an object based on the className parameter. * * @param className The name of the class to construct. Should be a fully qualified name and * generally the same as type T * @param typeName A type name used in error messages / exceptions. * @return An object of type className, which is cast to type T. * @throws ConfigurationException thrown if class name not found in class path, or does not * have a public, no-argument constructor, or is not a concrete class, or if it is * not a sub-type of T (or T itself). Usually this is * caused by a misconfiguration of the class names specified in the ESAPI.properties * file. Also thrown if the CTOR of the specified className throws * an Exception of some type. */ @SuppressWarnings({ "unchecked" }) // Added because of Eclipse warnings, but ClassCastException IS caught. public static T make(String className, String typeName) throws ConfigurationException { Object obj = null; String errMsg = null; try { if (null == className || "".equals(className) ) { throw new IllegalArgumentException("Classname cannot be null or empty."); } if (null == typeName || "".equals(typeName) ) { // No big deal...just use "[unknown?]" for this as it's only for an err msg. typeName = "[unknown?]"; // CHECKME: Any better suggestions? } Class theClass = loadClassByStringName(className); try { Method singleton = findSingletonCreateMethod(className, theClass); obj = singleton.invoke( null ); } catch (NoSuchMethodException e) { // This is a no-error exception, if this is caught we will continue on assuming the implementation was // not meant to be used as a singleton. obj = theClass.newInstance(); } catch (SecurityException e) { // The class is meant to be singleton, however, the SecurityManager restricts us from calling the // getInstance method on the class, thus this is a configuration issue and a ConfigurationException // is thrown throw new ConfigurationException( "The SecurityManager has restricted the object factory from getting a reference to the singleton implementation " + "of the class [" + className + "]", e ); } return (T)obj; // Eclipse warning here if @SupressWarnings omitted. // Issue 66 - Removed System.out calls as we are throwing an exception in each of these cases // anyhow. } catch( IllegalArgumentException ex ) { errMsg = ex.toString() + " " + typeName + " type name cannot be null or empty."; throw new ConfigurationException(errMsg, ex); }catch ( ClassNotFoundException ex ) { errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be in class path."; throw new ConfigurationException(errMsg, ex); } catch( InstantiationException ex ) { errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be concrete."; throw new ConfigurationException(errMsg, ex); } catch( IllegalAccessException ex ) { errMsg = ex.toString() + " " + typeName + " class (" + className + ") must have a public, no-arg constructor."; throw new ConfigurationException(errMsg, ex); } catch( ClassCastException ex ) { errMsg = ex.toString() + " " + typeName + " class (" + className + ") must be a subtype of T in ObjFactory"; throw new ConfigurationException(errMsg, ex); } catch( Exception ex ) { // Because we are using reflection, we want to catch any checked or unchecked Exceptions and // re-throw them in a way we can handle them. Because using reflection to construct the object, // we can't have the compiler notify us of uncaught exceptions. For example, JavaEncryptor() // CTOR can throw [well, now it can] an EncryptionException if something goes wrong. That case // is taken care of here. // // CHECKME: Should we first catch RuntimeExceptions so we just let unchecked Exceptions go through // unaltered??? // errMsg = ex.toString() + " " + typeName + " class (" + className + ") CTOR threw exception."; throw new ConfigurationException(errMsg, ex); } // DISCUSS: Should we also catch ExceptionInInitializerError here? See Google Issue #61 comments. } /** * Control whether cache for classes and method names should be enabled or disabled. Initial state is enabled. * Ordinally, you are not expected to want to / have to call this method. It's major purpose is a "just-in-case" * something goes wrong is some weird context where multiple ESAPI jars are loaded into a give application and something * goes wrong, etc. A secondary purpose is it allows us to easily disable the cache so we can measure its time savings. * * @param enable true - enable cache; false - disable cache */ public static void setCache(boolean enable) { cacheEnabled = enable; } /** * Load the class in cache, or load by the classloader and cache it * * @param className The name of the class to construct. Should be a fully qualified name * @return The target class * @throws ClassNotFoundException Failed to load class by the className */ private static Class loadClassByStringName(String className) throws ClassNotFoundException { Class clazz; if (cacheEnabled && CLASSES_CACHE.containsKey(className)) { clazz = CLASSES_CACHE.get(className); } else { clazz = Class.forName(className); if ( cacheEnabled ) CLASSES_CACHE.putIfAbsent(className, clazz); } return clazz; } /** * Find the method to create a singleton object * * @param className The name of the class to construct. Should be a fully qualified name * @param theClass The class loaded in prior * @return The method to create a singleton object * @throws NoSuchMethodException Failed to find the target method */ private static Method findSingletonCreateMethod(String className, Class theClass) throws NoSuchMethodException { MethodWrappedInfo singleton = loadMethodByStringName(className,theClass); // If the implementation class contains a getInstance method that is not static, this is an invalid // object configuration and a ConfigurationException will be thrown. if (!singleton.isStaticMethod()) { throw singleton.getNonStaticEx(); } return singleton.getMethod(); } /** * * @param className The name of the class to construct. Should be a fully qualified name * @param theClass The class loaded in prior * @return Wrapped data, contains the method object and the method is static method or not * @throws NoSuchMethodException Failed to find the target method */ private static MethodWrappedInfo loadMethodByStringName(String className, Class theClass) throws NoSuchMethodException { String methodName = className + "getInstance"; MethodWrappedInfo methodInfo; if (cacheEnabled && METHODS_CACHE.containsKey(methodName)) { methodInfo = METHODS_CACHE.get(methodName); } else { Method method = theClass.getMethod("getInstance"); boolean staticMethod = Modifier.isStatic(method.getModifiers()); ConfigurationException nonStaticEx = staticMethod ? null : new ConfigurationException("Class [" + className + "] contains a non-static getInstance method."); methodInfo = new MethodWrappedInfo(method, staticMethod, nonStaticEx); if ( cacheEnabled ) METHODS_CACHE.putIfAbsent(methodName, methodInfo); } return methodInfo; } /** * Not instantiable */ private ObjFactory() { } /** * Wrapped data, contains the method object and the method is static method or not.
* The goal to store the boolean value in field staticMethod is reduce the check times: check once, use many times.
* The goal to store the exception in field nonStaticException is reduce the cost of new Exception(): create once, use many times. */ private static class MethodWrappedInfo { private Method method; private boolean staticMethod; private ConfigurationException nonStaticEx; MethodWrappedInfo(Method method, boolean staticMethod, ConfigurationException nonStaticEx) { this.method = method; this.staticMethod = staticMethod; this.nonStaticEx = nonStaticEx; } Method getMethod() { return method; } boolean isStaticMethod() { return staticMethod; } ConfigurationException getNonStaticEx() { return nonStaticEx; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy