/******************************************************************************* * Copyright (c) 1998, 2015 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. * Note: SDO overrides this class so an override of getMethodReturnType * will also need an override in SDO. ******************************************************************************/ package org.eclipse.persistence.internal.descriptors; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.AccessController; import java.security.PrivilegedActionException; import org.eclipse.persistence.exceptions.DescriptorException; import org.eclipse.persistence.internal.helper.ConversionManager; import org.eclipse.persistence.internal.helper.Helper; import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; import org.eclipse.persistence.internal.security.PrivilegedGetMethodParameterTypes; import org.eclipse.persistence.internal.security.PrivilegedGetMethodReturnType; import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker; import org.eclipse.persistence.logging.AbstractSessionLog; import org.eclipse.persistence.logging.SessionLog; import org.eclipse.persistence.mappings.AttributeAccessor; /** * <p><b>Purpose</b>: A wrapper class for handling cases when the domain object attributes are * to be accessed thru the accessor methods. This could happen if the variables are not defined * public in the domain object. * * @author Sati * @since TOPLink/Java 1.0 */ public class MethodAttributeAccessor extends AttributeAccessor { protected String setMethodName = ""; protected String getMethodName; protected transient Method setMethod; protected transient Method getMethod; /** * Return the return type of the method accessor. */ @Override public Class getAttributeClass() { if (getGetMethod() == null) { return null; } return getGetMethodReturnType(); } /** * Gets the value of an instance variable in the object. */ @Override public Object getAttributeValueFromObject(Object anObject) throws DescriptorException { return getAttributeValueFromObject(anObject, (Object[]) null); } /** * Gets the value of an instance variable in the object. */ protected Object getAttributeValueFromObject(Object anObject, Object[] parameters) throws DescriptorException { try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { return AccessController.doPrivileged(new PrivilegedMethodInvoker(getGetMethod(), anObject, parameters)); } catch (PrivilegedActionException exception) { Exception throwableException = exception.getException(); if (throwableException instanceof IllegalAccessException) { throw DescriptorException.illegalAccessWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject.getClass().getName(), throwableException); } else { throw DescriptorException.targetInvocationWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject.getClass().getName(), throwableException); } } } else { // PERF: Direct-var access. return this.getMethod.invoke(anObject, parameters); } } catch (IllegalArgumentException exception) { throw DescriptorException.illegalArgumentWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject.getClass().getName(), exception); } catch (IllegalAccessException exception) { throw DescriptorException.illegalAccessWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject.getClass().getName(), exception); } catch (InvocationTargetException exception) { throw DescriptorException.targetInvocationWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject.getClass().getName(), exception); } catch (NullPointerException exception) { // Some JVM's throw this exception for some very odd reason throw DescriptorException.nullPointerWhileGettingValueThruMethodAccessor(getGetMethodName(), anObject.getClass().getName(), exception); } } /** * Return the accessor method for the attribute accessor. * 266912: For Metamodel API - change visibility from protected */ public Method getGetMethod() { return getMethod; } /** * Return the name of the accessor method for the attribute accessor. */ public String getGetMethodName() { return getMethodName; } /** * INTERNAL: * Return the GetMethod return type for this MethodAttributeAccessor. * A special check is made to determine if a missing method is a result of failed weaving. * @return */ // Note: SDO overrides this method and will handle a null GetMethod public Class getGetMethodReturnType() throws DescriptorException { // 323403: If the getMethod is missing - check for "_persistence_*_vh" to see if weaving was expected if(null == getGetMethod() && null != getGetMethodName() && (getGetMethodName().indexOf(Helper.PERSISTENCE_FIELDNAME_PREFIX) > -1)) { // warn before a possible NPE on accessing a weaved method that does not exist AbstractSessionLog.getLog().log(SessionLog.FINEST, "no_weaved_vh_method_found_verify_weaving_and_module_order", getGetMethodName(), null, this); // 323403: We cannot continue to process objects that are not weaved - if weaving is enabled // If we allow the getMethodReturnType to continue - we will throw an obscure NullPointerException throw DescriptorException.nullPointerWhileGettingValueThruMethodAccessorCausedByWeavingNotOccurringBecauseOfModuleOrder(getGetMethodName(), "", null); } if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { return AccessController.doPrivileged(new PrivilegedGetMethodReturnType(getGetMethod())); } catch (PrivilegedActionException exception) { // we should not get here since this call does not throw any checked exceptions return null; } } else { return PrivilegedAccessHelper.getMethodReturnType(getGetMethod()); } } /** * Return the set method for the attribute accessor. */ protected Method getSetMethod() { return setMethod; } /** * Return the name of the set method for the attribute accessor. */ public String getSetMethodName() { return setMethodName; } public Class getSetMethodParameterType() { return getSetMethodParameterType(0); } protected Class getSetMethodParameterType(int index) { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { return AccessController.doPrivileged(new PrivilegedGetMethodParameterTypes(getSetMethod()))[index]; } catch (PrivilegedActionException exception) { // we should not get here since this call does not throw any checked exceptions return null; } } else { return PrivilegedAccessHelper.getMethodParameterTypes(getSetMethod())[index]; } } protected Class[] getSetMethodParameterTypes() { return new Class[] {getGetMethodReturnType()}; } /** * Set get and set method after creating these methods by using * get and set method names */ @Override public void initializeAttributes(Class theJavaClass) throws DescriptorException { initializeAttributes(theJavaClass, (Class[]) null); } /** * Set get and set method after creating these methods by using * get and set method names */ protected void initializeAttributes(Class theJavaClass, Class[] getParameterTypes) throws DescriptorException { if (getAttributeName() == null) { throw DescriptorException.attributeNameNotSpecified(); } try { setGetMethod(Helper.getDeclaredMethod(theJavaClass, getGetMethodName(), getParameterTypes)); // The parameter type for the set method must always be the return type of the get method. if(!isWriteOnly()) { setSetMethod(Helper.getDeclaredMethod(theJavaClass, getSetMethodName(), getSetMethodParameterTypes())); } } catch (NoSuchMethodException ex) { DescriptorException descriptorException = DescriptorException.noSuchMethodWhileInitializingAttributesInMethodAccessor(getSetMethodName(), getGetMethodName(), theJavaClass.getName()); descriptorException.setInternalException(ex); throw descriptorException; } catch (SecurityException exception) { DescriptorException descriptorException = DescriptorException.securityWhileInitializingAttributesInMethodAccessor(getSetMethodName(), getGetMethodName(), theJavaClass.getName()); descriptorException.setInternalException(exception); throw descriptorException; } } /** * Returns true if this attribute accessor has been initialized and now stores a reference to the * class's attribute. An attribute accessor can become uninitialized on serialization. */ @Override public boolean isInitialized(){ return (this.getMethod != null || isReadOnly()) && (this.setMethod != null || isWriteOnly()); } @Override public boolean isMethodAttributeAccessor() { return true; } /** * Sets the value of the instance variable in the object to the value. */ @Override public void setAttributeValueInObject(Object domainObject, Object attributeValue) throws DescriptorException { setAttributeValueInObject(domainObject, attributeValue, new Object[] {attributeValue}); } /** * Sets the value of the instance variable in the object to the value. */ protected void setAttributeValueInObject(Object domainObject, Object attributeValue, Object[] parameters) throws DescriptorException { try { if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { AccessController.doPrivileged(new PrivilegedMethodInvoker(getSetMethod(), domainObject, parameters)); } catch (PrivilegedActionException exception) { Exception throwableException = exception.getException(); if (throwableException instanceof IllegalAccessException) { throw DescriptorException.illegalAccessWhileSettingValueThruMethodAccessor(getSetMethodName(), attributeValue, throwableException); } else { throw DescriptorException.targetInvocationWhileSettingValueThruMethodAccessor(getSetMethodName(), attributeValue, throwableException); } } } else { // PERF: Direct-var access. this.setMethod.invoke(domainObject, parameters); } } catch (IllegalAccessException exception) { throw DescriptorException.illegalAccessWhileSettingValueThruMethodAccessor(getSetMethodName(), attributeValue, exception); } catch (IllegalArgumentException exception) { // TODO: This code should be removed, it should not be required and may cause unwanted sideeffects. // Allow XML change set to merge correctly since new value in XML change set is always String try { if (attributeValue instanceof String) { Object newValue = ConversionManager.getDefaultManager().convertObject(attributeValue, getAttributeClass()); Object[] newParameters = new Object[1]; newParameters[0] = newValue; if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { AccessController.doPrivileged(new PrivilegedMethodInvoker(getSetMethod(), domainObject, newParameters)); } catch (PrivilegedActionException exc) { // Do nothing and move on to throw the original exception } } else { PrivilegedAccessHelper.invokeMethod(getSetMethod(), domainObject, newParameters); } return; } } catch (Exception e) { // Do nothing and move on to throw the original exception } throw DescriptorException.illegalArgumentWhileSettingValueThruMethodAccessor(getSetMethodName(), attributeValue, exception); } catch (InvocationTargetException exception) { throw DescriptorException.targetInvocationWhileSettingValueThruMethodAccessor(getSetMethodName(), attributeValue, exception); } catch (NullPointerException exception) { try { // TODO: This code should be removed, it should not be required and may cause unwanted side effects. // cr 3737 If a null pointer was thrown because EclipseLink attempted to set a null reference into a // primitive creating a primitive of value 0 to set in the object. // Is this really the best place for this? is this not why we have null-value and conversion-manager? Class fieldClass = getSetMethodParameterType(); //Found when fixing Bug2910086 if (fieldClass.isPrimitive() && (attributeValue == null)) { parameters[parameters.length-1] = ConversionManager.getDefaultManager().convertObject(Integer.valueOf(0), fieldClass); if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ try { AccessController.doPrivileged(new PrivilegedMethodInvoker(getSetMethod(), domainObject, parameters)); } catch (PrivilegedActionException exc) { Exception throwableException = exc.getException(); if (throwableException instanceof IllegalAccessException) { throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(getAttributeName(), null, throwableException); } else { throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(getAttributeName(), null, throwableException); } } } else { PrivilegedAccessHelper.invokeMethod(getSetMethod(), domainObject, parameters); } } else { // Some JVM's throw this exception for some very odd reason // See throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(getAttributeName(), attributeValue, exception); } } catch (IllegalAccessException accessException) { throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(getAttributeName(), attributeValue, exception); } catch (InvocationTargetException invocationException) { throw DescriptorException.nullPointerWhileSettingValueThruInstanceVariableAccessor(getAttributeName(), attributeValue, exception); } } } /** * Set the accessor method for the attribute accessor. */ protected void setGetMethod(Method getMethod) { this.getMethod = getMethod; } /** * Set the name of the accessor method for the attribute accessor. */ public void setGetMethodName(String getMethodName) { this.getMethodName = getMethodName; } /** * Set the set method for the attribute accessor. */ protected void setSetMethod(Method setMethod) { this.setMethod = setMethod; } /** * Set the name of the set method for the attribute accessor. */ public void setSetMethodName(String setMethodName) { this.setMethodName = setMethodName; } }