org.eclipse.persistence.queries.MethodBaseQueryRedirector Maven / Gradle / Ivy
/*
* 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.queries;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker;
import org.eclipse.persistence.internal.sessions.AbstractRecord;
import org.eclipse.persistence.sessions.DataRecord;
import org.eclipse.persistence.sessions.Session;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
/**
* Purpose:
* Allows a class to be a QueryRedirector
without implementing
* {@link QueryRedirector QueryRedirector}.
*
* Description:
* Normally to define a Redirector a Class must implement QueryRedirector
and
* the required {@link QueryRedirector#invokeQuery QueryRedirector.invokeQuery(DatabaseQuery, Record, Session)}.
*
* To maintain transparency it is possible to instead only define a static
* method that takes the same arguments as invokeQuery
.
*
* An instance of MethodBaseQueryRedirector
can be constructed, taking the name of that static
* method and the Class
in which it is defined as parameters.
*
* Whenever invokeQuery
is called on this instance reflection will
* automatically be used to invoke the custom method instead.
*
* Advantages:
*
* - The Redirector class and method name can be specified dynamically.
*
- The class containing the
invokeQuery
method does not need to implement
* QueryRedirector
.
* - The
invokeQuery
method can have any name.
* - The
invokeQuery
method can alternatively be defined to accept only
* Session session
and Vector arguments
as parameters.
*
* Disadvantages:
*
* - An extra step is added as the real
invokeQuery
method is called
* dynamically.
*
* Example:
*
* // First create a named query, define a redirector for it, and add the query
* // to the query manager.
* ReadObjectQuery query = new ReadObjectQuery(Employee.class);
* query.setName("findEmployeeByAnEmployee");
* query.addArgument("employee");
*
* MethodBaseQueryRedirector redirector = new
* MethodBaseQueryRedirector(QueryRedirectorTest.class, "findEmployeeByAnEmployee");
* query.setRedirector(redirector);
* ClassDescriptor descriptor = getSession().getDescriptor(query.getReferenceClass());
* descriptor.getQueryManager().addQuery(query.getName(), query);
*
* // Now execute the query by name, passing in an Employee as an argument.
* Vector arguments = new Vector();
* arguments.addElement(employee);
* objectFromDatabase =
* getSession().executeQuery("findEmployeeByAnEmployee", Employee.class, arguments);
*
* // Note this Class does not implement QueryRedirector or method invokeQuery.
* public class QueryRedirectorTest {
* public static Object findEmployeeByAnEmployee(DatabaseQuery query, Record arguments, Session session) {
* ((ReadObjectQuery) query).setSelectionObject(arguments.get("employee"));
* return session.executeQuery(query);
* }
* }
*
* @see QueryRedirector
* @author James Sutherland
* @since TOPLink/Java 3.0
*/
public class MethodBaseQueryRedirector implements QueryRedirector {
protected Class methodClass;
protected String methodClassName;
protected String methodName;
protected transient Method method;
/**
* PUBLIC:
* Returns a new query redirector.
*/
public MethodBaseQueryRedirector() {
}
/**
* PUBLIC:
* Returns a new query redirector based on the static method in methodClass.
*/
public MethodBaseQueryRedirector(Class methodClass, String methodName) {
this.methodClass = methodClass;
this.methodName = methodName;
}
/**
* INTERNAL:
* Returns the static method.
*/
protected Method getMethod() {
return method;
}
/**
* PUBLIC:
* Returns the class to execute the static method on.
*/
public Class getMethodClass() {
return methodClass;
}
/**
* INTERNAL:
* Returns the class to execute the static method on.
*/
public String getMethodClassName() {
if ((methodClassName == null) && (methodClass != null)) {
methodClassName = methodClass.getName();
}
return methodClassName;
}
/**
* PUBLIC:
* Returns the name of the static method.
* This method must be public, static and have argument of DatabaseQuery, Vector, Session.
* @see #setMethodName
*/
public String getMethodName() {
return methodName;
}
/**
* INTERNAL:
* Set the method.
*/
protected void initializeMethod(DatabaseQuery query) throws QueryException {
if ((getMethodName() == null) || (getMethodClass() == null)) {
throw QueryException.redirectionClassOrMethodNotSet(query);
}
// Must check 3 possible argument sets for backward compatibility.
// The DatabaseQuery, Record, Session should be used, check last the throw correct exception.
// Check Session, Vector.
Class[] arguments = new Class[2];
arguments[0] = ClassConstants.SessionsSession_Class;
arguments[1] = ClassConstants.Vector_class;
try {
setMethod(Helper.getDeclaredMethod(getMethodClass(), getMethodName(), arguments));
} catch (Exception ignore) {
// Check DatabaseQuery, Record, Session.
arguments = new Class[3];
arguments[0] = ClassConstants.DatabaseQuery_Class;
arguments[1] = ClassConstants.Record_Class;
arguments[2] = ClassConstants.SessionsSession_Class;
try {
setMethod(Helper.getDeclaredMethod(getMethodClass(), getMethodName(), arguments));
} catch (Exception ignoreAgain) {
// Check DatabaseQuery, Record, Session.
arguments = new Class[3];
arguments[0] = ClassConstants.DatabaseQuery_Class;
arguments[1] = ClassConstants.Record_Class;
arguments[2] = ClassConstants.SessionsSession_Class;
try {
setMethod(Helper.getDeclaredMethod(getMethodClass(), getMethodName(), arguments));
} catch (Exception exception) {
throw QueryException.redirectionMethodNotDefinedCorrectly(getMethodClass(), getMethodName(), exception, query);
}
}
}
// Ensure the method is static.
if (!Modifier.isStatic(getMethod().getModifiers())) {
throw QueryException.redirectionMethodNotDefinedCorrectly(getMethodClass(), getMethodName(), null, query);
}
}
/**
* INTERNAL:
* Call the static method to execute the query.
*/
@Override
public Object invokeQuery(DatabaseQuery query, DataRecord arguments, Session session) {
if (getMethod() == null) {
initializeMethod(query);
}
// To different methods type are supported for backward compatibility.
// Check method types to call with correct arguments.
Object result = null;
if (getMethod().getParameterTypes().length == 3) {
Object[] argumentArray = new Object[3];
argumentArray[0] = query;
argumentArray[1] = arguments;
argumentArray[2] = session;
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
result = AccessController.doPrivileged(new PrivilegedMethodInvoker(getMethod(), null, argumentArray));
}else{
result = PrivilegedAccessHelper.invokeMethod(getMethod(), null, argumentArray);
}
} catch (Exception exception) {
throw QueryException.redirectionMethodError(exception, query);
}
} else {
Object[] argumentArray = new Object[2];
argumentArray[0] = session;
argumentArray[1] = ((AbstractRecord)arguments).getValues();
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
result = AccessController.doPrivileged(new PrivilegedMethodInvoker(getMethod(), null, argumentArray));
}else{
result = PrivilegedAccessHelper.invokeMethod(getMethod(), null, argumentArray);
}
} catch (Exception exception) {
throw QueryException.redirectionMethodError(exception, query);
}
}
return result;
}
/**
* INTERNAL:
* Sets the static method.
*/
protected void setMethod(Method newMethod) {
method = newMethod;
}
/**
* PUBLIC:
* Sets the class to execute the static method on.
*/
public void setMethodClass(Class newMethodClass) {
methodClass = newMethodClass;
}
/**
* INTERNAL:
* Sets the class to execute the static method on.
*/
public void setMethodClassName(String newMethodClassName) {
methodClassName = newMethodClassName;
}
/**
* PUBLIC:
* Sets the name of the static method.
* This method must be public, static and have arguments of DatabaseQuery, Record, and Session.
*
* The DatabaseQuery argument is the query that is currently being executed.
*
* The Record will contain the Argument names added to the Query through addArgument(Sting) or, in the case
* of an Object query, the object attribute field names. These names will
* reference the argument values passed into the query, or in the case of an
* Object Query the values from the object.
*
* The session argument is the session that the query is currently being executed on.
*
* Alternatively the method can take only (Session session, Vector arguments)
* as parameters.
*/
public void setMethodName(String newMethodName) {
methodName = newMethodName;
}
}