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

org.eclipse.persistence.internal.indirection.WeavedObjectBasicIndirectionPolicy Maven / Gradle / Ivy

/*
 * Copyright (c) 1998, 2024 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 v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.indirection;

import org.eclipse.persistence.descriptors.changetracking.ChangeTracker;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.indirection.WeavedAttributeValueHolderInterface;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;

import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * A WeavedObjectBasicIndirectionPolicy is used by OneToOne mappings that are LAZY through weaving
 * and which use Property(method) access.
 * 

* It extends BasicIndirection by providing the capability of calling the set method that was initially * mapped in addition to the set method for the weaved valueholder in order to coordinate the value of the * underlying property with the value stored in the valueholder * * @author Tom Ware */ public class WeavedObjectBasicIndirectionPolicy extends BasicIndirectionPolicy { /** Name of the initial set method. */ protected String setMethodName = null; /** Lazily initialized set method based on the set method name. */ protected transient Method setMethod = null; /** Name of the initial get method. */ protected String getMethodName; /** indicates whether the mapping has originally used method access */ protected boolean hasUsedMethodAccess; /** Stores the actual type of the mapping if different from the reference type. Used for set method invocation*/ protected String actualTypeClassName = null; public WeavedObjectBasicIndirectionPolicy(String getMethodName, String setMethodName, String actualTypeClassName, boolean hasUsedMethodAccess) { super(); this.setMethodName = setMethodName; this.getMethodName = getMethodName; this.hasUsedMethodAccess = hasUsedMethodAccess; this.actualTypeClassName = actualTypeClassName; } public String getActualTypeClassName() { return actualTypeClassName; } /** * Return the "real" attribute value, as opposed to any wrapper. * This will trigger the wrapper to instantiate the value. In a weaved policy, this will * also call the initial setter method to coordinate the values of the valueholder with * the underlying data. */ @Override public Object getRealAttributeValueFromObject(Object object, Object attribute) { boolean wasInstantiated = attribute != null && attribute instanceof ValueHolderInterface && ((ValueHolderInterface)attribute).isInstantiated(); Object value = super.getRealAttributeValueFromObject(object, attribute); // Provide the indirection policy with a callback that allows it to do any updates it needs as the result of getting the value. if (!wasInstantiated && (value != attribute)) { //if the attribute was already unwrapped then do not call this method updateValueInObject(object, value, attribute); } return value; } /** * This method will lazily initialize the set method * Lazy initialization occurs to that we are not required to have a handle on * the actual class that we are using until runtime. This helps to satisfy the * weaving requirement that demands that we avoid loading domain classes into * the main class loader until after weaving occurs. */ protected Method getSetMethod() { if (setMethod == null) { ForeignReferenceMapping sourceMapping = (ForeignReferenceMapping)mapping; // The parameter type for the set method must always be the return type of the get method. Class[] parameterTypes = new Class[1]; parameterTypes[0] = sourceMapping.getReferenceClass(); try { setMethod = Helper.getDeclaredMethod(sourceMapping.getDescriptor().getJavaClass(), setMethodName, parameterTypes); } catch (NoSuchMethodException e) { if (actualTypeClassName != null) { try { // try the actual class of the field or property parameterTypes[0] = Helper.getClassFromClasseName(actualTypeClassName, sourceMapping.getReferenceClass().getClassLoader()); setMethod = Helper.getDeclaredMethod(sourceMapping.getDescriptor().getJavaClass(), setMethodName, parameterTypes); } catch (NoSuchMethodException nsme) {} if (setMethod != null) { return setMethod; } } try { // Get the set method type from the get method. Method getMethod = Helper.getDeclaredMethod(sourceMapping.getDescriptor().getJavaClass(), getGetMethodName(), new Class[0]); parameterTypes[0] = getMethod.getReturnType(); setMethod = Helper.getDeclaredMethod(sourceMapping.getDescriptor().getJavaClass(), setMethodName, parameterTypes); } catch (NoSuchMethodException e2) { // As a last ditch effort, change the parameter type to Object.class. // If the model uses generics: // public T getStuntDouble() // public void setStuntDouble(T) // The weaved methods will be: // public Object getStuntDouble() and // public void setStuntDouble(Object) try { parameterTypes[0] = Object.class; setMethod = Helper.getDeclaredMethod(sourceMapping.getDescriptor().getJavaClass(), setMethodName, parameterTypes); } catch (NoSuchMethodException e3) { // Throw the original exception. throw DescriptorException.errorAccessingSetMethodOfEntity(sourceMapping.getDescriptor().getJavaClass(), setMethodName, sourceMapping.getDescriptor(), e); } } } } return setMethod; } /** * Coordinate the valueholder for this mapping with the underlying property by calling the * initial setter method. */ public void updateValueInObject(Object object, Object value, Object attributeValue){ setRealAttributeValueInObject(object, value); ((WeavedAttributeValueHolderInterface)attributeValue).setIsCoordinatedWithProperty(true); } /** * Set the value of the appropriate attribute of target to attributeValue. * In this case, place the value inside the target's ValueHolder. * Change tracking will be turned off when this method is called */ @Override public void setRealAttributeValueInObject(Object target, Object attributeValue) { setRealAttributeValueInObject(target, attributeValue, false); } /** * Set the value of the appropriate attribute of target to attributeValue. * In this case, place the value inside the target's ValueHolder. * if trackChanges is true, set the value in the object as if the user was setting it. Allow change tracking to pick up the change. */ @Override public void setRealAttributeValueInObject(Object target, Object attributeValue, boolean trackChanges) { // If the target object is using change tracking, it must be disable first to avoid thinking the value changed. PropertyChangeListener listener = null; ChangeTracker trackedObject = null; if (!trackChanges && target instanceof ChangeTracker) { trackedObject = (ChangeTracker)target; listener = trackedObject._persistence_getPropertyChangeListener(); trackedObject._persistence_setPropertyChangeListener(null); } Object[] parameters = new Object[1]; parameters[0] = attributeValue; try { PrivilegedAccessHelper.callDoPrivilegedWithException( () -> PrivilegedAccessHelper.invokeMethod(getSetMethod(), target, parameters), (ex) -> { if (ex instanceof IllegalAccessException) { return DescriptorException.illegalAccessWhileSettingValueThruMethodAccessor(setMethod.getName(), attributeValue, ex); } else if (ex instanceof IllegalArgumentException) { return DescriptorException.illegalArgumentWhileSettingValueThruMethodAccessor(setMethod.getName(), attributeValue, ex); } else if (ex instanceof InvocationTargetException) { return DescriptorException.targetInvocationWhileSettingValueThruMethodAccessor(setMethod.getName(), attributeValue, ex); } // This indicates unexpected problem in the code return new RuntimeException(String.format( "Invocation of %s setter method failed", setMethod.getName()), ex); } ); } finally { if (!trackChanges && trackedObject != null) { trackedObject._persistence_setPropertyChangeListener(listener); } } } public String getGetMethodName() { return this.getMethodName; } public String getSetMethodName() { return this.setMethodName; } public boolean hasUsedMethodAccess() { return this.hasUsedMethodAccess; } @Override public boolean isWeavedObjectBasicIndirectionPolicy() { return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy