/******************************************************************************* | |
* 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 | |
******************************************************************************/ | |
package org.eclipse.persistence.internal.descriptors; | |
import java.io.*; | |
import java.lang.reflect.*; | |
import java.security.AccessController; | |
import java.security.PrivilegedActionException; | |
import org.eclipse.persistence.descriptors.ClassDescriptor; | |
import org.eclipse.persistence.exceptions.*; | |
import org.eclipse.persistence.internal.core.descriptors.CoreInstantiationPolicy; | |
import org.eclipse.persistence.internal.helper.*; | |
import org.eclipse.persistence.internal.security.*; | |
import org.eclipse.persistence.internal.sessions.AbstractSession; | |
/** | |
* <b>Purpose</b>: Allows customization of how an object is created/instantiated.<p> | |
* | |
* So, here is how it works:<p> | |
* | |
* If there is no method specified<br> | |
* - all the other settings are ignored and<br> | |
* - the descriptor class's default constructor is invoked.<br> | |
* If a factory is specified<br> | |
* - the factoryClass and factoryClassMethod are ignored and<br> | |
* - the method is invoked on the factory.<br> | |
* If neither a factory nor a factoryClass are specified<br> | |
* - the factoryClassMethod is ignored and<br> | |
* - the method is invoked on the descriptor class (as a static).<br> | |
* If only the factoryClass is specified<br> | |
* - the factory is created by invoking the factoryClass' default (zero-argument) constructor and<br> | |
* - the method is invoked on the resulting factory.<br> | |
* If both the factoryClass and the factoryClassMethod are specified<br> | |
* - the factory is created by invoking the factoryClassMethod on the factoryClass (as a static) and<br> | |
* - the method is invoked on the resulting factory.<p> | |
* | |
* The only thing we can't support in the current configuration is invoking a static on some, | |
* client-specified, factoryClass to build new instances of the descriptor class; and it's debatable | |
* whether that is desirable...<p> | |
* | |
* It might be reasonable to rework this into a number of different classes that implement | |
* an interface... | |
*/ | |
public class InstantiationPolicy extends CoreInstantiationPolicy implements Cloneable, Serializable { | |
/** | |
* The method invoked on either the descriptor class (in which case it is static) or | |
* the factory (in which case it is not static) to build a new instance of the descriptor class. | |
*/ | |
protected String methodName; | |
/** The method is resolved during initialization, and it is not serialized. */ | |
protected transient Method method; | |
/** | |
* The class of the factory. The factory is instantiated by either invoking this class's default | |
* (zero-argument) constructor or the factoryMethod specified below. | |
*/ | |
protected Class factoryClass; | |
protected String factoryClassName; | |
/** | |
* Static method invoked on the factoryClass to get the factory instance. If this is null, the | |
* factory class's default (zero-argument) constructor is invoked. | |
*/ | |
protected String factoryMethodName; | |
/** | |
* The object factory. This can be specified directly by the client, or it can be built dynamically | |
* using the the factoryClass and, optionally, the factoryMethodName. | |
*/ | |
protected Object factory; | |
/** Backpointer to descriptor. */ | |
protected ClassDescriptor descriptor; | |
/** Must be transient because java.lang.Constructor is not serializable. */ | |
private transient Constructor defaultConstructor; | |
/** | |
* Default constructor | |
*/ | |
public InstantiationPolicy() { | |
super(); | |
} | |
/** | |
* Build and return a new instance, using the appropriate mechanism. | |
*/ | |
@Override | |
public Object buildNewInstance() throws DescriptorException { | |
// PERF: Just check method-name. | |
if (this.methodName == null) { | |
return buildNewInstanceUsingDefaultConstructor(); | |
} else { | |
return buildNewInstanceUsingFactory(); | |
} | |
} | |
/** | |
* Build and return a new instance, using the default (zero-argument) constructor. | |
*/ | |
protected Object buildNewInstanceUsingDefaultConstructor() throws DescriptorException { | |
try { | |
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ | |
try { | |
return AccessController.doPrivileged(new PrivilegedInvokeConstructor(getDefaultConstructor(), (Object[])null)); | |
} catch (PrivilegedActionException exception) { | |
Exception throwableException = exception.getException(); | |
if (throwableException instanceof InvocationTargetException){ | |
throw DescriptorException.targetInvocationWhileConstructorInstantiation(getDescriptor(), throwableException); | |
} else if (throwableException instanceof IllegalAccessException){ | |
throw DescriptorException.illegalAccessWhileConstructorInstantiation(getDescriptor(), throwableException); | |
} else { | |
throw DescriptorException.instantiationWhileConstructorInstantiation(getDescriptor(), throwableException); | |
} | |
} | |
} else { | |
// PERF: Check lazy init with null check. | |
if (this.defaultConstructor == null) { | |
getDefaultConstructor(); | |
} | |
return this.defaultConstructor.newInstance((Object[])null); | |
} | |
} catch (InvocationTargetException exception) { | |
throw DescriptorException.targetInvocationWhileConstructorInstantiation(getDescriptor(), exception); | |
} catch (IllegalAccessException exception) { | |
throw DescriptorException.illegalAccessWhileConstructorInstantiation(getDescriptor(), exception); | |
} catch (InstantiationException exception) { | |
throw DescriptorException.instantiationWhileConstructorInstantiation(getDescriptor(), exception); | |
} catch (NoSuchMethodError exception) { | |
// This exception is not documented but gets thrown. | |
throw DescriptorException.noSuchMethodWhileConstructorInstantiation(getDescriptor(), exception); | |
} catch (NullPointerException exception) { | |
// Some JVMs will throw a NULL pointer exception here | |
throw DescriptorException.nullPointerWhileConstructorInstantiation(getDescriptor(), exception); | |
} | |
} | |
/** | |
* Build and return a new instance, using the factory. | |
* The factory can be null, in which case the method is a static method defined by the descriptor class. | |
*/ | |
protected Object buildNewInstanceUsingFactory() throws DescriptorException { | |
try { | |
// If the method is static, the first argument is ignored and can be null | |
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ | |
try { | |
return AccessController.doPrivileged(new PrivilegedMethodInvoker(getMethod(), getFactory(), new Object[0])); | |
} catch (PrivilegedActionException exception) { | |
Exception throwableException = exception.getException(); | |
if (throwableException instanceof IllegalAccessException) { | |
throw DescriptorException.illegalAccessWhileMethodInstantiation(getMethod().toString(), this.getDescriptor(), throwableException); | |
} else { | |
throw DescriptorException.targetInvocationWhileMethodInstantiation(getMethod().toString(), this.getDescriptor(), throwableException); | |
} | |
} | |
} else { | |
return PrivilegedAccessHelper.invokeMethod(getMethod(), getFactory(), new Object[0]); | |
} | |
} catch (IllegalAccessException exception) { | |
throw DescriptorException.illegalAccessWhileMethodInstantiation(getMethod().toString(), this.getDescriptor(), exception); | |
} catch (InvocationTargetException exception) { | |
throw DescriptorException.targetInvocationWhileMethodInstantiation(getMethod().toString(), this.getDescriptor(), exception); | |
} catch (NullPointerException exception) { | |
// Some JVMs will throw a NULL pointer exception here | |
throw DescriptorException.nullPointerWhileMethodInstantiation(this.getMethod().toString(), this.getDescriptor(), exception); | |
} | |
} | |
/** | |
* INTERNAL: | |
* Clones the InstantiationPolicy | |
*/ | |
public Object clone() { | |
try { | |
// clones itself | |
return super.clone(); | |
} catch (Exception exception) { | |
; | |
} | |
return null; | |
} | |
/** | |
* Return the default (zero-argument) constructor for the descriptor class. | |
*/ | |
protected Constructor getDefaultConstructor() throws DescriptorException { | |
// Lazy initialize, because the constructor cannot be serialized | |
if (defaultConstructor == null) { | |
this.setDefaultConstructor(this.buildDefaultConstructor()); | |
} | |
return defaultConstructor; | |
} | |
/** | |
* Build and return the default (zero-argument) constructor for the descriptor class. | |
*/ | |
protected Constructor buildDefaultConstructor() throws DescriptorException { | |
return this.buildDefaultConstructorFor(this.getDescriptor().getJavaClass()); | |
} | |
/** | |
* Build and return the default (zero-argument) constructor for the specified class. | |
*/ | |
protected Constructor buildDefaultConstructorFor(Class javaClass) throws DescriptorException { | |
try { | |
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ | |
try { | |
return (Constructor)AccessController.doPrivileged(new PrivilegedGetDeclaredConstructorFor(javaClass, new Class[0], true)); | |
} catch (PrivilegedActionException exception) { | |
throw DescriptorException.noSuchMethodWhileInitializingInstantiationPolicy(javaClass.getName() + ".<Default Constructor>", getDescriptor(), exception.getException()); | |
} | |
} else { | |
return PrivilegedAccessHelper.getDeclaredConstructorFor(javaClass, new Class[0], true); | |
} | |
} catch (NoSuchMethodException exception) { | |
throw DescriptorException.noSuchMethodWhileInitializingInstantiationPolicy(javaClass.getName() + ".<Default Constructor>", getDescriptor(), exception); | |
} | |
} | |
protected ClassDescriptor getDescriptor() { | |
return descriptor; | |
} | |
public String getFactoryMethodName() { | |
return factoryMethodName; | |
} | |
public Object getFactory() { | |
return factory; | |
} | |
public Class getFactoryClass() { | |
return factoryClass; | |
} | |
public String getFactoryClassName() { | |
if ((factoryClassName == null) && (factoryClass != null)) { | |
factoryClassName = factoryClass.getName(); | |
} | |
return factoryClassName; | |
} | |
protected Method getMethod() { | |
return method; | |
} | |
public String getMethodName() { | |
return methodName; | |
} | |
/** | |
* If necessary, initialize the factory and the method. | |
*/ | |
public void initialize(AbstractSession session) throws DescriptorException { | |
if (this.isUsingDefaultConstructor()) { | |
// May not have a constructor as may be abstract or interface so only lazy init. | |
return; | |
} | |
try { | |
this.initializeMethod(); | |
if (!(Modifier.isStatic(getMethod().getModifiers()))) { | |
// If the factory has been specified directly, do not overwrite it | |
if (this.getFactory() == null) { | |
this.setFactory(this.buildFactory()); | |
} | |
} | |
} catch (DescriptorException ex) { | |
session.getIntegrityChecker().handleError(ex); | |
} | |
} | |
protected Object buildFactory() throws DescriptorException { | |
// If there is no factory class specified, there is no factory; | |
// we will be using a static method defined by the descriptor class... | |
if (this.getFactoryClass() == null) { | |
return null; | |
} | |
// If there is a factory class specified but no factory method name, | |
// instantiate the factory using the default constructor | |
if (this.getFactoryMethodName() == null) { | |
return this.buildFactoryUsingDefaultConstructor(); | |
} | |
// If both the factory class and the factory method name have been specified, | |
// instantiate the factory by invoking the static factory method | |
return this.buildFactoryUsingStaticMethod(); | |
} | |
/** | |
* Build and return the factory, using its default constructor. | |
*/ | |
protected Object buildFactoryUsingDefaultConstructor() throws DescriptorException { | |
try { | |
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ | |
try { | |
return AccessController.doPrivileged(new PrivilegedInvokeConstructor(buildFactoryDefaultConstructor(), (Object[])null)); | |
} catch (PrivilegedActionException exception) { | |
Exception throwableException = exception.getException(); | |
if (throwableException instanceof InvocationTargetException){ | |
throw DescriptorException.targetInvocationWhileConstructorInstantiationOfFactory(getDescriptor(), throwableException); | |
} else if (throwableException instanceof IllegalAccessException){ | |
throw DescriptorException.illegalAccessWhileConstructorInstantiationOfFactory(getDescriptor(), throwableException); | |
} else { | |
throw DescriptorException.instantiationWhileConstructorInstantiationOfFactory(getDescriptor(), throwableException); | |
} | |
} | |
} else { | |
return PrivilegedAccessHelper.invokeConstructor(buildFactoryDefaultConstructor(), (Object[])null); | |
} | |
} catch (InvocationTargetException exception) { | |
throw DescriptorException.targetInvocationWhileConstructorInstantiationOfFactory(getDescriptor(), exception); | |
} catch (IllegalAccessException exception) { | |
throw DescriptorException.illegalAccessWhileConstructorInstantiationOfFactory(getDescriptor(), exception); | |
} catch (InstantiationException exception) { | |
throw DescriptorException.instantiationWhileConstructorInstantiationOfFactory(getDescriptor(), exception); | |
} catch (NoSuchMethodError exception) { | |
// This exception is not documented but gets thrown. | |
throw DescriptorException.noSuchMethodWhileConstructorInstantiationOfFactory(getDescriptor(), exception); | |
} catch (NullPointerException exception) { | |
// Some JVMs will throw a NULL pointer exception here | |
throw DescriptorException.nullPointerWhileConstructorInstantiationOfFactory(getDescriptor(), exception); | |
} | |
} | |
/** | |
* Build and return the default (zero-argument) constructor for the factory class. | |
*/ | |
protected Constructor buildFactoryDefaultConstructor() throws DescriptorException { | |
return this.buildDefaultConstructorFor(this.getFactoryClass()); | |
} | |
/** | |
* Build and return the factory, using the specified static method. | |
*/ | |
protected Object buildFactoryUsingStaticMethod() throws DescriptorException { | |
Method factoryMethod = this.buildMethod(this.getFactoryClass(), this.getFactoryMethodName(), new Class[0]); | |
try { | |
// it should be static and zero-argument... | |
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ | |
try { | |
return AccessController.doPrivileged(new PrivilegedMethodInvoker(factoryMethod, null, null)); | |
} catch (PrivilegedActionException exception) { | |
Exception throwableException = exception.getException(); | |
if (throwableException instanceof IllegalAccessException) { | |
throw DescriptorException.illegalAccessWhileMethodInstantiationOfFactory(getFactoryMethodName(), getDescriptor(), throwableException); | |
} else { | |
throw DescriptorException.targetInvocationWhileMethodInstantiationOfFactory(getFactoryMethodName(), getDescriptor(), throwableException); | |
} | |
} | |
} else { | |
return PrivilegedAccessHelper.invokeMethod(factoryMethod, null, null); | |
} | |
} catch (IllegalAccessException exception) { | |
throw DescriptorException.illegalAccessWhileMethodInstantiationOfFactory(getFactoryMethodName(), getDescriptor(), exception); | |
} catch (InvocationTargetException exception) { | |
throw DescriptorException.targetInvocationWhileMethodInstantiationOfFactory(getFactoryMethodName(), getDescriptor(), exception); | |
} catch (NullPointerException exception) { | |
// Some JVMs will throw a NULL pointer exception here | |
throw DescriptorException.nullPointerWhileMethodInstantiationOfFactory(getFactoryMethodName(), getDescriptor(), exception); | |
} | |
} | |
/** | |
* Initialize the method. | |
* It is either a static on the descriptor class, or it is a non-static on the factory. | |
*/ | |
protected void initializeMethod() throws DescriptorException { | |
Class tempClass; | |
if (this.getFactory() != null) { | |
tempClass = this.getFactory().getClass(); | |
} else if (this.getFactoryClass() == null) { | |
tempClass = this.getDescriptor().getJavaClass(); | |
} else { | |
tempClass = this.getFactoryClass(); | |
} | |
this.setMethod(this.buildMethod(tempClass, this.getMethodName(), new Class[0])); | |
} | |
/** | |
* Build the specified method. | |
*/ | |
protected Method buildMethod(Class methodClass, String methodName, Class[] methodParameterTypes) throws DescriptorException { | |
try { | |
return Helper.getDeclaredMethod(methodClass, methodName, methodParameterTypes); | |
} catch (NoSuchMethodException exception) { | |
throw DescriptorException.noSuchMethodWhileInitializingInstantiationPolicy(methodClass.getName() + "." + methodName, this.getDescriptor(), exception); | |
} catch (SecurityException exception) { | |
throw DescriptorException.securityWhileInitializingInstantiationPolicy(methodClass.getName() + "." + methodName, this.getDescriptor(), exception); | |
} | |
} | |
/** | |
* If no method name is specified, they we have to use the default (zero-argument) constructor. | |
*/ | |
public boolean isUsingDefaultConstructor() { | |
return this.getMethodName() == null; | |
} | |
protected void setDefaultConstructor(Constructor defaultConstructor) { | |
this.defaultConstructor = defaultConstructor; | |
} | |
public void setDescriptor(ClassDescriptor descriptor) { | |
this.descriptor = descriptor; | |
} | |
protected void setFactoryMethodName(String factoryMethodName) { | |
this.factoryMethodName = factoryMethodName; | |
} | |
protected void setFactory(Object factory) { | |
this.factory = factory; | |
} | |
protected void setFactoryClass(Class factoryClass) { | |
this.factoryClass = factoryClass; | |
} | |
protected void setFactoryClassName(String factoryClassName) { | |
this.factoryClassName = factoryClassName; | |
} | |
protected void setMethod(Method method) { | |
this.method = method; | |
} | |
public void setMethodName(String methodName) { | |
this.methodName = methodName; | |
} | |
/** | |
* INTERNAL: | |
* Convert all the class-name-based settings in this InstantiationPolicy to actual class-based | |
* settings. This method is used when converting a project that has been built | |
* with class names to a project with classes. | |
* @param classLoader | |
*/ | |
public void convertClassNamesToClasses(ClassLoader classLoader){ | |
if (factoryClassName == null){ | |
return; | |
} | |
Class factoryClass = null; | |
try{ | |
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ | |
try { | |
factoryClass = (Class)AccessController.doPrivileged(new PrivilegedClassForName(factoryClassName, true, classLoader)); | |
} catch (PrivilegedActionException exception) { | |
throw ValidationException.classNotFoundWhileConvertingClassNames(factoryClassName, exception.getException()); | |
} | |
} else { | |
factoryClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(factoryClassName, true, classLoader); | |
} | |
} catch (ClassNotFoundException exc){ | |
throw ValidationException.classNotFoundWhileConvertingClassNames(factoryClassName, exc); | |
} | |
setFactoryClass(factoryClass); | |
} | |
public String toString() { | |
String mName = null; | |
if (this.isUsingDefaultConstructor()) { | |
mName = "<CONSTRUCTOR>"; | |
} else { | |
mName = this.getMethodName(); | |
} | |
return Helper.getShortClassName(this) + "(" + mName + ")"; | |
} | |
public void useDefaultConstructorInstantiationPolicy() { | |
setMethodName(null); | |
setFactory(null); | |
setFactoryClass(null); | |
setFactoryClassName(null); | |
setFactoryMethodName(null); | |
} | |
public void useFactoryInstantiationPolicy(Class factoryClass, String methodName) { | |
setMethodName(methodName); | |
setFactory(null); | |
setFactoryClass(factoryClass); | |
setFactoryClassName(factoryClass.getName()); | |
setFactoryMethodName(null); | |
} | |
public void useFactoryInstantiationPolicy(Class factoryClass, String methodName, String factoryMethodName) { | |
setMethodName(methodName); | |
setFactory(null); | |
setFactoryClass(factoryClass); | |
setFactoryClassName(factoryClass.getName()); | |
setFactoryMethodName(factoryMethodName); | |
} | |
public void useFactoryInstantiationPolicy(String factoryClassName, String methodName) { | |
setMethodName(methodName); | |
setFactory(null); | |
setFactoryClass(null); | |
setFactoryClassName(factoryClassName); | |
setFactoryMethodName(null); | |
} | |
public void useFactoryInstantiationPolicy(String factoryClassName, String methodName, String factoryMethodName) { | |
setMethodName(methodName); | |
setFactory(null); | |
setFactoryClass(null); | |
setFactoryClassName(factoryClassName); | |
setFactoryMethodName(factoryMethodName); | |
} | |
public void useFactoryInstantiationPolicy(Object factory, String methodName) { | |
setMethodName(methodName); | |
setFactory(factory); | |
setFactoryClass(null); | |
setFactoryClassName(null); | |
setFactoryMethodName(null); | |
} | |
public void useMethodInstantiationPolicy(String staticMethodName) { | |
setMethodName(staticMethodName); | |
setFactory(null); | |
setFactoryClass(null); | |
setFactoryClassName(null); | |
setFactoryMethodName(null); | |
} | |
} |