org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2021 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 java.lang.reflect.Proxy;
import java.util.Map;
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;
/**
* 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