119 lines
4.8 KiB
Java
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);
|
|
}
|
|
}
|
|
|
|
}
|