blob: d9dd77d49f592856f836a525feb17eef150c17e7 [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:
// Oracle - initial API and implementation
// 08/01/2012-2.5 Chris Delahunt
// - 371950: Metadata caching
// 12/14/2017-3.0 Tomas Kraus
// - 291546: Performance degradation due to usage of Vector in DescriptorEventManager
package org.eclipse.persistence.internal.jpa.metadata.listeners;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEventListener;
import org.eclipse.persistence.descriptors.SerializableDescriptorEventHolder;
import org.eclipse.persistence.exceptions.ValidationException;
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;
public class JPAEntityListenerHolder implements SerializableDescriptorEventHolder, Cloneable {
public String listenerClassName;
public Boolean isDefaultListener;
public transient DescriptorEventListener listener;
public Map<String,java.util.List<MethodSerialImpl>> serializableMethods;
/**
* Default constructor.
*/
public JPAEntityListenerHolder() {
}
public void setIsDefaultListener(Boolean isDefaultListener) {
this.isDefaultListener = isDefaultListener;
}
@Override
public void addListenerToEventManager(ClassDescriptor descriptor, AbstractSession session, ClassLoader loader) {
if (listener == null) {
if (listenerClassName !=null) {
Class listenerClass = getListenerClass(loader);
if (DescriptorEventListener.class.isAssignableFrom(listenerClass)){
listener = (DescriptorEventListener)constructListenerInstance(listenerClass);
} else {
EntityListener entityListener = new EntityListener(listenerClass, descriptor.getJavaClass());
entityListener.setOwningSession(session);
if (!(serializableMethods == null)) {
//The user class is not a DescriptorEventListener, so wrap it in a JPA EntityListener instance
entityListener.setAllEventMethods(this.convertToMethods(loader));
}
listener = entityListener;
}
} else {
//it must be an EntityClassListener
EntityListener entityListener = new EntityClassListener(descriptor.getJavaClass());
entityListener.setAllEventMethods(this.convertToMethods(loader));
listener = entityListener;
}
}
//need to also check if this is a EntityClassListener and call
if (listenerClassName!=null) {
if (isDefaultListener) {
descriptor.getEventManager().addDefaultEventListener(listener);
} else {
descriptor.getEventManager().addEntityListenerEventListener(listener);
}
} else {
descriptor.getEventManager().setEntityEventListener(listener);
}
}
protected Object constructListenerInstance(Class listenerClass){
Object entityListenerClassInstance = null;
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
entityListenerClassInstance = AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(listenerClass));
} catch (PrivilegedActionException exception) {
throw ValidationException.errorInstantiatingClass(listenerClass, exception.getException());
}
} else {
entityListenerClassInstance = PrivilegedAccessHelper.newInstanceFromClass(listenerClass);
}
} catch (IllegalAccessException exception) {
throw ValidationException.errorInstantiatingClass(listenerClass, exception);
} catch (InstantiationException exception) {
throw ValidationException.errorInstantiatingClass(listenerClass, exception);
}
return entityListenerClassInstance;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public void convertToSerializableMethods(Map<String,java.util.List<Method>> methods) {
this.serializableMethods = new ConcurrentHashMap<>();
for (String event: methods.keySet()){
java.util.List<Method> methodList = methods.get(event);
java.util.List<MethodSerialImpl> newMethodList = new java.util.ArrayList<>();
for (Method method: methodList) {
MethodSerialImpl serializableMethod = new MethodSerialImpl(method);
newMethodList.add(serializableMethod);
}
this.serializableMethods.put(event, newMethodList);
}
}
/**
* INTERNAL:
* used to return an instance of the listenerClassName
* @return an instance of listenerClassName
*/
private Class getListenerClass(ClassLoader loader){
Class entityListenerClass = null;
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
entityListenerClass = AccessController.doPrivileged(new PrivilegedClassForName<>(listenerClassName, true, loader));
} catch (PrivilegedActionException exception) {
throw ValidationException.unableToLoadClass(listenerClassName, exception.getException());
}
} else {
entityListenerClass = PrivilegedAccessHelper.getClassForName(listenerClassName, true, loader);
}
} catch (ClassNotFoundException exception) {
throw ValidationException.unableToLoadClass(listenerClassName, exception);
}
return entityListenerClass;
}
/**
* INTERNAL:
* You can have multiple event methods for the same event, however, only
* one event method per class is permitted.
*/
public void addEventMethod(String event, Method method) {
if (!getMethods().containsKey(event)) {
List<MethodSerialImpl> methodsList = new ArrayList<>();
serializableMethods.put(event, methodsList);
}
MethodSerialImpl convertedMethod = new MethodSerialImpl(method);
serializableMethods.get(event).add(convertedMethod);
}
/**
* INTERNAL:
* This returns a hashtable of methods which are used in a JPA EntityListener instance, built from
* the MethodSerialImpl representation since Methods are not serializable
*/
public Map<String,java.util.List<Method>> convertToMethods(ClassLoader loader) {
Map<String,java.util.List<Method>> table = new ConcurrentHashMap<>();
for (String event: serializableMethods.keySet()){
java.util.List<MethodSerialImpl> methodList = serializableMethods.get(event);
java.util.List<Method> newMethodList = new java.util.ArrayList();
for (MethodSerialImpl serializedMethod: methodList) {
try {
Method method = serializedMethod.convertToMethod(loader);
newMethodList.add(method);
} catch (Exception e) {
throw new jakarta.persistence.PersistenceException(e);
}
}
table.put(event, newMethodList);
}
return table;
}
public Map<String,java.util.List<MethodSerialImpl>> getMethods() {
if (serializableMethods == null) {
serializableMethods = new ConcurrentHashMap<String, List<MethodSerialImpl>>();
}
return serializableMethods;
}
}