blob: c0f087de09c0a1d246182b4986280106d2c147cd [file] [log] [blame]
/*
* Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019 Payara Services Ltd.
*
* 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:
// 10/09/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 10/25/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 10/30/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 11/28/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 06/03/2013-2.5.1 Guy Pelletier
// - 402380: 3 jpa21/advanced tests failed on server with
// "java.lang.NoClassDefFoundError: org/eclipse/persistence/testing/models/jpa21/advanced/enums/Gender"
// 07/16/2013-2.5.1 Guy Pelletier
// - 412384: Applying Converter for parameterized basic-type for joda-time's DateTime does not work
package org.eclipse.persistence.mappings.converters;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.descriptors.ClassNameConversionRequired;
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.PrivilegedNewInstanceFromClass;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.DirectCollectionMapping;
import org.eclipse.persistence.mappings.DirectMapMapping;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.sessions.Session;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.PersistenceException;
/**
* A JPA attribute converter class wrapped with an EclipseLink converter. This
* class is placed directly on mappings.
*
* @author Guy Pelletier
* @since Eclipselink 2.5
*/
public class ConverterClass<T extends AttributeConverter<X,Y>,X,Y> implements Converter, ClassNameConversionRequired {
protected boolean isForMapKey;
protected boolean disableConversion;
protected Class fieldClassification;
protected String fieldClassificationName;
protected String attributeConverterClassName;
protected AttributeConverter<X,Y> attributeConverter;
protected AbstractSession session;
private Class<T> attributeConverterClass;
/**
* INTERNAL:
* This method will be called when creating a converter for an embedded
* mapping attribute. The isForMapKey information will need to be known
* for proper initialization.
*/
public ConverterClass(String attributeConverterClassName, boolean isForMapKey, String fieldClassificationName, boolean disableConversion) {
this.isForMapKey = isForMapKey;
this.disableConversion = disableConversion;
this.fieldClassificationName = fieldClassificationName;
this.attributeConverterClassName = attributeConverterClassName;
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this converter 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) {
attributeConverterClass = getAttributeConverterClass(classLoader);
constructFieldClassification(classLoader);
}
private void constructAttributeConverter() {
T attributeConverterInstance = getAttributeConverterInstance(attributeConverterClass);
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
if (attributeConverterInstance == null){
attributeConverterInstance = AccessController.doPrivileged(new PrivilegedNewInstanceFromClass<>(attributeConverterClass));
}
} catch (PrivilegedActionException exception) {
throw ValidationException.errorInstantiatingClass(attributeConverterClass, exception.getException());
}
} else {
if (attributeConverterInstance == null){
attributeConverterInstance = PrivilegedAccessHelper.newInstanceFromClass(attributeConverterClass);
}
}
} catch (IllegalAccessException | InstantiationException exception) {
throw ValidationException.errorInstantiatingClass(attributeConverterClass, exception);
}
attributeConverter = attributeConverterInstance;
}
private T getAttributeConverterInstance(Class<T> attributeConverterClass) {
try{
return session.<T>getInjectionManager().createManagedBeanAndInjectDependencies(attributeConverterClass);
} catch (Exception e){
session.logThrowable(SessionLog.FINEST, SessionLog.JPA, e);
return null;
}
}
private Class<T> getAttributeConverterClass(ClassLoader classLoader) {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) {
try {
return AccessController.doPrivileged(new PrivilegedClassForName<>(attributeConverterClassName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(attributeConverterClassName, exception.getException());
}
} else {
return PrivilegedAccessHelper.<T>getClassForName(attributeConverterClassName, true, classLoader);
}
} catch (ClassNotFoundException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(attributeConverterClassName, exception);
}
}
private void constructFieldClassification(ClassLoader classLoader) {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
fieldClassification = AccessController.doPrivileged(new PrivilegedClassForName<>(fieldClassificationName, true, classLoader));
} catch (PrivilegedActionException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(fieldClassificationName, exception.getException());
}
} else {
fieldClassification = PrivilegedAccessHelper.getClassForName(fieldClassificationName, true, classLoader);
}
} catch (ClassNotFoundException exception) {
throw ValidationException.classNotFoundWhileConvertingClassNames(fieldClassificationName, exception);
}
}
/**
* INTERNAL:
*/
@Override
@SuppressWarnings("unchecked")
public Object convertDataValueToObjectValue(Object dataValue, Session session) {
try {
return getAttributeConverter().convertToEntityAttribute((Y)dataValue);
} catch (RuntimeException re) {
throw new PersistenceException(ExceptionLocalization.buildMessage("wrap_convert_exception",
new Object[]{"convertToEntityAttribute", attributeConverterClassName, dataValue}), re);
}
}
/**
* INTERNAL:
*/
@Override
@SuppressWarnings("unchecked")
public Object convertObjectValueToDataValue(Object objectValue, Session session) {
try {
return getAttributeConverter().convertToDatabaseColumn((X) objectValue);
} catch (RuntimeException re) {
throw new PersistenceException(ExceptionLocalization.buildMessage("wrap_convert_exception",
new Object[]{"convertToDatabaseColumn", attributeConverterClassName, objectValue}), re);
}
}
/**
* INTERNAL:
*/
@Override
public void initialize(DatabaseMapping mapping, Session session) {
// Ensure the mapping has the correct field classification set.
if (mapping.isDirectToFieldMapping()) {
DirectToFieldMapping m = (DirectToFieldMapping) mapping;
if (disableConversion) {
m.setConverter(null);
} else {
m.setConverter(this);
m.setFieldClassification(fieldClassification);
m.setFieldClassificationClassName(fieldClassificationName);
}
} else if (mapping.isDirectMapMapping() && isForMapKey) {
DirectMapMapping m = (DirectMapMapping) mapping;
if (disableConversion) {
m.setKeyConverter(null);
} else {
m.setKeyConverter(this);
m.setDirectKeyFieldClassification(fieldClassification);
m.setDirectKeyFieldClassificationName(fieldClassificationName);
}
} else if (mapping.isDirectCollectionMapping()) {
DirectCollectionMapping m = (DirectCollectionMapping) mapping;
if (disableConversion) {
m.setValueConverter(null);
} else {
m.setValueConverter(this);
m.setDirectFieldClassification(fieldClassification);
m.setDirectFieldClassificationName(fieldClassificationName);
}
} else {
// TODO: what else could it be???
}
}
/**
* INTERNAL:
*/
@Override
public boolean isMutable() {
return false;
}
public void setSession(AbstractSession session) {
this.session = session;
}
protected AttributeConverter<X, Y> getAttributeConverter() {
if (attributeConverter == null) {
constructAttributeConverter();
}
return attributeConverter;
}
}