| /* |
| * 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 |
| // 05/16/2008-1.0M8 Guy Pelletier |
| // - 218084: Implement metadata merging functionality between mapping files |
| // 04/02/2009-2.0 Guy Pelletier |
| // - 270853: testBeerLifeCycleMethodAnnotationIgnored within xml merge testing need to be relocated |
| // 01/05/2010-2.1 Guy Pelletier |
| // - 211324: Add additional event(s) support to the EclipseLink-ORM.XML Schema |
| // 04/27/2010-2.1 Guy Pelletier |
| // - 309856: MappedSuperclasses from XML are not being initialized properly |
| // 07/15/2010-2.2 Guy Pelletier |
| // -311395 : Multiple lifecycle callback methods for the same lifecycle event |
| // 12/01/2010-2.2 Guy Pelletier |
| // - 331234: xml-mapping-metadata-complete overriden by metadata-complete specification |
| // 03/24/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 1) |
| package org.eclipse.persistence.internal.jpa.metadata.listeners; |
| |
| import java.lang.reflect.Method; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.persistence.descriptors.DescriptorEventListener; |
| |
| import org.eclipse.persistence.exceptions.ValidationException; |
| |
| import org.eclipse.persistence.internal.jpa.metadata.ORMetadata; |
| |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.MetadataAccessor; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAccessibleObject; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataMethod; |
| |
| import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings; |
| |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedClassForName; |
| import org.eclipse.persistence.internal.security.PrivilegedGetDeclaredMethods; |
| import org.eclipse.persistence.internal.security.PrivilegedGetMethods; |
| import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass; |
| |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_LOAD; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_PERSIST; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_REMOVE; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_POST_UPDATE; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRE_PERSIST; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRE_REMOVE; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRE_UPDATE; |
| |
| /** |
| * A MetadataEntityListener and is placed on the owning entity's descriptor. |
| * Callback methods from an EntityListener require a signature on the method. |
| * Namely, they must have an Object parameter. |
| * |
| * Key notes: |
| * - any metadata mapped from XML to this class must be compared in the |
| * equals method. |
| * - when loading from annotations, the constructor accepts the metadata |
| * accessor this metadata was loaded from. Used it to look up any |
| * 'companion' annotation needed for processing. |
| * - methods should be preserved in alphabetical order. |
| * |
| * @author Guy Pelletier |
| * @since TopLink 10.1.3/EJB 3.0 Preview |
| */ |
| public class EntityListenerMetadata extends ORMetadata implements Cloneable { |
| private MetadataClass m_entityListenerClass; |
| |
| protected EntityListener m_listener; |
| private String m_className; |
| private String m_postLoad; |
| private String m_postPersist; |
| private String m_postRemove; |
| private String m_postUpdate; |
| private String m_prePersist; |
| private String m_preRemove; |
| private String m_preUpdate; |
| |
| /** |
| * INTERNAL: |
| * Used for XML loading. |
| */ |
| public EntityListenerMetadata() { |
| super("<entity-listener>"); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for annotation loading. |
| */ |
| public EntityListenerMetadata(MetadataAnnotation entityListeners, MetadataClass entityListenerClass, MetadataAccessor accessor) { |
| super(entityListeners, accessor); |
| |
| m_entityListenerClass = entityListenerClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method should be called when dealing with default listeners. |
| */ |
| @Override |
| public Object clone() { |
| try { |
| return super.clone(); |
| } catch (CloneNotSupportedException error) { |
| throw new InternalError(error.getMessage()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean equals(Object objectToCompare) { |
| if (objectToCompare instanceof EntityListenerMetadata) { |
| EntityListenerMetadata entityListener = (EntityListenerMetadata) objectToCompare; |
| |
| if (! valuesMatch(m_className, entityListener.getClassName())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_postLoad, entityListener.getPostLoad())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_postPersist, entityListener.getPostPersist())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_postRemove, entityListener.getPostRemove())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_postUpdate, entityListener.getPostUpdate())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_prePersist, entityListener.getPrePersist())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_preRemove, entityListener.getPreRemove())) { |
| return false; |
| } |
| |
| return valuesMatch(m_preUpdate, entityListener.getPreUpdate()); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = m_className != null ? m_className.hashCode() : 0; |
| result = 31 * result + (m_postLoad != null ? m_postLoad.hashCode() : 0); |
| result = 31 * result + (m_postPersist != null ? m_postPersist.hashCode() : 0); |
| result = 31 * result + (m_postRemove != null ? m_postRemove.hashCode() : 0); |
| result = 31 * result + (m_postUpdate != null ? m_postUpdate.hashCode() : 0); |
| result = 31 * result + (m_prePersist != null ? m_prePersist.hashCode() : 0); |
| result = 31 * result + (m_preRemove != null ? m_preRemove.hashCode() : 0); |
| result = 31 * result + (m_preUpdate != null ? m_preUpdate.hashCode() : 0); |
| return result; |
| } |
| |
| /** |
| * INTERNAL: |
| * Find the method in the list where method.getName() == methodName. |
| */ |
| protected Method getCallbackMethod(String methodName, Method[] methods) { |
| Method method = getMethod(methodName, methods); |
| |
| if (method == null) { |
| throw ValidationException.invalidCallbackMethod(m_listener.getListenerClass(), methodName); |
| } |
| |
| return method; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns a list of methods from the given class, which can have private, |
| * protected, package and public access, AND will also return public |
| * methods from superclasses. |
| */ |
| Method[] getCandidateCallbackMethodsForEntityListener() { |
| Set<Method> candidateMethods = new HashSet<>(); |
| Class listenerClass = m_listener.getListenerClass(); |
| |
| // Add all the declared methods ... |
| Method[] declaredMethods = getDeclaredMethods(listenerClass); |
| for (int i = 0; i < declaredMethods.length; i++) { |
| candidateMethods.add(declaredMethods[i]); |
| } |
| |
| // Now add any public methods from superclasses ... |
| Method[] methods = getMethods(listenerClass); |
| for (int i = 0; i < methods.length; i++) { |
| if (candidateMethods.contains(methods[i])) { |
| continue; |
| } |
| |
| candidateMethods.add(methods[i]); |
| } |
| |
| return candidateMethods.toArray(new Method[candidateMethods.size()]); |
| } |
| |
| /** |
| * INTERNAL: |
| * Load a class from a given class name. |
| */ |
| Class getClass(MetadataClass metadataClass, ClassLoader loader) { |
| String classname = metadataClass.getName(); |
| |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| return AccessController.doPrivileged(new PrivilegedClassForName<>(classname, true, loader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.unableToLoadClass(classname, exception.getException()); |
| } |
| } else { |
| return PrivilegedAccessHelper.getClassForName(classname, true, loader); |
| } |
| } catch (ClassNotFoundException exception) { |
| throw ValidationException.unableToLoadClass(classname, exception); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getClassName() { |
| return m_className; |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the declared methods from a class using the doPriveleged security |
| * access. This call returns all methods (private, protected, package and |
| * public) on the given class ONLY. It does not traverse the superclasses. |
| */ |
| Method[] getDeclaredMethods(Class cls) { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| return AccessController.doPrivileged(new PrivilegedGetDeclaredMethods(cls)); |
| } else { |
| return PrivilegedAccessHelper.getDeclaredMethods(cls); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public String getIdentifier() { |
| return m_className; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected Object getInstance(Class cls) { |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| return AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(cls)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.errorInstantiatingClass(cls, exception.getException()); |
| } |
| } else { |
| return PrivilegedAccessHelper.newInstanceFromClass(cls); |
| } |
| } catch (IllegalAccessException exception) { |
| throw ValidationException.errorInstantiatingClass(cls, exception); |
| } catch (InstantiationException exception) { |
| throw ValidationException.errorInstantiatingClass(cls, exception); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Find the method in the list where method.getName() == methodName. |
| */ |
| Method getMethod(String methodName, Method[] methods) { |
| for (int i = 0; i < methods.length; i++) { |
| Method method = methods[i]; |
| |
| if (method.getName().equals(methodName)) { |
| return method; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the methods from a class using the doPriveleged security access. |
| * This call returns only public methods from the given class and its |
| * superclasses. |
| */ |
| Method[] getMethods(Class cls) { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| return AccessController.doPrivileged(new PrivilegedGetMethods(cls)); |
| } else { |
| return PrivilegedAccessHelper.getMethods(cls); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getPostLoad() { |
| return m_postLoad; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getPostPersist() { |
| return m_postPersist; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getPostRemove() { |
| return m_postRemove; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping |
| */ |
| public String getPostUpdate() { |
| return m_postUpdate; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getPrePersist() { |
| return m_prePersist; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getPreRemove() { |
| return m_preRemove; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getPreUpdate() { |
| return m_preUpdate; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) { |
| super.initXMLObject(accessibleObject, entityMappings); |
| |
| m_entityListenerClass = initXMLClassName(m_className); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void process(ClassAccessor classAccessor, ClassLoader loader, boolean isDefaultListener) { |
| // Make sure the entityListenerClass is initialized (default listeners |
| // are cloned and m_entityListenerClass may be null) |
| if (m_entityListenerClass == null) { |
| m_entityListenerClass = getMetadataFactory().getMetadataClass(m_className); |
| } |
| JPAEntityListenerHolder holder = new JPAEntityListenerHolder(); |
| holder.setIsDefaultListener(isDefaultListener); |
| |
| holder.listenerClassName = m_entityListenerClass.getName(); |
| |
| if (m_entityListenerClass.extendsInterface(DescriptorEventListener.class)) { |
| holder.listener = (DescriptorEventListener)getInstance(getClass(m_entityListenerClass, loader)); |
| } else { |
| // Initialize the listener class before processing the callback methods. |
| m_listener = new EntityListener(getClass(m_entityListenerClass, loader), getClass(classAccessor.getDescriptorJavaClass(), loader)); |
| // Process the callback methods defined from XML and annotations. |
| processCallbackMethods(getCandidateCallbackMethodsForEntityListener(), classAccessor); |
| holder.convertToSerializableMethods(m_listener.getAllEventMethods()); |
| holder.listener = m_listener; |
| m_listener.setOwningSession(getProject().getSession()); |
| } |
| classAccessor.getDescriptor().getClassDescriptor().getEventManager().addEntityListenerHolder(holder); |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the the callback methods. The XML defined callback methods are |
| * always added first, followed by those defined by annotations (only if not |
| * already defined in XML) |
| */ |
| protected void processCallbackMethods(Method[] methods, ClassAccessor classAccessor) { |
| // 1 - Set the XML specified methods first. |
| if (m_postLoad != null) { |
| setPostLoad(getCallbackMethod(m_postLoad, methods)); |
| } |
| |
| if (m_postPersist != null) { |
| setPostPersist(getCallbackMethod(m_postPersist, methods)); |
| } |
| |
| if (m_postRemove != null) { |
| setPostRemove(getCallbackMethod(m_postRemove, methods)); |
| } |
| |
| if (m_postUpdate != null) { |
| setPostUpdate(getCallbackMethod(m_postUpdate, methods)); |
| } |
| |
| if (m_prePersist != null) { |
| setPrePersist(getCallbackMethod(m_prePersist, methods)); |
| } |
| |
| if (m_preRemove != null) { |
| setPreRemove(getCallbackMethod(m_preRemove, methods)); |
| } |
| |
| if (m_preUpdate != null) { |
| setPreUpdate(getCallbackMethod(m_preUpdate, methods)); |
| } |
| |
| // 2 - Set any annotation defined methods second. We should only add |
| // add them if they were not overridden in XML. |
| for (Method method : methods) { |
| // Bug 495587 - Ignoring bridge methods |
| if (!method.isBridge()) { |
| MetadataMethod metadataMethod = getMetadataClass(method.getDeclaringClass().getName(), false).getMethod(method.getName(), method.getParameterTypes()); |
| // Metadata method can be null when dealing with jdk methods: equals, notify, toString, wait etc.. |
| if (metadataMethod != null) { |
| if (metadataMethod.isAnnotationPresent(JPA_POST_LOAD, classAccessor) && m_postLoad == null) { |
| setPostLoad(method); |
| } |
| |
| if (metadataMethod.isAnnotationPresent(JPA_POST_PERSIST, classAccessor) && m_postPersist == null) { |
| setPostPersist(method); |
| } |
| |
| if (metadataMethod.isAnnotationPresent(JPA_POST_REMOVE, classAccessor) && m_postRemove == null) { |
| setPostRemove(method); |
| } |
| |
| if (metadataMethod.isAnnotationPresent(JPA_POST_UPDATE, classAccessor) && m_postUpdate == null) { |
| setPostUpdate(method); |
| } |
| |
| if (metadataMethod.isAnnotationPresent(JPA_PRE_PERSIST, classAccessor) && m_prePersist == null) { |
| setPrePersist(method); |
| } |
| |
| if (metadataMethod.isAnnotationPresent(JPA_PRE_REMOVE, classAccessor) && m_preRemove == null) { |
| setPreRemove(method); |
| } |
| |
| if (metadataMethod.isAnnotationPresent(JPA_PRE_UPDATE, classAccessor) && m_preUpdate == null) { |
| setPreUpdate(method); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setClassName(String className) { |
| m_className = className; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the post load event method on the listener. |
| */ |
| protected void setPostLoad(Method method) { |
| // bug 259404: PostClone is called for all objects when registered with the unitOfWork |
| m_listener.setPostCloneMethod(method); |
| m_listener.setPostRefreshMethod(method); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPostLoad(String postLoad) { |
| m_postLoad = postLoad; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the post persist event method on the listener. |
| */ |
| protected void setPostPersist(Method method) { |
| m_listener.setPostInsertMethod(method); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPostPersist(String postPersist) { |
| m_postPersist = postPersist; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the post remove event method on the listener. |
| */ |
| protected void setPostRemove(Method method) { |
| m_listener.setPostDeleteMethod(method); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPostRemove(String postRemove) { |
| m_postRemove = postRemove; |
| } |
| |
| /** |
| * INTERNAL: |
| * * Set the post update event method on the listener. |
| */ |
| protected void setPostUpdate(Method method) { |
| m_listener.setPostUpdateMethod(method); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPostUpdate(String postUpdate) { |
| m_postUpdate = postUpdate; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the pre persist event method on the listener. |
| */ |
| protected void setPrePersist(Method method) { |
| m_listener.setPrePersistMethod(method); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping |
| */ |
| public void setPrePersist(String prePersist) { |
| m_prePersist = prePersist; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the pre remove event method on the listener. |
| */ |
| protected void setPreRemove(Method method) { |
| m_listener.setPreRemoveMethod(method); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPreRemove(String preRemove) { |
| m_preRemove = preRemove; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the pre update event method on the listener. |
| */ |
| protected void setPreUpdate(Method method) { |
| m_listener.setPreUpdateWithChangesMethod(method); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPreUpdate(String preUpdate) { |
| m_preUpdate = preUpdate; |
| } |
| } |