Skip to content

Commit

Permalink
Merge pull request #405 from avaje/feature/preDestroy-priority
Browse files Browse the repository at this point in the history
Add support for PreDestroy priority to control ordering
  • Loading branch information
SentryMan authored Sep 21, 2023
2 parents b8393eb + 592375e commit bb7d57a
Show file tree
Hide file tree
Showing 21 changed files with 188 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.example.myapp;

import io.avaje.inject.PreDestroy;
import jakarta.inject.Singleton;
import org.example.myapp.aspect.MyTimed;

Expand Down Expand Up @@ -36,4 +37,9 @@ public void withParamImport(ConcurrentHashMap<String, String> param0) {
public void withListString(List<String> param0) {
System.out.println("withListString " + param0);
}

@PreDestroy(priority = 1001)
public void close() {
MyDestroyOrder.add("ExampleService");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@
public interface HelloData {

String helloData();

default void shutdown() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ void postCon(BeanScope scope) {

}

@PreDestroy
@PreDestroy(priority = 100)
void preDest() {

MyDestroyOrder.add("HelloService");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.example.myapp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class MyDestroyOrder {

private static final List<String> ordering = Collections.synchronizedList(new ArrayList<>());

public static void add(String val){
ordering.add(val);
}

public static List<String> ordering() {
return ordering;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.example.myapp.HelloData;
import org.example.myapp.MyDestroyOrder;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
Expand All @@ -13,12 +14,17 @@ public class AppConfig {

public static AtomicBoolean BEAN_AUTO_CLOSED = new AtomicBoolean();

@PreDestroy
void close() {
MyDestroyOrder.add("AppConfig");
}

@Bean(autoCloseable = true)
SomeInterface someInterface() {
return new SomeInterfaceWithClose();
}

@Bean
@Bean(destroyMethod = "shutdown", destroyPriority = 1500)
HelloData data() {
return new AppHelloData();
}
Expand All @@ -29,6 +35,11 @@ private static class AppHelloData implements HelloData {
public String helloData() {
return "AppHelloData";
}

@Override
public void shutdown() {
MyDestroyOrder.add("AppHelloData");
}
}

@Prototype
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package org.example.myapp.i347;

import io.avaje.inject.Component;
import io.avaje.inject.PreDestroy;
import org.example.myapp.MyDestroyOrder;

@Component
public class MyMetaDataRepo extends MyMongoRepo<MyMetaData> {
@Override
public MyMetaData doThings(MoDocument moDocument) {
return null;
}

@PreDestroy
void close() {
MyDestroyOrder.add("MyMetaDataRepo");
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package org.example.myapp.named;

import io.avaje.inject.Component;
import io.avaje.inject.PreDestroy;
import jakarta.inject.Named;
import org.example.myapp.MyDestroyOrder;

@Named("my-name-with-hyphens")
@Component
public class MyNamed {

@PreDestroy
public void close() {
MyDestroyOrder.add("MyNamed");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.example.myapp;

import io.avaje.inject.BeanScope;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class MyDestroyOrderTest {

@Test
void ordering() {
MyDestroyOrder.ordering().clear();
try (BeanScope beanScope = BeanScope.builder().build()) {
beanScope.get(HelloService.class);
}
List<String> ordering = MyDestroyOrder.ordering();
assertThat(ordering).containsExactly("HelloService", "AppConfig", "MyNamed", "MyMetaDataRepo", "ExampleService", "AppHelloData");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ final class BeanReader {
private final BeanAspects aspects;
private final BeanConditions conditions = new BeanConditions();
private final boolean importedComponent;
private final Integer preDestroyPriority;
private boolean writtenToFile;
private boolean suppressBuilderImport;
private boolean suppressGeneratedImport;
Expand All @@ -57,6 +58,7 @@ final class BeanReader {
this.factoryMethods = typeReader.factoryMethods();
this.postConstructMethod = typeReader.postConstructMethod();
this.preDestroyMethod = typeReader.preDestroyMethod();
this.preDestroyPriority = typeReader.preDestroyPriority();
this.constructor = typeReader.constructor();
this.importedComponent = importedComponent && (constructor != null && constructor.isPublic());
}
Expand Down Expand Up @@ -247,7 +249,8 @@ void addLifecycleCallbacks(Append writer, String indent) {
}
if (preDestroyMethod != null) {
prototypeNotSupported("@PreDestroy");
writer.append("%s builder.addPreDestroy($bean::%s);", indent, preDestroyMethod.getSimpleName()).eol();
var priority = preDestroyPriority == null || preDestroyPriority == 1000 ? "" : ", " + preDestroyPriority;
writer.append("%s builder.addPreDestroy($bean::%s%s);", indent, preDestroyMethod.getSimpleName(), priority).eol();
} else if (typeReader.isClosable() && !prototype) {
writer.append("%s builder.addPreDestroy($bean);", indent).eol();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ final class MethodReader {
private final boolean isFactory;
private final String initMethod;
private final String destroyMethod;
private final Integer destroyPriority;
private final boolean beanCloseable;
private final String name;
private final TypeReader typeReader;
Expand Down Expand Up @@ -72,6 +73,7 @@ final class MethodReader {
this.isVoid = Util.isVoid(topType);
String initMethod = (bean == null) ? null : bean.initMethod();
String destroyMethod = (bean == null) ? null : bean.destroyMethod();
this.destroyPriority = (bean == null) ? null : bean.destroyPriority();
this.beanCloseable = (bean != null) && bean.autoCloseable();
this.name = qualifierName;
TypeElement returnElement = asElement(returnMirror);
Expand Down Expand Up @@ -227,10 +229,11 @@ void builderBuildAddBean(Append writer) {
if (notEmpty(initMethod)) {
writer.append(indent).append("builder.addPostConstruct($bean::%s);", initMethod).eol();
}
var priority = destroyPriority == null || destroyPriority == 1000 ? "" : ", " + destroyPriority;
if (notEmpty(destroyMethod)) {
writer.append(indent).append("builder.addPreDestroy($bean::%s);", destroyMethod).eol();
writer.append(indent).append("builder.addPreDestroy($bean::%s%s);", destroyMethod, priority).eol();
} else if (typeReader != null && typeReader.isClosable()) {
writer.append(indent).append("builder.addPreDestroy($bean::close);").eol();
writer.append(indent).append("builder.addPreDestroy($bean::close%s);", priority).eol();
} else if (beanCloseable) {
writer.append(indent).append("builder.addAutoClosable($bean);").eol();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ final class TypeExtendsInjection {
private final List<AspectPair> typeAspects;
private Element postConstructMethod;
private Element preDestroyMethod;
private Integer preDestroyPriority;

TypeExtendsInjection(TypeElement baseType, boolean factory, ImportTypeMap importTypes) {
this.importTypes = importTypes;
Expand Down Expand Up @@ -124,6 +125,7 @@ private void readMethod(Element element, TypeElement type) {
if (AnnotationUtil.hasAnnotationWithName(element, "PreDestroy")) {
preDestroyMethod = element;
checkAspect = false;
PreDestroyPrism.getOptionalOn(element).ifPresent(preDestroy -> preDestroyPriority = preDestroy.priority());
}
if (checkAspect) {
checkForAspect(methodElement);
Expand Down Expand Up @@ -202,6 +204,10 @@ Element preDestroyMethod() {
return preDestroyMethod;
}

Integer preDestroyPriority() {
return preDestroyPriority;
}

MethodReader constructor() {
if (injectConstructor != null) {
return injectConstructor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ Element preDestroyMethod() {
return extendsInjection.preDestroyMethod();
}

Integer preDestroyPriority() {
return extendsInjection.preDestroyPriority();
}

MethodReader constructor() {
return extendsInjection.constructor();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Element preDestroyMethod() {
return extendsReader.preDestroyMethod();
}

Integer preDestroyPriority() {
return extendsReader.preDestroyPriority();
}

MethodReader constructor() {
return extendsReader.constructor();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@GeneratePrism(Primary.class)
@GeneratePrism(Secondary.class)
@GeneratePrism(Proxy.class)
@GeneratePrism(PreDestroy.class)
@GeneratePrism(DependencyMeta.class)
@GeneratePrism(Bean.class)
@GeneratePrism(QualifiedMap.class)
Expand Down
9 changes: 9 additions & 0 deletions inject/src/main/java/io/avaje/inject/Bean.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@
*/
String destroyMethod() default "";

/**
* Specify the priority of the destroy method to control its execution
* order relative to other destroy methods.
* <p>
* Low values execute earlier than high values. All destroy methods without
* any explicit priority are given a value of 1000.
*/
int destroyPriority() default 0;

/**
* Specify that the concrete instance of the bean is an AutoCloseable. Use if your bean interface
* doesn't extend AutoCloseable but the concrete class implements it.
Expand Down
11 changes: 11 additions & 0 deletions inject/src/main/java/io/avaje/inject/BeanScopeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,17 @@ default <D> BeanScopeBuilder provideDefault(Type type, Supplier<D> provider) {
*/
BeanScopeBuilder addPreDestroy(AutoCloseable preDestroyHook);

/**
* Add hook with a priority that will execute before this scope is destroyed.
* <p>
* Specify the priority of the destroy method to control its execution
* order relative to other destroy methods.
* <p>
* Low values for priority execute earlier than high values. All destroy methods
* without any explicit priority are given a value of 1000.
*/
BeanScopeBuilder addPreDestroy(AutoCloseable preDestroyHook, int priority);

/**
* Set the ClassLoader to use when loading modules.
*
Expand Down
13 changes: 10 additions & 3 deletions inject/src/main/java/io/avaje/inject/DBeanScopeBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ final class DBeanScopeBuilder implements BeanScopeBuilder.ForTesting {
private final Set<Module> includeModules = new LinkedHashSet<>();
private final List<Runnable> postConstructList = new ArrayList<>();
private final List<Consumer<BeanScope>> postConstructConsumerList = new ArrayList<>();
private final List<AutoCloseable> preDestroyList = new ArrayList<>();
private final List<ClosePair> preDestroyList = new ArrayList<>();
private BeanScope parent;
private boolean parentOverride = true;
private boolean shutdownHook;
Expand Down Expand Up @@ -128,7 +128,12 @@ public BeanScopeBuilder addPostConstruct(Consumer<BeanScope> postConstructConsum

@Override
public BeanScopeBuilder addPreDestroy(AutoCloseable preDestroyHook) {
preDestroyList.add(preDestroyHook);
return addPreDestroy(preDestroyHook, 1000);
}

@Override
public BeanScopeBuilder addPreDestroy(AutoCloseable preDestroyHook, int priority) {
preDestroyList.add(new ClosePair(priority, preDestroyHook));
return this;
}

Expand Down Expand Up @@ -264,7 +269,9 @@ public BeanScope build() {

postConstructList.forEach(builder::addPostConstruct);
postConstructConsumerList.forEach(builder::addPostConstruct);
preDestroyList.forEach(builder::addPreDestroy);
for (ClosePair closePair : preDestroyList) {
builder.addPreDestroy(closePair.closeable(), closePair.priority());
}
return builder.build(shutdownHook, start);
}

Expand Down
9 changes: 9 additions & 0 deletions inject/src/main/java/io/avaje/inject/PreDestroy.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,13 @@
@Retention(RUNTIME)
@Target(METHOD)
public @interface PreDestroy {

/**
* Specify the priority of the destroy method to control its execution
* order relative to other destroy methods.
* <p>
* Low values execute earlier than high values. All destroy methods without
* any explicit priority are given a value of 1000.
*/
int priority() default 1000;
}
5 changes: 5 additions & 0 deletions inject/src/main/java/io/avaje/inject/spi/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ static Builder newBuilder(Set<String> profiles, PropertyRequiresPlugin plugin, L
*/
void addPreDestroy(AutoCloseable closeable);

/**
* Add lifecycle PreDestroy method with a given priority.
*/
void addPreDestroy(AutoCloseable closeable, int priority);

/**
* Check if the instance is AutoCloseable and if so register it with PreDestroy.
*
Expand Down
25 changes: 25 additions & 0 deletions inject/src/main/java/io/avaje/inject/spi/ClosePair.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.avaje.inject.spi;

public final class ClosePair implements Comparable<ClosePair> {

private final int priority;
private final AutoCloseable closeable;

public ClosePair(int priority, AutoCloseable closeable) {
this.priority = priority;
this.closeable = closeable;
}

public int priority() {
return priority;
}

public AutoCloseable closeable() {
return closeable;
}

@Override
public int compareTo(ClosePair o) {
return Integer.compare(priority, o.priority);
}
}
Loading

0 comments on commit bb7d57a

Please sign in to comment.