Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
****************************************************************/
package org.apache.cayenne.reflect;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.LifecycleListener;
import org.apache.cayenne.Persistent;
import org.apache.cayenne.annotation.PostAdd;
import org.apache.cayenne.annotation.PostLoad;
import org.apache.cayenne.annotation.PostPersist;
import org.apache.cayenne.annotation.PostRemove;
import org.apache.cayenne.annotation.PostUpdate;
import org.apache.cayenne.annotation.PrePersist;
import org.apache.cayenne.annotation.PreRemove;
import org.apache.cayenne.annotation.PreUpdate;
import org.apache.cayenne.map.EntityResolver;
import org.apache.cayenne.map.LifecycleEvent;
import org.apache.cayenne.map.ObjEntity;
import org.apache.cayenne.util.Util;
/**
* A registry of lifecycle callbacks for all callback event types. Valid event types are
* defined in {@link LifecycleEvent} enum.
*
* @since 3.0
*/
public class LifecycleCallbackRegistry {
private EntityResolver entityResolver;
private LifecycleCallbackEventHandler[] eventCallbacks;
private Map annotationsMap;
private Map>> entitiesByAnnotation;
/**
* Creates an empty callback registry.
*/
public LifecycleCallbackRegistry(EntityResolver resolver) {
this.entityResolver = resolver;
// initialize callbacks map in constructor to avoid synchronization issues
// downstream.
this.eventCallbacks = new LifecycleCallbackEventHandler[LifecycleEvent.values().length];
for (int i = 0; i < eventCallbacks.length; i++) {
eventCallbacks[i] = new LifecycleCallbackEventHandler(resolver);
}
// other "static" lookup maps are initialized on-demand
this.entitiesByAnnotation = new ConcurrentHashMap>>();
}
/**
* Removes all listeners for all event types.
*/
public void clear() {
for (LifecycleCallbackEventHandler eventCallback : eventCallbacks) {
eventCallback.clear();
}
}
/**
* Removes listeners for a single event type.
*/
public void clear(int type) {
eventCallbacks[type].clear();
}
/**
* Returns true if there are no listeners for a specific event type.
*/
public boolean isEmpty(LifecycleEvent type) {
return eventCallbacks[type.ordinal()].isEmpty();
}
/**
* Registers a {@link LifecycleListener} for all events on all entities. Note that
* listeners are not required to implement {@link LifecycleListener} interface. Other
* methods in this class can be used to register arbitrary listeners.
*/
public void addDefaultListener(LifecycleListener listener) {
addDefaultListener(LifecycleEvent.POST_ADD, listener, "postAdd");
addDefaultListener(LifecycleEvent.PRE_PERSIST, listener, "prePersist");
addDefaultListener(LifecycleEvent.POST_PERSIST, listener, "postPersist");
addDefaultListener(LifecycleEvent.PRE_REMOVE, listener, "preRemove");
addDefaultListener(LifecycleEvent.POST_REMOVE, listener, "postRemove");
addDefaultListener(LifecycleEvent.PRE_UPDATE, listener, "preUpdate");
addDefaultListener(LifecycleEvent.POST_UPDATE, listener, "postUpdate");
addDefaultListener(LifecycleEvent.POST_LOAD, listener, "postLoad");
}
/**
* Registers a callback method to be invoked on a provided non-entity object when a
* lifecycle event occurs on any entity that does not suppress default callbacks.
*/
public void addDefaultListener(LifecycleEvent type, Object listener, String methodName) {
eventCallbacks[type.ordinal()].addDefaultListener(listener, methodName);
}
/**
* Registers a {@link LifecycleListener} for all events on all entities. Note that
* listeners are not required to implement {@link LifecycleListener} interface. Other
* methods in this class can be used to register arbitrary listeners.
*/
public void addListener(Class> entityClass, LifecycleListener listener) {
addListener(LifecycleEvent.POST_ADD, entityClass, listener, "postAdd");
addListener(LifecycleEvent.PRE_PERSIST, entityClass, listener, "prePersist");
addListener(LifecycleEvent.POST_PERSIST, entityClass, listener, "postPersist");
addListener(LifecycleEvent.PRE_REMOVE, entityClass, listener, "preRemove");
addListener(LifecycleEvent.POST_REMOVE, entityClass, listener, "postRemove");
addListener(LifecycleEvent.PRE_UPDATE, entityClass, listener, "preUpdate");
addListener(LifecycleEvent.POST_UPDATE, entityClass, listener, "postUpdate");
addListener(LifecycleEvent.POST_LOAD, entityClass, listener, "postLoad");
}
/**
* Registers callback method to be invoked on a provided non-entity object when a
* lifecycle event occurs for a specific entity.
*/
public void addListener(
LifecycleEvent type,
Class> entityClass,
Object listener,
String methodName) {
eventCallbacks[type.ordinal()].addListener(entityClass, listener, methodName);
}
/**
* Registers a callback method to be invoked on an entity class instances when a
* lifecycle event occurs.
*/
public void addListener(LifecycleEvent type, Class> entityClass, String methodName) {
eventCallbacks[type.ordinal()].addListener(entityClass, methodName);
}
/**
* Adds a listener, mapping its methods to events based on annotations.
*
* @since 3.1
*/
public void addListener(Object listener) {
if (listener == null) {
throw new NullPointerException("Null listener");
}
Class> listenerType = listener.getClass();
do {
for (Method m : listenerType.getDeclaredMethods()) {
for (Annotation a : m.getAnnotations()) {
AnnotationReader reader = getAnnotationsMap().get(
a.annotationType().getName());
if (reader != null) {
Set> types = new HashSet>();
Class>[] entities = reader.entities(a);
Class extends Annotation>[] entityAnnotations =
reader.entityAnnotations(a);
for (Class> type : entities) {
// TODO: ignoring entity subclasses? whenever we add those,
// take
// into account "exlcudeSuperclassListeners" flag
types.add(type);
}
for (Class extends Annotation> type : entityAnnotations) {
types.addAll(getAnnotatedEntities(type));
}
for (Class> type : types) {
eventCallbacks[reader.eventType().ordinal()].addListener(
type,
listener,
m);
}
// if no entities specified then adding global callback
if (entities.length == 0 && entityAnnotations.length == 0) {
eventCallbacks[reader.eventType().ordinal()].addDefaultListener(
listener,
m.getName());
}
}
}
}
listenerType = listenerType.getSuperclass();
} while (listenerType != null && !listenerType.equals(Object.class));
}
/**
* Invokes callbacks of a specific type for a given entity object.
*/
public void performCallbacks(LifecycleEvent type, Persistent object) {
eventCallbacks[type.ordinal()].performCallbacks(object);
}
/**
* Invokes callbacks of a specific type for a collection of entity objects.
*/
public void performCallbacks(LifecycleEvent type, Collection> objects) {
eventCallbacks[type.ordinal()].performCallbacks(objects);
}
// used by unit tests to poke inside the registry
LifecycleCallbackEventHandler getHandler(LifecycleEvent type) {
return eventCallbacks[type.ordinal()];
}
private Map getAnnotationsMap() {
if (annotationsMap == null) {
Map annotationsMap = new HashMap();
annotationsMap.put(PostAdd.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.POST_ADD;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PostAdd) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PostAdd) a).value();
}
});
annotationsMap.put(PrePersist.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.PRE_PERSIST;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PrePersist) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PrePersist) a).value();
}
});
annotationsMap.put(PreRemove.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.PRE_REMOVE;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PreRemove) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PreRemove) a).value();
}
});
annotationsMap.put(PreUpdate.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.PRE_UPDATE;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PreUpdate) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PreUpdate) a).value();
}
});
annotationsMap.put(PostLoad.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.POST_LOAD;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PostLoad) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PostLoad) a).value();
}
});
annotationsMap.put(PostPersist.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.POST_PERSIST;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PostPersist) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PostPersist) a).value();
}
});
annotationsMap.put(PostUpdate.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.POST_UPDATE;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PostUpdate) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PostUpdate) a).value();
}
});
annotationsMap.put(PostRemove.class.getName(), new AnnotationReader() {
@Override
LifecycleEvent eventType() {
return LifecycleEvent.POST_REMOVE;
}
@Override
Class extends Annotation>[] entityAnnotations(Annotation a) {
return ((PostRemove) a).entityAnnotations();
}
@Override
Class>[] entities(Annotation a) {
return ((PostRemove) a).value();
}
});
this.annotationsMap = annotationsMap;
}
return annotationsMap;
}
private Collection> getAnnotatedEntities(
Class extends Annotation> annotationType) {
Collection> entities = entitiesByAnnotation
.get(annotationType.getName());
if (entities == null) {
// ensure no dupes
entities = new HashSet>();
for (ObjEntity entity : entityResolver.getObjEntities()) {
Class> entityType;
try {
entityType = Util.getJavaClass(entity.getClassName());
}
catch (ClassNotFoundException e) {
throw new CayenneRuntimeException("Class not found: "
+ entity.getClassName(), e);
}
// ensure that we don't register the same callback for multiple
// classes in the same hierarchy, so find the topmost type using a given
// annotation and register it once
// TODO: This ignores "excludeSuperclassListeners" setting, which is
// not possible with annotations anyways
while (entityType != null
&& entityType.isAnnotationPresent(annotationType)) {
Class> superType = entityType.getSuperclass();
if (superType == null
|| !superType.isAnnotationPresent(annotationType)) {
entities.add(entityType);
break;
}
entityType = superType;
}
}
entitiesByAnnotation.put(annotationType.getName(), entities);
}
return entities;
}
abstract class AnnotationReader {
abstract LifecycleEvent eventType();
abstract Class>[] entities(Annotation a);
abstract Class extends Annotation>[] entityAnnotations(Annotation a);
}
}