blob: 5cf89d80fb8d05243ef8884a054852d4d0714a16 [file] [log] [blame]
/*
* 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.InvocationTargetException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.transformers.AttributeTransformer;
/**
* @version $Header: TransformerBasedValueHolder.java 30-aug-2006.11:32:36 gyorke Exp $
* @author mmacivor
* @since release specific (what release of product did this appear in)
* This class is to replace the MethodBasedValueHolder for TransformationMappings
* Holds on to an AttributeTransformer and uses it to generate the attribute value when triggered.
* That Transformer may be a MethodBasedAttributeTransformer or a user defined implementation.
*
*/
public class TransformerBasedValueHolder<T> extends DatabaseValueHolder<T> {
/**
* Stores the method to be executed. The method can take
* one (the row) or two parameters (the row and session).
*/
protected transient AttributeTransformer transformer;
/**
* Stores the object which owns this attribute.
*/
protected transient Object object;
/**
* Initialize the method-based value holder.
* @param theTransformer The method that returns the object when executed.
* @param theObject the Object which owns this attribute.
* @param theRow The row representation of the object.
* @param theSession The session to the database that stores the object.
*/
public TransformerBasedValueHolder(AttributeTransformer theTransformer, Object theObject, AbstractRecord theRow, AbstractSession theSession) {
super();
row = theRow;
session = theSession;
// Make sure not to put a ClientSession or IsolatedClientSession in
// the shared cache (indirectly).
// Skip this if unitOfWork, for we use session.isUnitOfWork() to implement
// isTransactionalValueholder(), saving us from needing a boolean instance variable.
// If unitOfWork this safety measure is deferred until merge time with
// releaseWrappedValuehHolder.
// Note that if isolated session & query will return itself, which is safe
// for if isolated this valueholder is not in the shared cache.
if (!session.isUnitOfWork()) {
this.session = session.getRootSession(null);
}
transformer = theTransformer;
object = theObject;
}
/**
* Return the method.
*/
protected AttributeTransformer getTransformer() {
return transformer;
}
/**
* Return the receiver.
*/
protected Object getObject() {
return object;
}
/**
* Instantiate the object by executing the method on the transformer.
*/
@Override
protected T instantiate() throws DescriptorException {
return instantiate(getObject(), getSession());
}
@SuppressWarnings({"unchecked"})
protected T instantiate(Object object, AbstractSession session) throws DescriptorException {
try {
return (T) transformer.buildAttributeValue(getRow(), object, session);
} catch (DescriptorException ex) {
Throwable nestedException = ex.getInternalException();
if (nestedException instanceof IllegalAccessException) {
throw DescriptorException.illegalAccessWhileInstantiatingMethodBasedProxy(nestedException);
} else if (nestedException instanceof IllegalArgumentException) {
throw DescriptorException.illegalArgumentWhileInstantiatingMethodBasedProxy(nestedException);
} else if (nestedException instanceof InvocationTargetException) {
throw DescriptorException.targetInvocationWhileInstantiatingMethodBasedProxy(nestedException);
} else {
throw ex;
}
}
}
/**
* Triggers UnitOfWork valueholders directly without triggering the wrapped
* valueholder (this).
* <p>
* When in transaction and/or for pessimistic locking the UnitOfWorkValueHolder
* needs to be triggered directly without triggering the wrapped valueholder.
* However only the wrapped valueholder knows how to trigger the indirection,
* i.e. it may be a batchValueHolder, and it stores all the info like the row
* and the query.
* Note: This method is not thread-safe. It must be used in a synchronized manner
*/
@Override
public T instantiateForUnitOfWorkValueHolder(UnitOfWorkValueHolder<T> unitOfWorkValueHolder) {
return instantiate(getObject(), unitOfWorkValueHolder.getUnitOfWork());
}
/**
* INTERNAL:
* Answers if this valueholder is a pessimistic locking one. Such valueholders
* are special in that they can be triggered multiple times by different
* UnitsOfWork. Each time a lock query will be issued. Hence even if
* instantiated it may have to be instantiated again, and once instantiated
* all fields can not be reset.
* Note: This method is not thread-safe. It must be used in a synchronized manner
*/
@Override
public boolean isPessimisticLockingValueHolder() {
// there is no way to tell, as a transformation mapping may have
// a reference class or query to check, but by design there is no
// way we can access at it.
return false;
}
/**
* Set the transformer.
*/
protected void setTransformer(AttributeTransformer theTransformer) {
transformer = theTransformer;
}
/**
* Set the receiver.
*/
protected void setObject(Object theObject) {
object = theObject;
}
}