io.permazen.annotation.OnDelete Maven / Gradle / Ivy
Show all versions of permazen-main Show documentation
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package io.permazen.annotation;
import io.permazen.DetachedPermazenTransaction;
import io.permazen.ReferencePath;
import io.permazen.core.Transaction;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotates Permazen model class methods that are to be invoked whenever an object is about to be deleted.
*
*
*
*
*
* Overview
*
*
* When a matching object is deleted, annotated methods are invoked just prior to actual deletion.
*
*
* Methods must return void and normally take one parameter. A matching object is one whose type is compatible
* with the method parameter and which is found at the end of the {@linkplain ReferencePath reference path}
* specified by {@link #path}, starting from the object to be notified. See {@link ReferencePath} for more
* information about reference paths.
*
*
* In the case of an instance method where {@link #path} is empty (the default), the method is allowed to take
* zero parameters; in this case, the object monitors itself.
*
*
* For static methods, {@link #path} must be empty and every object whose type is compatible with the method's parameter
* is a matching object and can produce notifications.
*
*
* The annotated method may may have any level of access, including {@code private}.
*
*
* A class may have multiple {@link OnDelete @OnDelete} methods, each with a specific purpose.
*
*
Examples
*
*
* This example shows various ways an annotated method can be matched:
*
*
* @PermazenType
* public abstract class User implements PermazenObject {
*
* public abstract Account getAccount();
* public abstract void setAccount(Account account);
*
* @OnDelete(path = "->account")
* private void handleDeletion1(Account account) {
* // Invoked when MY Account is deleted
* }
* }
*
* @PermazenType
* public abstract class Feature implements PermazenObject {
*
* public abstract Account getAccount();
* public abstract void setAccount(Account account);
*
* @OnDelete(path = "->account<-User.account")
* private void handleDeletion1(User user) {
* // Invoked when ANY User associated with MY Account is deleted
* }
* }
*
* @PermazenType
* public abstract class Account implements PermazenObject {
*
* @NotNull
* public abstract String getName();
* public abstract void setName(String name);
*
* // Non-static @OnDelete methods
*
* @OnDelete
* private void handleDeletion1() {
* // Invoked when THIS Account is deleted
* }
*
* @OnDelete
* private void handleDeletion2(SpecialAccount self) {
* // Invoked when THIS Account is deleted IF it's also a SpecialAccount
* }
*
* @OnDelete(path = "<-User.account")
* private void handleDeletion3(User user) {
* // Invoked when ANY User associated with THIS Account is deleted
* }
*
* @OnDelete(path = "<-User.account")
* private void handleDeletion4(Object obj) {
* // Invoked when ANY User OR Feature associated with THIS Account is deleted
* }
*
* @OnDelete(path = "<-Feature.account->account<-User.account")
* private void handleDeletion5(User user) {
* // Invoked when ANY User associated with the same Account
* // as ANY Feature associated with THIS Account is deleted
* }
*
* // Static @OnDelete methods
*
* @OnDelete
* private static void handleDeletion5(Object obj) {
* // Invoked when ANY object is deleted
* }
*
* @OnDelete
* private static void handleDeletion6(Account account) {
* // Invoked when ANY Account is deleted
* }
* }
*
*
* Instance vs. Static Methods
*
*
* An instance method will be invoked on each object for which the deleted object is found at the end
* of the specified reference path, starting from that object. For example, if there are three child {@code Node}'s
* pointing to the same parent {@code Node}, and the {@code Node} class has an instance method annotated with
* {@link OnDelete @OnDelete}{@code (path = "parent")}, then all three child {@code Node}'s will be notified
* when the parent is deleted.
*
*
* A static method is invoked once for any matching object; the {@link path} is ignored and must be empty.
*
*
Notification Delivery
*
*
* Notifications are delivered in the same thread that deletes the object, before the deletion actually occurs.
* At most one delete notification will ever be delivered for any object deletion event. In particular, if an
* annotated method attempts to re-entrantly delete the same object again, no new notification is delivered.
*
*
* Some notifications may need to be ignored by objects in {@linkplain DetachedPermazenTransaction detached} transactions;
* you can use {@code this.isDetached()} to detect that situation.
*
*
* Actions that have effects visible to the outside world should be made contingent on successful transaction commit,
* for example, via {@link Transaction#addCallback Transaction.addCallback()}.
*
*
Meta-Annotations
*
*
* This annotation may be configured indirectly as a Spring
* meta-annotation
* when {@code spring-core} is on the classpath.
*
* @see OnCreate
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Documented
public @interface OnDelete {
/**
* Specify the reference path to the target object(s) that should be monitored for deletion.
* See {@link ReferencePath} for information on reference paths and their proper syntax.
*
*
* The default empty path means the monitored object and the notified object are the same. In that case,
* the type of the parameter (if any) restricts notifications to compatible subclasses.
*
*
* When annotating static methods, this property is unused and must be left unset.
*
* @return reference path leading to monitored objects
* @see ReferencePath
*/
String path() default "";
}