blob: 359013f402e3fbb723310511dbb1a0f996ad7608 [file] [log] [blame]
/*
* Copyright (c) 2012, 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:
// 02/08/2012-2.4 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 08/24/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 02/06/2013-2.5 Guy Pelletier
// - 382503: Use of @ConstructorResult with createNativeQuery(sqlString, resultSetMapping) results in NullPointerException
package org.eclipse.persistence.queries;
import java.lang.reflect.Constructor;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedClassForName;
import org.eclipse.persistence.internal.security.PrivilegedGetConstructorFor;
import org.eclipse.persistence.internal.security.PrivilegedInvokeConstructor;
import org.eclipse.persistence.sessions.DatabaseRecord;
/**
* <p><b>Purpose</b>:
* Concrete class to represent the ConstructorResult structure as defined by
* the JPA 2.1 Persistence specification.
*
* @author Guy Pelletier
* @since EclipseLink 2.4
*/
public class ConstructorResult extends SQLResult {
/** Stores the class of result */
protected String targetClassName;
protected transient Class targetClass;
/** Stored the column results of this constructor result */
protected List<ColumnResult> columnResults;
protected transient Constructor constructor;
protected Class[] constructorArgTypes;
/**
* Default constructor is protected. Users must initialize the constructor
* result with a target class.
*/
protected ConstructorResult() {
columnResults = new ArrayList<>();
}
/**
* Constructor accepting target class.
*/
public ConstructorResult(Class targetClass){
this();
if (targetClass == null) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("null_value_for_constructor_result"));
}
this.targetClass = targetClass;
this.targetClassName = targetClass.getName();
}
/**
* Constructor accepting target class name.
*/
public ConstructorResult(String targetClassName){
this();
if (targetClassName == null) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("null_value_for_constructor_result"));
}
this.targetClassName = targetClassName;
}
/**
* Add a column result to this constructor result.
*/
public void addColumnResult(ColumnResult columnResult) {
columnResults.add(columnResult);
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this query to actual class-based
* settings. This method is used when converting a project that has been built
* with class names to a project with classes.
*/
@Override
public void convertClassNamesToClasses(ClassLoader classLoader){
super.convertClassNamesToClasses(classLoader);
// Make sure the column results have been converted.
for (ColumnResult columnResult : columnResults) {
columnResult.convertClassNamesToClasses(classLoader);
}
//no need to get the class if we already have it
if (targetClass == null && targetClassName!=null) {
try{
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
targetClass = AccessController.doPrivileged(new PrivilegedClassForName(targetClassName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(targetClassName, exception.getException());
}
} else {
targetClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(targetClassName, true, classLoader);
}
} catch (ClassNotFoundException exc){
throw ValidationException.classNotFoundWhileConvertingClassNames(targetClassName, exc);
}
}
}
/**
* Return the columns result of this constructor result.
*/
public List<ColumnResult> getColumnResults() {
return columnResults;
}
/**
* INTERNAL:
* This method is a convenience method for extracting values from results/
*/
@Override
public Object getValueFromRecord(DatabaseRecord record, ResultSetMappingQuery query) {
if (constructor == null) {
initialize(record, query);
}
int columnResultsSize = getColumnResults().size();
Object[] constructorArgs = new Object[columnResultsSize];
for (int i = 0; i < columnResultsSize; i++) {
constructorArgs[i] = ConversionManager.getDefaultManager().convertObject(record.get(getColumnResults().get(i).getColumn()), constructorArgTypes[i]);
}
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
return AccessController.doPrivileged(new PrivilegedInvokeConstructor(constructor, constructorArgs));
} catch (PrivilegedActionException exception) {
throw QueryException.exceptionWhileInitializingConstructor(exception.getException(), query, targetClass);
}
} else {
return PrivilegedAccessHelper.invokeConstructor(constructor, constructorArgs);
}
} catch (IllegalAccessException exception) {
throw QueryException.exceptionWhileInitializingConstructor(exception, query, targetClass);
} catch (java.lang.reflect.InvocationTargetException exception) {
throw QueryException.exceptionWhileInitializingConstructor(exception, query, targetClass);
} catch (InstantiationException exception) {
throw QueryException.exceptionWhileInitializingConstructor(exception, query, targetClass);
}
}
/**
* INTERNAL:
*/
protected void initialize(DatabaseRecord record, ResultSetMappingQuery query) {
int columnResultsSize = getColumnResults().size();
constructorArgTypes = new Class[columnResultsSize];
for (int i = 0; i < columnResultsSize; i++) {
ColumnResult result = getColumnResults().get(i);
DatabaseField resultField = result.getColumn();
if (resultField.getType() == null) {
Object recordResultField = record.get(resultField);
if (recordResultField == null) {
throw QueryException.columnResultNotFound(resultField);
} else {
constructorArgTypes[i] = recordResultField.getClass();
}
} else {
constructorArgTypes[i] = resultField.getType();
}
}
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
constructor = AccessController.doPrivileged(new PrivilegedGetConstructorFor(targetClass, constructorArgTypes, true));
} catch (PrivilegedActionException exception) {
throw QueryException.exceptionWhileInitializingConstructor(exception.getException(), query, targetClass);
}
} else {
constructor = PrivilegedAccessHelper.getConstructorFor(targetClass, constructorArgTypes, true);
}
} catch (NoSuchMethodException exception) {
throw QueryException.exceptionWhileInitializingConstructor(exception, query, targetClass);
}
}
/**
* Return true if this is a constructor result.
*/
@Override
public boolean isConstructorResult(){
return true;
}
/**
* Set columns result of this constructor result.
*/
public void setColumnResults(List<ColumnResult> columnResults) {
this.columnResults = columnResults;
}
}