| /* |
| * 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.queries; |
| |
| import java.lang.reflect.*; |
| import java.security.AccessController; |
| |
| import org.eclipse.persistence.exceptions.*; |
| import org.eclipse.persistence.sessions.*; |
| import org.eclipse.persistence.internal.helper.*; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| |
| /** |
| * <p><b>Purpose</b>:</p> |
| * Allows a class to be a <code>QueryRedirector</code> without implementing |
| * {@link QueryRedirector QueryRedirector}. |
| * |
| * <p><b>Description</b>:</p> |
| * Normally to define a Redirector a Class must implement <code>QueryRedirector</code> and |
| * the required {@link QueryRedirector#invokeQuery QueryRedirector.invokeQuery(DatabaseQuery, Record, Session)}. |
| * <p> |
| * To maintain transparency it is possible to instead only define a static |
| * method that takes the same arguments as <code>invokeQuery</code>. |
| * </p> |
| * An instance of <code>MethodBaseQueryRedirector</code> can be constructed, taking the name of that static |
| * method and the <code>Class</code> in which it is defined as parameters. |
| * <p> |
| * Whenever <code>invokeQuery</code> is called on this instance reflection will |
| * automatically be used to invoke the custom method instead. |
| * </p> |
| * <b>Advantages</b>: |
| * <ul> |
| * <li> The Redirector class and method name can be specified dynamically. |
| * <li> The class containing the <code>invokeQuery</code> method does not need to implement |
| * <code>QueryRedirector</code>. |
| * <li> The <code>invokeQuery</code> method can have any name. |
| * <li> The <code>invokeQuery</code> method can alternatively be defined to accept only |
| * <code>Session session</code> and <code>Vector arguments</code> as parameters. |
| * </ul> |
| * <b>Disadvantages</b>: |
| * <ul> |
| * <li> An extra step is added as the real <code>invokeQuery</code> method is called |
| * dynamically. |
| * </ul> |
| * <p><b>Example</b>:</p> |
| * <BLOCKQUOTE><PRE> |
| * // 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); |
| * } |
| * }</PRE></BLOCKQUOTE> |
| * |
| * @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.<p> |
| * This method must be public, static and have arguments of DatabaseQuery, Record, and Session. |
| * <p> |
| * The DatabaseQuery argument is the query that is currently being executed. |
| * <p> |
| * 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. |
| * <p> |
| * The session argument is the session that the query is currently being executed on. |
| * <p> |
| * Alternatively the method can take only <code>(Session session, Vector arguments)</code> |
| * as parameters. |
| */ |
| public void setMethodName(String newMethodName) { |
| methodName = newMethodName; |
| } |
| } |