halo/application/src/main/java/run/halo/app/infra/ExtensionResourceInitialize...

119 lines
4.8 KiB
Java

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.ApplicationStartedEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationListener;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import run.halo.app.extension.ExtensionUtil;
import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.extension.Unstructured;
import run.halo.app.infra.properties.HaloProperties;
import run.halo.app.infra.utils.YamlUnstructuredLoader;
/**
* <p>Extension resources initializer.</p>
* <p>Check whether {@link HaloProperties#getInitialExtensionLocations()} is configured
* When the system ready, and load resources according to it to creates {@link Unstructured}</p>
*
* @author guqing
* @since 2.0.0
*/
@Slf4j
@Component
public class ExtensionResourceInitializer implements ApplicationListener<ApplicationStartedEvent> {
public static final Set<String> REQUIRED_EXTENSION_LOCATIONS =
Set.of("classpath:/extensions/*.yaml", "classpath:/extensions/*.yml");
private final HaloProperties haloProperties;
private final ReactiveExtensionClient extensionClient;
private final ApplicationEventPublisher eventPublisher;
public ExtensionResourceInitializer(HaloProperties haloProperties,
ReactiveExtensionClient extensionClient,
ApplicationEventPublisher eventPublisher) {
this.haloProperties = haloProperties;
this.extensionClient = extensionClient;
this.eventPublisher = eventPublisher;
}
public void onApplicationEvent(ApplicationStartedEvent initializedEvent) {
var locations = new HashSet<String>();
if (!haloProperties.isRequiredExtensionDisabled()) {
locations.addAll(REQUIRED_EXTENSION_LOCATIONS);
}
if (haloProperties.getInitialExtensionLocations() != null) {
locations.addAll(haloProperties.getInitialExtensionLocations());
}
if (CollectionUtils.isEmpty(locations)) {
return;
}
Flux.fromIterable(locations)
.doOnNext(location ->
log.debug("Trying to initialize extension resources from location: {}", location))
.map(this::listResources)
.distinct()
.flatMapIterable(resources -> resources)
.doOnNext(resource -> log.debug("Initializing extension resource from location: {}",
resource))
.map(resource -> new YamlUnstructuredLoader(resource).load())
.flatMapIterable(extensions -> extensions)
.doOnNext(extension -> {
if (log.isDebugEnabled()) {
log.debug("Initializing extension resource: {}/{}",
extension.groupVersionKind(), extension.getMetadata().getName());
}
})
.flatMap(this::createOrUpdate)
.doOnNext(extension -> {
if (log.isDebugEnabled()) {
log.debug("Initialized extension resource: {}/{}", extension.groupVersionKind(),
extension.getMetadata().getName());
}
})
.then(Mono.fromRunnable(
() -> eventPublisher.publishEvent(new ExtensionInitializedEvent(this))))
.block(Duration.ofMinutes(1));
}
private Mono<Unstructured> createOrUpdate(Unstructured extension) {
return Mono.just(extension)
.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(() -> {
if (ExtensionUtil.isDeleted(extension)) {
// skip deleted extension
return Mono.empty();
}
return extensionClient.create(extension);
}));
}
private List<Resource> listResources(String location) {
var resolver = new PathMatchingResourcePatternResolver();
try {
return List.of(resolver.getResources(location));
} catch (IOException ie) {
throw new IllegalArgumentException("Invalid extension location: " + location, ie);
}
}
}