Please wait. This can take some minutes ...
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.
io.micronaut.data.runtime.event.EntityEventRegistry Maven / Gradle / Ivy
/*
* Copyright 2017-2021 original authors
*
* Licensed 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
*
* https://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 io.micronaut.data.runtime.event;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.context.BeanContext;
import io.micronaut.context.annotation.Primary;
import io.micronaut.context.processor.ExecutableMethodProcessor;
import io.micronaut.core.order.OrderUtil;
import io.micronaut.core.type.Argument;
import io.micronaut.data.annotation.event.*;
import io.micronaut.data.event.EntityEventContext;
import io.micronaut.data.event.EntityEventListener;
import io.micronaut.data.event.PersistenceEventException;
import io.micronaut.data.event.QueryEventContext;
import io.micronaut.data.event.listeners.*;
import io.micronaut.data.model.runtime.RuntimePersistentEntity;
import io.micronaut.inject.BeanDefinition;
import io.micronaut.inject.BeanDefinitionMethodReference;
import io.micronaut.inject.ExecutableMethod;
import jakarta.inject.Singleton;
import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* Primary implementation of the {@link EntityEventListener} interface that aggregates all other listeners.
*
* @author graemerocher
* @since 2.3.0
*/
@Singleton
@Primary
public class EntityEventRegistry implements EntityEventListener, ExecutableMethodProcessor {
public static final List> EVENT_TYPES = Arrays.asList(
PostLoad.class,
PostPersist.class,
PostRemove.class,
PostUpdate.class,
PrePersist.class,
PreRemove.class,
PreUpdate.class
);
private final Collection> allEventListeners;
private final Map, Map, EntityEventListener>> entityToEventListeners = new ConcurrentHashMap<>(50);
private final BeanContext beanContext;
private final Map, Collection>> beanEventHandlers = new HashMap<>(10);
/**
* Default constructor.
*
* @param beanContext The bean context
*/
public EntityEventRegistry(BeanContext beanContext) {
this.beanContext = beanContext;
//noinspection RedundantCast
this.allEventListeners = beanContext.getBeanDefinitions(EntityEventListener.class)
.stream().filter(bd -> ((Class) bd.getBeanType()) != (Class) getClass())
.collect(Collectors.toList());
}
@Override
public boolean supports(RuntimePersistentEntity entity, Class extends Annotation> eventType) {
Map, EntityEventListener> listeners = getListeners(entity);
return listeners.containsKey(eventType);
}
@Override
public boolean prePersist(@NonNull EntityEventContext context) {
try {
final EntityEventListener target = getListeners(context.getPersistentEntity()).get(PrePersist.class);
if (target != null) {
return target.prePersist(context);
}
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking pre-persist event listeners: " + e.getMessage(), e);
}
return true;
}
@Override
public void postPersist(@NonNull EntityEventContext context) {
try {
final EntityEventListener target = getListeners(context.getPersistentEntity()).get(PostPersist.class);
if (target != null) {
target.postPersist(context);
}
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking post-persist event listeners: " + e.getMessage(), e);
}
}
@Override
public void postLoad(@NonNull EntityEventContext context) {
try {
final EntityEventListener target = getListeners(context.getPersistentEntity()).get(PostLoad.class);
if (target != null) {
target.postLoad(context);
}
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking post-load event listeners: " + e.getMessage(), e);
}
}
@Override
public boolean preRemove(@NonNull EntityEventContext context) {
try {
final EntityEventListener target = getListeners(context.getPersistentEntity()).get(PreRemove.class);
if (target != null) {
return target.preRemove(context);
}
return true;
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking pre-remove event listeners: " + e.getMessage(), e);
}
}
@Override
public void postRemove(@NonNull EntityEventContext context) {
try {
final EntityEventListener target = getListeners(context.getPersistentEntity()).get(PostRemove.class);
if (target != null) {
target.postRemove(context);
}
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking post-remove event listeners: " + e.getMessage(), e);
}
}
@Override
public boolean preUpdate(@NonNull EntityEventContext context) {
try {
final EntityEventListener target = getListeners(context.getPersistentEntity()).get(PreUpdate.class);
if (target != null) {
return target.preUpdate(context);
}
return true;
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking pre-update event listeners: " + e.getMessage(), e);
}
}
@Override
public void postUpdate(@NonNull EntityEventContext context) {
try {
final EntityEventListener target = getListeners(context.getPersistentEntity()).get(PostUpdate.class);
if (target != null) {
target.postUpdate(context);
}
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking post-update event listeners: " + e.getMessage(), e);
}
}
@NonNull
private Map, EntityEventListener> getListeners(RuntimePersistentEntity entity) {
Map, EntityEventListener> listeners = entityToEventListeners.get(entity);
if (listeners == null) {
listeners = initListeners(entity);
entityToEventListeners.put(entity, listeners);
}
return listeners;
}
@NonNull
private Map, EntityEventListener> initListeners(RuntimePersistentEntity entity) {
Map, Collection>> listeners = new HashMap<>(8);
for (BeanDefinition beanDefinition : allEventListeners) {
List> typeArguments = beanDefinition.getTypeArguments();
if (typeArguments.isEmpty()) {
typeArguments = beanDefinition.getTypeArguments(EntityEventListener.class);
}
if (isApplicableListener(entity, typeArguments)) {
@SuppressWarnings("unchecked")
final EntityEventListener eventListener = beanContext.getBean(beanDefinition);
for (Class extends Annotation> et : EVENT_TYPES) {
if (eventListener.supports(entity, et)) {
final Collection> eventListeners =
listeners.computeIfAbsent(et, (t) -> new ArrayList<>(5));
eventListeners.add(eventListener);
}
}
}
}
beanEventHandlers.forEach((annotation, references) -> references.forEach(reference -> {
if (isApplicableListener(entity, Arrays.asList(reference.getArguments()))) {
final Object bean = beanContext.getBean(reference.getBeanDefinition());
final Collection> eventListeners =
listeners.computeIfAbsent(annotation, (t) -> new ArrayList<>(5));
if (annotation == PrePersist.class) {
eventListeners.add((PrePersistEventListener) entity1 -> {
try {
reference.invoke(bean, entity1);
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking pre-persist event listener method [" + reference.getDescription(true) + "]: " + e.getMessage(), e);
}
return true;
});
} else if (annotation == PreRemove.class) {
eventListeners.add((PreRemoveEventListener) entity1 -> {
try {
reference.invoke(bean, entity1);
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking pre-remove event listener method [" + reference.getDescription(true) + "]: " + e.getMessage(), e);
}
return true;
});
} else if (annotation == PreUpdate.class) {
eventListeners.add((PreUpdateEventListener) entity1 -> {
try {
reference.invoke(bean, entity1);
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking pre-update event listener method [" + reference.getDescription(true) + "]: " + e.getMessage(), e);
}
return true;
});
} else if (annotation == PostPersist.class) {
eventListeners.add((PostPersistEventListener) entity1 -> {
try {
reference.invoke(bean, entity1);
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking post-persist event listener method [" + reference.getDescription(true) + "]: " + e.getMessage(), e);
}
});
} else if (annotation == PostRemove.class) {
eventListeners.add((PostRemoveEventListener) entity1 -> {
try {
reference.invoke(bean, entity1);
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking post-remove event listener method [" + reference.getDescription(true) + "]: " + e.getMessage(), e);
}
});
} else if (annotation == PostUpdate.class) {
eventListeners.add((PostUpdateEventListener) entity1 -> {
try {
reference.invoke(bean, entity1);
} catch (Exception e) {
throw new PersistenceEventException("An error occurred invoking post-update event listener method [" + reference.getDescription(true) + "]: " + e.getMessage(), e);
}
});
}
}
}));
Map, EntityEventListener> finalListeners;
if (listeners.isEmpty()) {
finalListeners = Collections.emptyMap();
} else {
finalListeners = listeners.entrySet().stream().collect(Collectors.toMap(
Map.Entry::getKey,
(entry) -> {
final Collection> v = entry.getValue();
if (v.isEmpty()) {
return EntityEventListener.NOOP;
} else if (v.size() == 1) {
return v.iterator().next();
} else {
return new CompositeEventListener(v);
}
}
));
}
return finalListeners;
}
private boolean isApplicableListener(RuntimePersistentEntity entity, List> typeArguments) {
return typeArguments.isEmpty() || typeArguments.get(0).getType().isAssignableFrom(entity.getIntrospection().getBeanType());
}
@Override
public void process(BeanDefinition> beanDefinition, ExecutableMethod, ?> method) {
final Argument[] arguments = method.getArguments();
if (arguments.length == 1) {
final List> eventTypes = method
.getAnnotationTypesByStereotype(EntityEventMapping.class);
for (Class extends Annotation> eventType : eventTypes) {
@SuppressWarnings("unchecked") final BeanDefinitionMethodReference ref =
BeanDefinitionMethodReference.of(
(BeanDefinition) beanDefinition,
(ExecutableMethod) method
);
beanEventHandlers.computeIfAbsent(eventType, (t) -> new ArrayList<>(5)).add(ref);
}
}
}
private static final class CompositeEventListener implements EntityEventListener {
private final EntityEventListener[] listenerArray;
public CompositeEventListener(Collection> listeners) {
//noinspection unchecked
this.listenerArray = listeners.stream().sorted(OrderUtil.COMPARATOR).toArray(EntityEventListener[]::new);
}
@Override
public boolean supports(RuntimePersistentEntity entity, Class extends Annotation> eventType) {
for (EntityEventListener listener : listenerArray) {
if (listener.supports(entity, eventType)) {
return true;
}
}
return false;
}
@Override
public boolean prePersist(@NonNull EntityEventContext context) {
for (EntityEventListener listener : listenerArray) {
if (!listener.prePersist(context)) {
return false;
}
}
return true;
}
@Override
public void postPersist(@NonNull EntityEventContext context) {
for (EntityEventListener listener : listenerArray) {
listener.postPersist(context);
}
}
@Override
public void postLoad(@NonNull EntityEventContext context) {
for (EntityEventListener listener : listenerArray) {
listener.postLoad(context);
}
}
@Override
public boolean preRemove(@NonNull EntityEventContext context) {
for (EntityEventListener listener : listenerArray) {
if (!listener.preRemove(context)) {
return false;
}
}
return true;
}
@Override
public void postRemove(@NonNull EntityEventContext context) {
for (EntityEventListener listener : listenerArray) {
listener.postRemove(context);
}
}
@Override
public boolean preUpdate(@NonNull EntityEventContext context) {
for (EntityEventListener listener : listenerArray) {
if (!listener.preUpdate(context)) {
return false;
}
}
return true;
}
@Override
public boolean preQuery(@NonNull QueryEventContext context) {
for (EntityEventListener listener : listenerArray) {
if (!listener.preQuery(context)) {
return false;
}
}
return true;
}
@Override
public void postUpdate(@NonNull EntityEventContext context) {
for (EntityEventListener listener : listenerArray) {
listener.postUpdate(context);
}
}
}
}