org.eclipse.persistence.internal.jpa.metadata.listeners.EntityListener Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction 180e602
/*******************************************************************************
* Copyright (c) 1998, 2013 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 v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* 05/16/2008-1.0M8 Guy Pelletier
* - 218084: Implement metadata merging functionality between mapping files
* 01/05/2010-2.1 Guy Pelletier
* - 211324: Add additional event(s) support to the EclipseLink-ORM.XML Schema
* 07/15/2010-2.2 Guy Pelletier
* -311395 : Multiple lifecycle callback methods for the same lifecycle event
******************************************************************************/
package org.eclipse.persistence.internal.jpa.metadata.listeners;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.DescriptorEventAdapter;
import org.eclipse.persistence.descriptors.DescriptorEventManager;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker;
import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.logging.SessionLog;
/**
* An EntityListener 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.
*
* @author Guy Pelletier
* @since Eclipselink 1.0
*/
public class EntityListener extends DescriptorEventAdapter {
public final static String POST_BUILD = "postBuild";
public final static String POST_CLONE = "postClone";
public final static String POST_DELETE = "postDelete";
public final static String POST_INSERT = "postInsert";
public final static String POST_REFRESH = "postRefresh";
public final static String POST_UPDATE = "postUpdate";
public final static String PRE_PERSIST = "prePersist";
public final static String PRE_REMOVE = "preRemove";
public final static String PRE_UPDATE_WITH_CHANGES = "preUpdateWithChanges";
private Object m_listener;
private Class m_listenerClass;
private Class m_entityClass;
private Hashtable> m_methods;
private Hashtable> m_overriddenEvents;
private static Hashtable m_eventStrings;
private AbstractSession owningSession;
/**
* INTERNAL:
*/
protected EntityListener(Class entityClass) {
m_entityClass = entityClass;
m_methods = new Hashtable>();
// Remember which events are overridden in subclasses. Overriden events
// must be built for each subclass chain.
m_overriddenEvents = new Hashtable>();
// For quick look up of equivalent event strings from event codes.
if (m_eventStrings == null) {
m_eventStrings = new Hashtable();
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PostBuildEvent), POST_BUILD);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PostCloneEvent), POST_CLONE);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PostDeleteEvent), POST_DELETE);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PostInsertEvent), POST_INSERT);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PostRefreshEvent), POST_REFRESH);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PostUpdateEvent), POST_UPDATE);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PrePersistEvent), PRE_PERSIST);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PreRemoveEvent), PRE_REMOVE);
m_eventStrings.put(Integer.valueOf(DescriptorEventManager.PreUpdateWithChangesEvent), PRE_UPDATE_WITH_CHANGES);
}
}
public EntityListener(Class listenerClass, Class entityClass){
this(entityClass);
this.m_listenerClass = listenerClass;
}
/**
* 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 (m_methods.containsKey(event)) {
Method lastEventMethod = getLastEventMethod(event);
if (lastEventMethod.getDeclaringClass().equals(method.getDeclaringClass())) {
throw ValidationException.multipleLifecycleCallbackMethodsForSameLifecycleEvent(getListenerClass(), method, lastEventMethod);
} else {
// We must be adding a callback method from a mapped superclass
// at this point, so validate the method and add it.
validateMethod(method);
m_methods.get(event).add(method);
}
} else {
// Validate the method is valid.
validateMethod(method);
// Create the methods list and add this method to it.
List methods = new ArrayList();
methods.add(method);
m_methods.put(event, methods);
}
}
/**
* Create the wrapped listener and trigger CDI injection.
* @param entityListenerClass
* @return the class instance that has had injection run on it. If injection failes, null.
*/
protected Object createEntityListenerAndInjectDependancies(Class entityListenerClass){
try{
return owningSession.getEntityListenerInjectionManager().createEntityListenerAndInjectDependancies(entityListenerClass);
} catch (Exception e){
owningSession.logThrowable(SessionLog.FINEST, SessionLog.JPA, e);
}
return null;
}
/**
* Construct an instance of the wrapped entity listener
* This method will attempt to create the listener in a CDI injection
* friendly manner and if that fails, reflectively instantiate the class
* @return
*/
protected Object constructListenerInstance(AbstractSession session){
Object entityListenerClassInstance = createEntityListenerAndInjectDependancies(m_listenerClass);
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
if (entityListenerClassInstance == null){
entityListenerClassInstance = AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(m_listenerClass));
}
} catch (PrivilegedActionException exception) {
throw ValidationException.errorInstantiatingClass(m_listenerClass, exception.getException());
}
} else {
if (entityListenerClassInstance == null){
entityListenerClassInstance = PrivilegedAccessHelper.newInstanceFromClass(m_listenerClass);
}
}
} catch (IllegalAccessException exception) {
throw ValidationException.errorInstantiatingClass(m_listenerClass, exception);
} catch (InstantiationException exception) {
throw ValidationException.errorInstantiatingClass(m_listenerClass, exception);
}
return entityListenerClassInstance;
}
/**
* INTERNAL:
*/
public Class getEntityClass() {
return m_entityClass;
}
/**
* INTERNAL:
*/
public Hashtable> getAllEventMethods() {
return m_methods;
}
/**
* INTERNAL:
*/
public void setAllEventMethods(Hashtable> methods) {
m_methods = methods;
}
/**
* INTERNAL:
*/
public void setOwningSession(AbstractSession owningSession) {
this.owningSession = owningSession;
}
/**
* INTERNAL:
*/
protected List getEventMethods(int eventCode) {
String eventString = m_eventStrings.get(eventCode);
if (eventString != null) {
return getEventMethods(eventString);
} else {
return null;
}
}
/**
* INTERNAL:
*/
protected List getEventMethods(String event) {
return m_methods.get(event);
}
/**
* INTERNAL:
* Assumes a check for event methods for the given event has been called
* beforehand.
*/
protected Method getLastEventMethod(String event) {
List methods = m_methods.get(event);
return methods.get(methods.size()-1);
}
public Object getListener(AbstractSession session) {
if (m_listener == null){
m_listener = constructListenerInstance(session);
}
return m_listener;
}
/**
* INTERNAL:
*/
public Class getListenerClass() {
return m_listenerClass;
}
/**
* INTERNAL:
*/
public AbstractSession getOwningSession() {
return owningSession;
}
/**
* INTERNAL:
*/
public boolean hasCallbackMethods() {
return m_methods.size() > 0;
}
/**
* INTERNAL:
*/
protected boolean hasEventMethods(int eventCode) {
return getEventMethods(eventCode) != null;
}
/**
* INTERNAL:
*/
protected boolean hasEventMethods(String event) {
return getEventMethods(event) != null;
}
/**
* INTERNAL:
*/
protected boolean hasOverriddenEventMethod(List eventMethods, Method eventMethod) {
if (eventMethods != null) {
for (Method method : eventMethods) {
if (method.getName().equals(eventMethod.getName())) {
return true;
}
}
}
return false;
}
/**
* INTERNAL:
*/
protected boolean hasOverriddenEventMethod(Method eventMethod, int eventCode) {
return hasOverriddenEventMethod(getEventMethods(eventCode), eventMethod);
}
/**
* INTERNAL:
*/
protected boolean hasOverriddenEventMethod(Method eventMethod, String eventCode) {
return hasOverriddenEventMethod(getEventMethods(eventCode), eventMethod);
}
/**
* INTERNAL:
*/
void invokeMethod(Method method, Object onObject, Object[] objectList, DescriptorEvent event) {
if (method != null) {
try {
if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
try {
AccessController.doPrivileged(new PrivilegedMethodInvoker(method, onObject, objectList));
} catch (PrivilegedActionException exception) {
Exception throwableException = exception.getException();
if (throwableException instanceof IllegalAccessException) {
throw ValidationException.invalidCallbackMethod(onObject.getClass(), method.toString());
} else {
Throwable cause = throwableException.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw (Error) cause;
}
}
}
} else {
PrivilegedAccessHelper.invokeMethod(method, onObject, objectList);
}
} catch (IllegalAccessException exception) {
throw ValidationException.invalidCallbackMethod(onObject.getClass(), method.toString());
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else {
throw (Error) cause;
}
}
}
}
/**
* INTERNAL:
*/
void invokeMethod(String event, DescriptorEvent descriptorEvent) {
List eventMethods = getEventMethods(event);
if (eventMethods != null) {
for (Method method : eventMethods) {
Object[] objectList = { descriptorEvent.getSource() };
invokeMethod(method, getListener(descriptorEvent.getSession()), objectList, descriptorEvent);
}
}
}
/**
* INTERNAL:
* Return true if listener has a lifecycle callback method that is
* overridden in a subclass.
*/
public boolean isOverriddenEvent(DescriptorEvent event, Vector eventManagers) {
int eventCode = event.getEventCode();
String forSubclass = event.getDescriptor().getJavaClassName();
Hashtable subClassMap = m_overriddenEvents.get(forSubclass);
// If we haven't built an overridden events map for this subclass, do so now.
if (subClassMap == null) {
subClassMap = new Hashtable();
}
// Now check the individual events for this subclass.
if (! subClassMap.containsKey(eventCode)) {
boolean hasOverrides = false;
if (hasEventMethods(eventCode)) {
List eventMethods = getEventMethods(eventCode);
for (Method eventMethod : eventMethods) {
for (DescriptorEventManager eventManager : (Vector) eventManagers) {
EntityListener childListener = (EntityListener) eventManager.getEntityEventListener();
// We can't override ourself ...
if (childListener == this) {
break;
} else {
if (childListener.hasOverriddenEventMethod(eventMethod, eventCode)) {
hasOverrides = true;
break;
}
}
}
if (hasOverrides){
break;
}
}
}
subClassMap.put(eventCode, hasOverrides);
//putting this here prevents a vm reorder from putting an unbuilt Map in the
//m_overriddenEvents collection
m_overriddenEvents.put(forSubclass, subClassMap);
}
return subClassMap.get(eventCode);
}
/**
* INTERNAL:
*/
public void postBuild(DescriptorEvent event) {
invokeMethod(POST_BUILD, event);
}
/**
* INTERNAL:
*/
public void postClone(DescriptorEvent event) {
invokeMethod(POST_CLONE, event);
}
/**
* INTERNAL:
*/
public void postDelete(DescriptorEvent event) {
invokeMethod(POST_DELETE, event);
}
/**
* INTERNAL:
*/
public void postInsert(DescriptorEvent event) {
invokeMethod(POST_INSERT, event);
}
/**
* INTERNAL:
*/
public void postRefresh(DescriptorEvent event) {
invokeMethod(POST_REFRESH, event);
}
/**
* INTERNAL:
*/
public void postUpdate(DescriptorEvent event) {
invokeMethod(POST_UPDATE, event);
}
/**
* INTERNAL:
*/
public void prePersist(DescriptorEvent event) {
invokeMethod(PRE_PERSIST, event);
}
/**
* INTERNAL:
*/
public void preRemove(DescriptorEvent event) {
invokeMethod(PRE_REMOVE, event);
}
/**
* INTERNAL:
*/
public void preUpdateWithChanges(DescriptorEvent event) {
invokeMethod(PRE_UPDATE_WITH_CHANGES, event);
}
/**
* INTERNAL:
*/
public void setPostBuildMethod(Method method) {
addEventMethod(POST_BUILD, method);
}
/**
* INTERNAL:
*/
public void setPostCloneMethod(Method method) {
addEventMethod(POST_CLONE, method);
}
/**
* INTERNAL:
*/
public void setPostDeleteMethod(Method method) {
addEventMethod(POST_DELETE, method);
}
/**
* INTERNAL:
*/
public void setPostInsertMethod(Method method) {
addEventMethod(POST_INSERT, method);
}
/**
* INTERNAL:
*/
public void setPostRefreshMethod(Method method) {
addEventMethod(POST_REFRESH, method);
}
/**
* INTERNAL:
*/
public void setPostUpdateMethod(Method method) {
addEventMethod(POST_UPDATE, method);
}
/**
* INTERNAL:
*/
public void setPrePersistMethod(Method method) {
addEventMethod(PRE_PERSIST, method);
}
/**
* INTERNAL:
*/
public void setPreRemoveMethod(Method method) {
addEventMethod(PRE_REMOVE, method);
}
/**
* INTERNAL:
*/
public void setPreUpdateWithChangesMethod(Method method) {
addEventMethod(PRE_UPDATE_WITH_CHANGES, method);
}
/**
* INTERNAL:
* Used in the debugger.
*/
public String toString() {
return getEntityClass().getName();
}
/**
* INTERNAL:
*/
protected void validateMethod(Method method) {
int numberOfParameters = method.getParameterTypes().length;
if (numberOfParameters == 1 && method.getParameterTypes()[0].isAssignableFrom(m_entityClass)) {
// So far so good, now check the method modifiers.
validateMethodModifiers(method);
} else {
Class parameterClass = (numberOfParameters == 0) ? null : method.getParameterTypes()[0];
throw ValidationException.invalidEntityListenerCallbackMethodArguments(m_entityClass, parameterClass, getListenerClass(), method.getName());
}
}
/**
* INTERNAL:
*/
protected void validateMethodModifiers(Method method) {
int modifiers = method.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
throw ValidationException.invalidCallbackMethodModifier(getListenerClass(), method.getName());
}
}
}