blob: ede69d4c2c20e9af7eb3000c47372c94a2b4c3fe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
* 08/23/2010-2.2 Michael O'Brien
* - 323043: application.xml module ordering may cause weaving not to occur causing an NPE.
* warn if expected "_persistence_*_vh" method not found
* instead of throwing NPE during deploy validation.
* 30/05/2012-2.4 Guy Pelletier
* - 354678: Temp classloader is still being used during metadata processing
*
******************************************************************************/
package org.eclipse.persistence.internal.security;
import java.security.*;
import java.util.HashMap;
import java.util.Map;
import java.lang.reflect.*;
import org.eclipse.persistence.internal.helper.Helper;
/**
* INTERNAL:
* Privileged Access Helper provides a utility so all calls that require privileged access can use the same code.
*
* Do privileged blocks can be used with a security manager to grant a code base (eclipselink.jar) access to certain
* Java operations such as reflection. Generally a security manager is not enabled in a JVM, so this is not an issue.
* If a security manager is enabled, then either the application can be configured to have access to operations such as
* reflection, or only EclipseLink can be given access. If only EclipseLink is desired to be given access then
* do privileged must be enabled through the System property "eclipselink.security.usedoprivileged"=true.
*
* Note the usage of do privileged has major impacts on performance, so should normally be avoided.
*/
public class PrivilegedAccessHelper {
private static boolean shouldCheckPrivilegedAccess = true;
private static boolean shouldUsePrivilegedAccess = false;
private static Map<String, Class> primitiveClasses;
static {
primitiveClasses = new HashMap<String, Class>();
primitiveClasses.put("boolean", boolean.class);
primitiveClasses.put("int", int.class);
primitiveClasses.put("long", long.class);
primitiveClasses.put("float", float.class);
primitiveClasses.put("double", double.class);
primitiveClasses.put("char", char.class);
primitiveClasses.put("byte", byte.class);
primitiveClasses.put("void", void.class);
primitiveClasses.put("short", short.class);
}
/**
* Finding a field within a class potentially has to navigate through it's superclasses to eventually
* find the field. This method is called by the public getDeclaredField() method and does a recursive
* search for the named field in the given classes or it's superclasses.
*/
private static Field findDeclaredField(Class javaClass, String fieldName) throws NoSuchFieldException {
try {
return javaClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException ex) {
Class superclass = javaClass.getSuperclass();
if (superclass == null) {
throw ex;
} else {
return findDeclaredField(superclass, fieldName);
}
}
}
/**
* Finding a method within a class potentially has to navigate through it's superclasses to eventually
* find the method. This method is called by the public getDeclaredMethod() method and does a recursive
* search for the named method in the given classes or it's superclasses.
*/
private static Method findMethod(Class javaClass, String methodName, Class[] methodParameterTypes) throws NoSuchMethodException {
try {
// use a combination of getDeclaredMethod() and recursion to ensure we get the non-public methods
// getMethod will not help because it returns only public methods
return javaClass.getDeclaredMethod(methodName, methodParameterTypes);
} catch (NoSuchMethodException ex) {
Class superclass = javaClass.getSuperclass();
if (superclass == null) {
throw ex;
} else {
try{
return findMethod(superclass, methodName, methodParameterTypes);
}catch (NoSuchMethodException lastEx){
throw ex;
}
}
}
}
/**
* Execute a java Class.forName(). Wrap the call in a doPrivileged block if necessary.
* @param className
*/
public static Class getClassForName(final String className) throws ClassNotFoundException {
// Check for primitive types.
Class primitive = primitiveClasses.get(className);
if (primitive != null) {
return primitive;
}
return Class.forName(className);
}
/**
* Execute a java Class.forName() wrap the call in a doPrivileged block if necessary.
*/
public static Class getClassForName(final String className, final boolean initialize, final ClassLoader loader) throws ClassNotFoundException {
// Check for primitive types.
Class primitive = primitiveClasses.get(className);
if (primitive != null) {
return primitive;
}
return Class.forName(className, initialize, loader);
}
/**
* Gets the class loader for a given class. Wraps the call in a privileged block if necessary
*/
public static ClassLoader getClassLoaderForClass(final Class clazz) {
return clazz.getClassLoader();
}
/**
* Gets the class loader for a given class. Wraps the call in a privileged block if necessary
*/
public static ClassLoader privilegedGetClassLoaderForClass(final Class clazz) {
try{
return (ClassLoader) AccessController.doPrivileged(new PrivilegedGetClassLoaderForClass(clazz));
}catch (PrivilegedActionException ex){
throw (RuntimeException) ex.getCause();
}
}
/**
* Get the public constructor for the given class and given arguments and wrap it in doPrivileged if
* necessary. The shouldSetAccessible parameter allows the the setAccessible API to be called as well.
* This option was added to avoid making multiple doPrivileged calls within InstantiationPolicy.
* @param javaClass The class to get the Constructor for
* @param args An array of classes representing the argument types of the constructor
* @param shouldSetAccessible whether or not to call the setAccessible API
* @throws java.lang.NoSuchMethodException
*/
public static Constructor getConstructorFor(final Class javaClass, final Class[] args, final boolean shouldSetAccessible) throws NoSuchMethodException {
Constructor result = null;
try {
result = javaClass.getConstructor(args);
} catch (NoSuchMethodException missing) {
// Search for any constructor with the same number of arguments and assignable types.
for (Constructor constructor : javaClass.getConstructors()) {
if (constructor.getParameterTypes().length == args.length) {
boolean found = true;
for (int index = 0; index < args.length; index++) {
Class parameterType = Helper.getObjectClass(constructor.getParameterTypes()[index]);
Class argType = Helper.getObjectClass(args[index]);
if ((!parameterType.isAssignableFrom(argType))
&& (!argType.isAssignableFrom(parameterType))) {
found = false;
break;
}
}
if (found) {
result = constructor;
break;
}
}
}
if (result == null) {
throw missing;
}
}
if (shouldSetAccessible) {
result.setAccessible(true);
}
return result;
}
/**
* Get the context ClassLoader for a thread. Wrap the call in a doPrivileged block if necessary.
*/
public static ClassLoader getContextClassLoader(final Thread thread) {
return thread.getContextClassLoader();
}
/**
* Get the constructor for the given class and given arguments (regardless of whether it is public
* or private))and wrap it in doPrivileged if necessary. The shouldSetAccessible parameter allows
* the the setAccessible API to be called as well. This option was added to avoid making multiple
* doPrivileged calls within InstantiationPolicy.
* @param javaClass The class to get the Constructor for
* @param args An array of classes representing the argument types of the constructor
* @param shouldSetAccessible whether or not to call the setAccessible API
* @throws java.lang.NoSuchMethodException
*/
public static Constructor getDeclaredConstructorFor(final Class javaClass, final Class[] args, final boolean shouldSetAccessible) throws NoSuchMethodException {
Constructor result = javaClass.getDeclaredConstructor(args);
if (shouldSetAccessible) {
result.setAccessible(true);
}
return result;
}
/**
* Get a field in a class or its superclasses and wrap the call in doPrivileged if necessary.
* The shouldSetAccessible parameter allows the the setAccessible API to be called as well.
* This option was added to avoid making multiple doPrivileged calls within InstanceVariableAttributeAccessor.
* @param javaClass The class to get the field from
* @param fieldName The name of the field
* @param shouldSetAccessible whether or not to call the setAccessible API
* @throws java.lang.NoSuchFieldException
*/
public static Field getField(final Class javaClass, final String fieldName, final boolean shouldSetAccessible) throws NoSuchFieldException {
Field field = findDeclaredField(javaClass, fieldName);
if (shouldSetAccessible) {
field.setAccessible(true);
}
return field;
}
/**
* Get a field actually declared in a class and wrap the call in doPrivileged if necessary.
* The shouldSetAccessible parameter allows the the setAccessible API to be called as well.
* This option was added to avoid making multiple doPrivileged calls within InstanceVariableAttributeAccessor.
* @param javaClass The class to get the field from
* @param fieldName The name of the field
* @param shouldSetAccessible whether or not to call the setAccessible API
* @throws java.lang.NoSuchFieldException
*/
public static Field getDeclaredField(final Class javaClass, final String fieldName, final boolean shouldSetAccessible) throws NoSuchFieldException {
Field field = javaClass.getDeclaredField(fieldName);
if (shouldSetAccessible) {
field.setAccessible(true);
}
return field;
}
/**
* Get the list of fields in a class. Wrap the call in doPrivileged if necessary
* Excludes inherited fields.
* @param clazz the class to get the fields from.
*/
public static Field[] getDeclaredFields(final Class clazz) {
return clazz.getDeclaredFields();
}
/**
* Get the list of public fields in a class. Wrap the call in doPrivileged if necessary
* @param clazz the class to get the fields from.
*/
public static Field[] getFields(final Class clazz) {
return clazz.getFields();
}
/**
* Return a method on a given class with the given method name and parameter
* types. This call will NOT traverse the superclasses. Wrap the call in
* doPrivileged if necessary.
* @param method the class to get the method from
* @param methodName the name of the method to get
* @param methodParameters a list of classes representing the classes of the
* parameters of the method.
*/
public static Method getDeclaredMethod(final Class clazz, final String methodName, final Class[] methodParameterTypes) throws NoSuchMethodException {
return clazz.getDeclaredMethod(methodName, methodParameterTypes);
}
/**
* Get a method declared in the given class. Wrap the call in doPrivileged
* if necessary. This call will traverse the superclasses. The
* shouldSetAccessible parameter allows the the setAccessible API to be
* called as well. This option was added to avoid making multiple
* doPrivileged calls within MethodBasedAttributeAccessor.
* @param javaClass The class to get the method from
* @param methodName The name of the method to get
* @param methodParameterTypes A list of classes representing the classes of the parameters of the mthod
* @param shouldSetAccessible whether or not to call the setAccessible API
* @throws java.lang.NoSuchMethodException
*/
public static Method getMethod(final Class javaClass, final String methodName, final Class[] methodParameterTypes, final boolean shouldSetAccessible) throws NoSuchMethodException {
Method method = findMethod(javaClass, methodName, methodParameterTypes);
if (shouldSetAccessible) {
method.setAccessible(true);
}
return method;
}
/**
* Get a public method declared in the given class. Wrap the call in doPrivileged if necessary.
* This call will traverse the superclasses. The shouldSetAccessible parameter allows the the
* setAccessible API to be called as well. This option was added to avoid making multiple
* doPrivileged calls within MethodBasedAttributeAccessor.
*
* @param javaClass The class to get the method from
* @param methodName The name of the method to get
* @param methodParameterTypes A list of classes representing the classes of the parameters of the method
* @param shouldSetAccessible whether or not to call the setAccessible API
* @throws java.lang.NoSuchMethodException
*/
public static Method getPublicMethod(final Class javaClass, final String methodName, final Class[] methodParameterTypes, final boolean shouldSetAccessible) throws NoSuchMethodException {
// Return the (public) method - will traverse superclass(es) if necessary
Method method = javaClass.getMethod(methodName, methodParameterTypes);
if (shouldSetAccessible) {
method.setAccessible(true);
}
return method;
}
/**
* Get the list of methods in a class. Wrap the call in doPrivileged if
* necessary. Excludes inherited methods.
* @param clazz the class to get the methods from.
*/
public static Method[] getDeclaredMethods(final Class clazz) {
return clazz.getDeclaredMethods();
}
/**
* Get the return type for a given method. Wrap the call in doPrivileged if necessary.
* @param field
*/
public static Class getFieldType(final Field field) {
return field.getType();
}
/**
* Get the line separator character.
* Previous versions of TopLink always did this in a privileged block so we will continue to do so.
*/
public static String getLineSeparator() {
if (shouldUsePrivilegedAccess()) {
return (String)AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return System.getProperty("file.separator");
}
});
} else {
return org.eclipse.persistence.internal.helper.Helper.cr();
}
}
/**
* Get the list of parameter types for a given method. Wrap the call in doPrivileged if necessary.
* @param method The method to get the parameter types of
*/
public static Class[] getMethodParameterTypes(final Method method) {
return method.getParameterTypes();
}
/**
* Get the return type for a given method. Wrap the call in doPrivileged if necessary.
* @param method
*/
public static Class getMethodReturnType(final Method method) {
// 323148: a null method as a possible problem with module ordering breaking weaving - has been trapped by implementors of this method.
return method.getReturnType();
}
/**
* Get the list of methods in a class. Wrap the call in doPrivileged if
* necessary. This call will traverse the superclasses.
* @param clazz the class to get the methods from.
*/
public static Method[] getMethods(final Class clazz) {
return clazz.getMethods();
}
/**
* Get the value of the given field in the given object.
*/
public static Object getValueFromField(final Field field, final Object object) throws IllegalAccessException {
return field.get(object);
}
/**
* Construct an object with the given Constructor and the given array of arguments. Wrap the call in a
* doPrivileged block if necessary.
*/
public static Object invokeConstructor(final Constructor constructor, final Object[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
return constructor.newInstance(args);
}
/**
* Invoke the givenMethod on a givenObject. Assumes method does not take
* parameters. Wrap in a doPrivileged block if necessary.
*/
public static Object invokeMethod(final Method method, final Object object) throws IllegalAccessException, InvocationTargetException {
return invokeMethod(method, object, (Object[]) null);
}
/**
* Invoke the givenMethod on a givenObject using the array of parameters given. Wrap in a doPrivileged block
* if necessary.
*/
public static Object invokeMethod(final Method method, final Object object, final Object[] parameters) throws IllegalAccessException, InvocationTargetException {
// Ensure the method is accessible.
if (!method.isAccessible()) {
method.setAccessible(true);
}
return method.invoke(object, parameters);
}
/**
* Get a new instance of a class using the default constructor. Wrap the call in a privileged block
* if necessary.
*/
public static Object newInstanceFromClass(final Class clazz) throws IllegalAccessException, InstantiationException {
return clazz.newInstance();
}
/**
* Set the value of a given field in the given object with the value given. Wrap the call in a privileged block
* if necessary.
*/
public static void setValueInField(final Field field, final Object object, final Object value) throws IllegalAccessException {
field.set(object, value);
}
/**
* This method checks to see if calls should be made to doPrivileged.
* It will only return true if a security manager is enabled,
* and the "eclipselink.security.usedoprivileged" property is set.
* <p>
* Note: it is not possible to run EclipseLink using doPrivileged blocks when there is no SecurityManager
* enabled.
*/
public static boolean shouldUsePrivilegedAccess() {
// We will only detect whether to use doPrivileged once.
if (shouldCheckPrivilegedAccess) {
if (System.getSecurityManager() != null) {
Boolean privilegedPropertySet = (Boolean)AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
String usePrivileged = System.getProperty("eclipselink.security.usedoprivileged");
return (usePrivileged != null) && usePrivileged.equalsIgnoreCase("true");
}
});
shouldUsePrivilegedAccess = privilegedPropertySet.booleanValue();
} else {
shouldUsePrivilegedAccess = false;
}
shouldCheckPrivilegedAccess = false;
}
return shouldUsePrivilegedAccess;
}
}