org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998, 2023 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.ClassDescriptor;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.IntegrityChecker;
import org.eclipse.persistence.indirection.ValueHolder;
import org.eclipse.persistence.indirection.ValueHolderInterface;
import org.eclipse.persistence.internal.descriptors.DescriptorIterator;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.MergeManager;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.internal.sessions.remote.ObjectDescriptor;
import org.eclipse.persistence.internal.sessions.remote.RemoteSessionController;
import org.eclipse.persistence.internal.sessions.remote.RemoteUnitOfWork;
import org.eclipse.persistence.queries.ObjectLevelReadQuery;
import org.eclipse.persistence.queries.ReadQuery;
import org.eclipse.persistence.sessions.remote.DistributedSession;
import java.lang.reflect.Proxy;
import java.util.Map;
/**
* ProxyIndirectionPolicy
*
* Define the behavior for Proxy Indirection.
*
* Proxy Indirection uses the Proxy
and InvocationHandler
features
* of JDK 1.3 to provide "transparent indirection" for 1:1 relationships. In order to use Proxy
* Indirection:
*
*
* - The target class must implement at least one public interface
* - The attribute on the source class must be typed as that public interface
*
*
* In this policy, proxy objects are returned during object creation. When a message other than
* toString
is called on the proxy the real object data is retrieved from the database.
*
* @see org.eclipse.persistence.internal.indirection.ProxyIndirectionHandler
* @author Rick Barkhouse
* @since TopLink 3.0
*/
public class ProxyIndirectionPolicy extends BasicIndirectionPolicy {
private final Class[] targetInterfaces;
public ProxyIndirectionPolicy(Class[] targetInterfaces) {
this.targetInterfaces = targetInterfaces;
}
public ProxyIndirectionPolicy() {
this.targetInterfaces = new Class[] { };
}
/**
* INTERNAL:
* Nothing required.
*/
@Override
public void initialize() {
// Nothing required
}
/**
* Reset the wrapper used to store the value.
*/
@Override
public void reset(Object target) {
// Nothing required.
}
/**
* INTERNAL:
* Return if targetInterfaces is not empty.
*/
public boolean hasTargetInterfaces() {
return (targetInterfaces != null) && (targetInterfaces.length != 0);
}
/**
* INTERNAL:
* Return the value to be stored in the object's attribute.
* This will be a proxy object.
*/
@Override
public Object valueFromRow(Object object) {
ValueHolderInterface valueHolder = new ValueHolder<>(object);
return ProxyIndirectionHandler.newProxyInstance(object.getClass(), targetInterfaces, valueHolder);
}
/**
* INTERNAL:
* Return the value to be stored in the object's attribute.
* This will be a proxy object.
*/
@Override
public Object valueFromQuery(ReadQuery query, AbstractRecord row, AbstractSession session) {
ClassDescriptor descriptor = null;
try {
// Need an instance of the implementing class
//CR#3838
descriptor = session.getDescriptor(query.getReferenceClass());
if (descriptor.isDescriptorForInterface()) {
descriptor = descriptor.getInterfacePolicy().getChildDescriptors().get(0);
}
} catch (Exception e) {
return null;
}
ValueHolderInterface valueHolder = new QueryBasedValueHolder<>(query, row, session);
return ProxyIndirectionHandler.newProxyInstance(descriptor.getJavaClass(), targetInterfaces, valueHolder);
}
/**
* INTERNAL:
* Return the value to be stored in the object's attribute.
* This value is determined by invoking the appropriate method on the object and passing it the
* row and session.
*/
@Override
public Object valueFromMethod(Object object, AbstractRecord row, AbstractSession session) {
ValueHolderInterface valueHolder = new TransformerBasedValueHolder<>(this.getTransformationMapping().getAttributeTransformer(), object, row, session);
return ProxyIndirectionHandler.newProxyInstance(object.getClass(), targetInterfaces, valueHolder);
}
/**
* INTERNAL:
* Return the value to be stored in the object's attribute.
* This value is determined by the batch query. *
* NOTE: Currently not supported anyway.
*/
@Override
public Object valueFromBatchQuery(ReadQuery batchQuery, AbstractRecord row, ObjectLevelReadQuery originalQuery, CacheKey parentCacheKey) {
Object object;
try {
// Need an instance of the implementing class
final ClassDescriptor cd = originalQuery.getDescriptor().isDescriptorForInterface() ?
originalQuery.getDescriptor().getInterfacePolicy().getChildDescriptors().get(0) : originalQuery.getDescriptor();
object = PrivilegedAccessHelper.callDoPrivilegedWithException(
() -> PrivilegedAccessHelper.newInstanceFromClass(cd.getJavaClass())
);
} catch (Exception e) {
//org.eclipse.persistence.internal.helper.Helper.toDo("*** Should probably throw some sort of TopLink exception here. ***");
e.printStackTrace();
return null;
}
ValueHolderInterface valueHolder = new BatchValueHolder<>(batchQuery, row, this.getForeignReferenceMapping(), originalQuery, parentCacheKey);
return ProxyIndirectionHandler.newProxyInstance(object.getClass(), targetInterfaces, valueHolder);
}
/**
* INTERNAL:
* Return whether the specified object is instantiated.
*/
@Override
public boolean objectIsInstantiated(Object object) {
if (object instanceof Proxy) {
ProxyIndirectionHandler handler = (ProxyIndirectionHandler)Proxy.getInvocationHandler(object);
ValueHolderInterface valueHolder = handler.getValueHolder();
return valueHolder.isInstantiated();
} else {
return true;
}
}
/**
* INTERNAL:
* Return whether the specified object can be instantiated without database access.
*/
@Override
public boolean objectIsEasilyInstantiated(Object object) {
if (object instanceof Proxy) {
ProxyIndirectionHandler handler = (ProxyIndirectionHandler)Proxy.getInvocationHandler(object);
ValueHolderInterface valueHolder = handler.getValueHolder();
if (valueHolder instanceof DatabaseValueHolder) {
return ((DatabaseValueHolder)valueHolder).isEasilyInstantiated();
}
}
return true;
}
/**
* INTERNAL:
* Return the null value of the appropriate attribute. That is, the field from the database is NULL,
* return what should be placed in the object's attribute as a result.
*/
@Override
public Object nullValueFromRow() {
return null;
}
/**
* INTERNAL:
* Replace the client value holder with the server value holder after copying some of the settings from
* the client value holder.
*/
@Override
public void mergeRemoteValueHolder(Object clientSideDomainObject, Object serverSideDomainObject, MergeManager mergeManager) {
getMapping().setAttributeValueInObject(clientSideDomainObject, serverSideDomainObject);
}
/**
* INTERNAL:
* Return the "real" attribute value, as opposed to any wrapper. This will trigger the wrapper to
* instantiate the value.
*/
@Override
public Object getRealAttributeValueFromObject(Object obj, Object object) {
if (object instanceof Proxy) {
ProxyIndirectionHandler handler = (ProxyIndirectionHandler)Proxy.getInvocationHandler(object);
ValueHolderInterface valueHolder = handler.getValueHolder();
return valueHolder.getValue();
} else {
return object;
}
}
/**
* INTERNAL:
* Given a proxy object, trigger the indirection and return the actual object represented by the proxy.
*/
public static Object getValueFromProxy(Object value) {
if (Proxy.isProxyClass(value.getClass())) {
return ((ProxyIndirectionHandler)Proxy.getInvocationHandler(value)).getValueHolder().getValue();
}
return value;
}
/**
* INTERNAL:
* Set the "real" value of the attribute to attributeValue.
*/
@Override
public void setRealAttributeValueInObject(Object target, Object attributeValue) {
this.getMapping().setAttributeValueInObject(target, attributeValue);
}
/**
* INTERNAL:
* Return the original indirection object for a unit of work indirection object.
*/
@Override
public Object getOriginalIndirectionObject(Object unitOfWorkIndirectionObject, AbstractSession session) {
if (unitOfWorkIndirectionObject instanceof UnitOfWorkValueHolder) {
ValueHolderInterface valueHolder = ((UnitOfWorkValueHolder)unitOfWorkIndirectionObject).getWrappedValueHolder();
if ((valueHolder == null) && session.isRemoteUnitOfWork()) {
RemoteSessionController controller = ((RemoteUnitOfWork)session).getParentSessionController();
valueHolder = controller.getRemoteValueHolders().get(((UnitOfWorkValueHolder)unitOfWorkIndirectionObject).getWrappedValueHolderRemoteID());
}
return valueHolder;
} else {
return unitOfWorkIndirectionObject;
}
}
/**
* INTERNAL:
* An object has been serialized from the server to the client. Replace the transient attributes of the
* remote value holders with client-side objects.
*/
@Override
public void fixObjectReferences(Object object, Map