MMKB-5027: Adapts magnolia update mechanism
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
package at.ucs.magnolia.updates;
|
||||
|
||||
import info.magnolia.module.delta.Task;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IntranetUpdateModuleConfig {
|
||||
List<Task> getInitialUpdateTasks();
|
||||
String getYamlUpdateDir();
|
||||
String getUpdateTaskPackage();
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package at.ucs.magnolia.updates;
|
||||
|
||||
import at.ucs.magnolia.updates.util.TaskWrapper;
|
||||
import at.ucs.magnolia.updates.util.VersionComparator;
|
||||
import at.ucs.magnolia.updates.util.VersionUtil;
|
||||
import info.magnolia.module.DefaultModuleVersionHandler;
|
||||
import info.magnolia.module.InstallContext;
|
||||
import info.magnolia.module.delta.Delta;
|
||||
import info.magnolia.module.delta.DeltaBuilder;
|
||||
import info.magnolia.module.delta.Task;
|
||||
import info.magnolia.module.model.Version;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.reflections.Reflections;
|
||||
import org.reflections.scanners.SubTypesScanner;
|
||||
|
||||
import javax.jcr.RepositoryException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
@Slf4j
|
||||
public abstract class IntranetUpdateModuleVersionHandler extends DefaultModuleVersionHandler {
|
||||
|
||||
protected abstract IntranetUpdateModuleConfig getModuleConfig();
|
||||
|
||||
@Override
|
||||
protected List<Task> getExtraInstallTasks(InstallContext installContext) {
|
||||
return getModuleConfig().getInitialUpdateTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Delta> getDeltas(InstallContext installContext, Version from) {
|
||||
List<Delta> deltaList = new ArrayList<>();
|
||||
|
||||
if (from == null) {
|
||||
deltaList.add(getInstall(installContext));
|
||||
}
|
||||
|
||||
deltaList.addAll(getUpdateDeltas(installContext, from));
|
||||
return deltaList;
|
||||
}
|
||||
|
||||
protected List<Delta> getUpdateDeltas(InstallContext installContext, Version from) {
|
||||
try {
|
||||
return new Reflections(
|
||||
getModuleConfig().getUpdateTaskPackage(),
|
||||
new SubTypesScanner()
|
||||
).getSubTypesOf(ModuleUpdate.class).stream()
|
||||
.map(this::initiateModuleUpdate)
|
||||
.filter(Objects::nonNull)
|
||||
.sorted(new VersionComparator())
|
||||
.map(updateModule -> buildDelta(installContext, updateModule))
|
||||
.toList();
|
||||
} catch (Exception e) {
|
||||
log.error("Could not register update tasks", e);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private ModuleUpdate initiateModuleUpdate(Class<? extends ModuleUpdate> moduleUpdateClass) {
|
||||
try {
|
||||
ModuleUpdate moduleUpdate = moduleUpdateClass.getConstructor().newInstance();
|
||||
moduleUpdate.setYamlPath(getModuleConfig().getYamlUpdateDir());
|
||||
return moduleUpdate;
|
||||
} catch (RuntimeException | NoSuchMethodException | InstantiationException |
|
||||
IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
log.error(e.getLocalizedMessage(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Predicate<TaskWrapper> installNecessary(InstallContext installContext) {
|
||||
return (wrapper) -> {
|
||||
try {
|
||||
return !VersionUtil.containsVersion(installContext, wrapper.getVersion());
|
||||
} catch (RepositoryException e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Delta buildDelta(InstallContext installContext, ModuleUpdate moduleUpdate) {
|
||||
final String version = moduleUpdate.getVersion();
|
||||
List<Task> wrappedTasks = IntStream.range(0, moduleUpdate.getAllTasks().size())
|
||||
.mapToObj(i -> {
|
||||
final String wrappedVersion = MessageFormat.format("{0}_{1}", version, i);
|
||||
final Task task = moduleUpdate.getAllTasks().get(i);
|
||||
return new TaskWrapper(task, wrappedVersion);
|
||||
})
|
||||
.filter(installNecessary(installContext))
|
||||
.map(Task.class::cast)
|
||||
.toList();
|
||||
// Fake sem version
|
||||
// We don't actually need it
|
||||
return DeltaBuilder.update(Version.parseVersion(0, 4, 2), "")
|
||||
.addTasks(wrappedTasks);
|
||||
}
|
||||
}
|
||||
42
src/main/java/at/ucs/magnolia/updates/ModuleUpdate.java
Normal file
42
src/main/java/at/ucs/magnolia/updates/ModuleUpdate.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package at.ucs.magnolia.updates;
|
||||
|
||||
import at.ucs.magnolia.updates.util.BootstrapUpdateYamlsWithProperties;
|
||||
import at.ucs.magnolia.updates.util.LoggingTask;
|
||||
import info.magnolia.module.delta.Task;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@RequiredArgsConstructor
|
||||
public abstract class ModuleUpdate {
|
||||
|
||||
private final String version;
|
||||
private String yamlPath;
|
||||
|
||||
public List<Task> getUpdateTasks() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<Task> getUpdateTasksAfterBootstrap() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public List<Task> getAllTasks() {
|
||||
List<Task> tasks = new ArrayList<>();
|
||||
tasks.add(new LoggingTask("updating to version: " + version, ""));
|
||||
tasks.addAll(getUpdateTasks());
|
||||
tasks.add(new LoggingTask("Importing bootsrap Yamls for: " + version, ""));
|
||||
tasks.add(new BootstrapUpdateYamlsWithProperties(
|
||||
"Import bootstrap YAML folder",
|
||||
MessageFormat.format("Imports bootstrap YAML files from {0}{1}", yamlPath, version), version, yamlPath));
|
||||
tasks.addAll(getUpdateTasksAfterBootstrap());
|
||||
return tasks;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package at.ucs.magnolia.updates.util;
|
||||
|
||||
import info.magnolia.cms.util.ClasspathResourcesUtil;
|
||||
import info.magnolia.module.InstallContext;
|
||||
import info.magnolia.module.delta.BootstrapResourcesTask;
|
||||
|
||||
import javax.jcr.RepositoryException;
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This task bootstraps all yamls of update-yamls in a specific version and replaces magnolia property placeholder.
|
||||
*/
|
||||
public class BootstrapUpdateYamlsWithProperties extends BootstrapResourcesTask {
|
||||
private final String version;
|
||||
private final String yamlPath;
|
||||
|
||||
public BootstrapUpdateYamlsWithProperties(String name, String description, String version, String yamlPath) {
|
||||
super(name, description);
|
||||
this.version = version;
|
||||
this.yamlPath = yamlPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bootstrap(InstallContext installContext, int importUUIDBehavior) throws IOException, RepositoryException {
|
||||
// In the original Task super.bootstrap is called, but since we want to call our own BootstrapUtil
|
||||
// we have to use code called in the super class explicitly
|
||||
String[] resourcesToBootstrap = ClasspathResourcesUtil.findResources(name -> acceptResource(installContext, name));
|
||||
resourcesToBootstrap = filterResourcesToBootstrap(resourcesToBootstrap);
|
||||
UcsBootstrapUtil.bootstrap(resourcesToBootstrap, importUUIDBehavior);
|
||||
}
|
||||
|
||||
private String[] filterResourcesToBootstrap(String[] resourcesToBootstrap) {
|
||||
return Arrays.stream(resourcesToBootstrap)
|
||||
.filter(s -> s.startsWith(MessageFormat.format("/{0}/{1}/", yamlPath, version)))
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
20
src/main/java/at/ucs/magnolia/updates/util/LoggingTask.java
Normal file
20
src/main/java/at/ucs/magnolia/updates/util/LoggingTask.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package at.ucs.magnolia.updates.util;
|
||||
|
||||
import info.magnolia.module.InstallContext;
|
||||
import info.magnolia.module.delta.AbstractTask;
|
||||
import info.magnolia.module.delta.TaskExecutionException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LoggingTask extends AbstractTask {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LoggingTask.class.getName());
|
||||
public LoggingTask(String taskName, String taskDescription) {
|
||||
super(taskName, taskDescription);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(InstallContext installContext) throws TaskExecutionException {
|
||||
log.info(getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package at.ucs.magnolia.updates.util;
|
||||
|
||||
import info.magnolia.init.PropertySource;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.text.StringSubstitutor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.apache.commons.text.StringSubstitutor.*;
|
||||
|
||||
public class MagnoliaPropertyResolver {
|
||||
private MagnoliaPropertyResolver() {
|
||||
}
|
||||
|
||||
public static InputStream resolve(PropertySource properties, InputStream in) {
|
||||
try {
|
||||
return IOUtils.toInputStream(replaceMagnoliaPlaceholders(properties, in), UTF_8);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String replaceMagnoliaPlaceholders(PropertySource properties, InputStream in) throws IOException {
|
||||
StringSubstitutor substitutor = new StringSubstitutor(properties::getProperty, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
|
||||
return substitutor.replace(IOUtils.toString(in, UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
39
src/main/java/at/ucs/magnolia/updates/util/TaskWrapper.java
Normal file
39
src/main/java/at/ucs/magnolia/updates/util/TaskWrapper.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package at.ucs.magnolia.updates.util;
|
||||
|
||||
import info.magnolia.module.InstallContext;
|
||||
import info.magnolia.module.InstallStatus;
|
||||
import info.magnolia.module.delta.AbstractTask;
|
||||
import info.magnolia.module.delta.Task;
|
||||
import info.magnolia.module.delta.TaskExecutionException;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.jcr.Node;
|
||||
import javax.jcr.RepositoryException;
|
||||
|
||||
@Slf4j
|
||||
public class TaskWrapper extends AbstractTask {
|
||||
private final Task task;
|
||||
|
||||
@Getter
|
||||
private final String version;
|
||||
|
||||
public TaskWrapper(Task task, String version) {
|
||||
super(task.getName(), task.getDescription());
|
||||
this.task = task;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(InstallContext installContext) throws TaskExecutionException {
|
||||
task.execute(installContext);
|
||||
|
||||
if (installContext.getStatus() != InstallStatus.installFailed) {
|
||||
try {
|
||||
VersionUtil.addVersion(installContext, getVersion(), task.getName());
|
||||
} catch (RepositoryException e) {
|
||||
log.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package at.ucs.magnolia.updates.util;
|
||||
|
||||
import info.magnolia.cms.util.StringLengthComparator;
|
||||
import info.magnolia.importexport.BootstrapUtil;
|
||||
import info.magnolia.init.MagnoliaConfigurationProperties;
|
||||
import info.magnolia.objectfactory.Components;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.jcr.Node;
|
||||
import javax.jcr.RepositoryException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* This class is exactly working like the original BootstrapUtil class of Magnolia,
|
||||
* with the only difference, that it substitutes Magnolia Property placeholders
|
||||
* with their actual value, since this is not implemented for bootstrap yamls by Magnolia
|
||||
*/
|
||||
public class UcsBootstrapUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(BootstrapUtil.class);
|
||||
|
||||
public static void bootstrap(String[] resourceNames, int importUUIDBehavior) throws IOException, RepositoryException {
|
||||
// sort by length --> import parent node firstsubPath
|
||||
List<String> list = new ArrayList<>(Arrays.asList(resourceNames));
|
||||
if (list.contains(null)) {
|
||||
throw new IllegalArgumentException("Resource names contain a <null> entry that cannot be processed.");
|
||||
}
|
||||
|
||||
Collections.sort(list, new StringLengthComparator());
|
||||
|
||||
for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
|
||||
bootstrap(iter.next(), null, importUUIDBehavior);
|
||||
}
|
||||
}
|
||||
|
||||
public static void bootstrap(String resourceName, String subPath, int importUUIDBehavior) throws IOException, RepositoryException {
|
||||
final InputStream stream = BootstrapUtil.class.getResourceAsStream(resourceName);
|
||||
if (stream == null) {
|
||||
throw new IOException("Can't find resource to bootstrap at " + resourceName);
|
||||
}
|
||||
|
||||
MagnoliaConfigurationProperties magnoliaConfiguration = Components.getComponent(MagnoliaConfigurationProperties.class);
|
||||
InputStream resolvedStream = MagnoliaPropertyResolver.resolve(magnoliaConfiguration, stream);
|
||||
// Verify if the node already exists and execute jcr import command
|
||||
bootstrap(resourceName, subPath, resolvedStream, importUUIDBehavior);
|
||||
}
|
||||
|
||||
public static void bootstrap(String resourceName, String subPath, InputStream stream, int importUUIDBehavior) throws RepositoryException {
|
||||
BootstrapUtil.bootstrap(resourceName, subPath, stream, importUUIDBehavior);
|
||||
}
|
||||
|
||||
public static void export(Node content, File directory) throws IOException, RepositoryException {
|
||||
BootstrapUtil.export(content, directory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package at.ucs.magnolia.updates.util;
|
||||
|
||||
import at.ucs.magnolia.updates.ModuleUpdate;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Comparator;
|
||||
|
||||
@Slf4j
|
||||
public class VersionComparator implements Comparator<ModuleUpdate> {
|
||||
|
||||
@Override
|
||||
public int compare(ModuleUpdate o1, ModuleUpdate o2) {
|
||||
var o1Version = o1.getVersion();
|
||||
var o2Version = o2.getVersion();
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("ddMMyyyyHHmm");
|
||||
LocalDateTime first = LocalDateTime.from(dateTimeFormatter.parse(o1Version.substring(0, 12)));
|
||||
LocalDateTime second = LocalDateTime.from(dateTimeFormatter.parse(o2Version.substring(0, 12)));
|
||||
return first.compareTo(second);
|
||||
}
|
||||
|
||||
}
|
||||
41
src/main/java/at/ucs/magnolia/updates/util/VersionUtil.java
Normal file
41
src/main/java/at/ucs/magnolia/updates/util/VersionUtil.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package at.ucs.magnolia.updates.util;
|
||||
|
||||
import info.magnolia.jcr.util.NodeTypes;
|
||||
import info.magnolia.jcr.util.NodeUtil;
|
||||
import info.magnolia.module.InstallContext;
|
||||
|
||||
import javax.jcr.Node;
|
||||
import javax.jcr.RepositoryException;
|
||||
import javax.jcr.Session;
|
||||
|
||||
public class VersionUtil {
|
||||
|
||||
private static Node getVersionsNode(InstallContext installContext) throws RepositoryException {
|
||||
// make sure we have the /modules node
|
||||
if (!installContext.hasModulesNode()) {
|
||||
final Session session = installContext.getConfigJCRSession();
|
||||
session.getRootNode().addNode("modules", NodeTypes.Content.NAME);
|
||||
}
|
||||
|
||||
final Node moduleNode = installContext.getOrCreateCurrentModuleNode();
|
||||
|
||||
if (!moduleNode.hasNode("config/versions")) {
|
||||
NodeUtil.createPath(moduleNode, "config/versions", "mgnl:content");
|
||||
}
|
||||
|
||||
return moduleNode.getNode("config/versions");
|
||||
}
|
||||
|
||||
public static boolean containsVersion(InstallContext installContext, String version) throws RepositoryException {
|
||||
Node versionNode = getVersionsNode(installContext);
|
||||
return versionNode != null && versionNode.hasProperty(version);
|
||||
}
|
||||
|
||||
public static void addVersion(InstallContext installContext, String version, String name) throws RepositoryException {
|
||||
Node versionsNode = getVersionsNode(installContext);
|
||||
if (versionsNode != null) {
|
||||
versionsNode.setProperty(version, name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user