Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PreDestroy priority to control ordering #405

Merged
merged 1 commit into from
Sep 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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