diff --git a/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java b/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java index a6d68526c..f6313d983 100644 --- a/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java +++ b/application/src/main/java/run/halo/app/extension/controller/DefaultControllerManager.java @@ -11,11 +11,11 @@ import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.controller.Reconciler.Request; -import run.halo.app.infra.SchemeInitializedEvent; +import run.halo.app.infra.ExtensionInitializedEvent; @Slf4j public class DefaultControllerManager - implements ApplicationListener, + implements ApplicationListener, ApplicationContextAware, DisposableBean, ControllerManager { private final ExtensionClient client; @@ -68,7 +68,7 @@ public class DefaultControllerManager } @Override - public void onApplicationEvent(SchemeInitializedEvent event) { + public void onApplicationEvent(ExtensionInitializedEvent event) { // register reconcilers in system after scheme initialized applicationContext.>getBeanProvider( forClassWithGenerics(Reconciler.class, Request.class)) diff --git a/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java b/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java index a20d82b93..6b32f5add 100644 --- a/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java +++ b/application/src/main/java/run/halo/app/extension/gc/GcControllerInitializer.java @@ -4,11 +4,11 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; import run.halo.app.extension.controller.Controller; -import run.halo.app.infra.SchemeInitializedEvent; +import run.halo.app.infra.ExtensionInitializedEvent; @Component public class GcControllerInitializer - implements ApplicationListener, DisposableBean { + implements ApplicationListener, DisposableBean { private final Controller gcController; @@ -17,7 +17,7 @@ public class GcControllerInitializer } @Override - public void onApplicationEvent(SchemeInitializedEvent event) { + public void onApplicationEvent(ExtensionInitializedEvent event) { gcController.start(); } diff --git a/application/src/main/java/run/halo/app/infra/ExtensionInitializedEvent.java b/application/src/main/java/run/halo/app/infra/ExtensionInitializedEvent.java new file mode 100644 index 000000000..428b1a86f --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/ExtensionInitializedEvent.java @@ -0,0 +1,16 @@ +package run.halo.app.infra; + +import org.springframework.context.ApplicationEvent; + +/** + * ExtensionInitializedEvent is fired after extensions have been initialized completely. + * + * @author johnniang + */ +public class ExtensionInitializedEvent extends ApplicationEvent { + + public ExtensionInitializedEvent(Object source) { + super(source); + } + +} diff --git a/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java b/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java index 03b5bfcbd..d26120d1d 100644 --- a/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java +++ b/application/src/main/java/run/halo/app/infra/ExtensionResourceInitializer.java @@ -1,21 +1,18 @@ package run.halo.app.infra; import java.io.IOException; -import java.time.Duration; import java.util.HashSet; import java.util.List; import java.util.Set; import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.util.retry.Retry; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Unstructured; import run.halo.app.infra.properties.HaloProperties; @@ -38,14 +35,18 @@ public class ExtensionResourceInitializer { private final HaloProperties haloProperties; private final ReactiveExtensionClient extensionClient; + private final ApplicationEventPublisher eventPublisher; + public ExtensionResourceInitializer(HaloProperties haloProperties, - ReactiveExtensionClient extensionClient) { + ReactiveExtensionClient extensionClient, + ApplicationEventPublisher eventPublisher) { this.haloProperties = haloProperties; this.extensionClient = extensionClient; + this.eventPublisher = eventPublisher; } - @EventListener(ApplicationReadyEvent.class) - public Mono initialize(ApplicationReadyEvent readyEvent) { + @EventListener(SchemeInitializedEvent.class) + public Mono initialize(SchemeInitializedEvent initializedEvent) { var locations = new HashSet(); if (!haloProperties.isRequiredExtensionDisabled()) { locations.addAll(REQUIRED_EXTENSION_LOCATIONS); @@ -80,7 +81,8 @@ public class ExtensionResourceInitializer { extension.getMetadata().getName()); } }) - .then(); + .then(Mono.fromRunnable( + () -> eventPublisher.publishEvent(new ExtensionInitializedEvent(this)))); } private Mono createOrUpdate(Unstructured extension) { @@ -88,17 +90,11 @@ public class ExtensionResourceInitializer { .flatMap(ext -> extensionClient.fetch(extension.groupVersionKind(), extension.getMetadata().getName())) .flatMap(existingExt -> { + // force update extension.getMetadata().setVersion(existingExt.getMetadata().getVersion()); return extensionClient.update(extension); }) - .switchIfEmpty(Mono.defer(() -> extensionClient.create(extension))) - .retryWhen(Retry.fixedDelay(3, Duration.ofMillis(100)) - .filter(t -> t instanceof OptimisticLockingFailureException)) - .onErrorContinue(OptimisticLockingFailureException.class, (throwable, o) -> { - log.warn("Failed to create or update extension resource: {}/{} due to modification " - + "conflict", - extension.groupVersionKind(), extension.getMetadata().getName()); - }); + .switchIfEmpty(Mono.defer(() -> extensionClient.create(extension))); } private List listResources(String location) { diff --git a/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java b/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java index ec95ac004..4d80e8813 100644 --- a/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java +++ b/application/src/test/java/run/halo/app/infra/ExtensionResourceInitializerTest.java @@ -19,10 +19,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.skyscreamer.jsonassert.JSONAssert; -import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.util.FileSystemUtils; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -42,21 +43,22 @@ import run.halo.app.infra.utils.JsonUtils; class ExtensionResourceInitializerTest { @Mock - private ReactiveExtensionClient extensionClient; + ReactiveExtensionClient extensionClient; @Mock - private HaloProperties haloProperties; + HaloProperties haloProperties; @Mock - private ApplicationReadyEvent applicationReadyEvent; + SchemeInitializedEvent applicationReadyEvent; - private ExtensionResourceInitializer extensionResourceInitializer; + @Mock + ApplicationEventPublisher eventPublisher; + + @InjectMocks + ExtensionResourceInitializer extensionResourceInitializer; List dirsToClean; @BeforeEach void setUp() throws IOException { - extensionResourceInitializer = - new ExtensionResourceInitializer(haloProperties, extensionClient); - dirsToClean = new ArrayList<>(2); Path tempDirectory = Files.createTempDirectory("extension-resource-initializer-test");