Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.openl.rules.webstudio.Migrator Maven / Gradle / Ivy
package org.openl.rules.webstudio;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.openl.rules.dataformat.yaml.YamlMapperFactory;
import org.openl.rules.repository.RepositoryInstatiator;
import org.openl.rules.repository.git.branch.BranchesData;
import org.openl.rules.webstudio.web.Props;
import org.openl.rules.webstudio.web.admin.AdministrationSettings;
import org.openl.rules.webstudio.web.install.KeyPairCertUtils;
import org.openl.rules.workspace.dtr.impl.ProjectIndex;
import org.openl.rules.workspace.dtr.impl.ProjectInfo;
import org.openl.spring.env.DynamicPropertySource;
import org.openl.util.PropertiesUtils;
import org.openl.util.StringUtils;
/**
* For setting migration purposes. It cleans up default settings and reconfigure user defined properties.
*
* @author Yury Molchan
*/
public class Migrator {
private Migrator() {
}
private static final Logger LOG = LoggerFactory.getLogger(Migrator.class);
public static void migrate() {
DynamicPropertySource settings = DynamicPropertySource.get();
HashMap props = new HashMap<>();
String fromVersion = settings.version();
String stringFromVersion = fromVersion == null ? "5.23.1" : fromVersion;
// add subsequent migrations in order of priority
if (stringFromVersion.compareTo("5.24.0") < 0) {
migrateTo5_24(settings, props);
}
if (stringFromVersion.compareTo("5.26.0") < 0) {
migrateTo5_26_0(settings, props);
}
if (stringFromVersion.compareTo("5.26.1") < 0) {
migrateTo5_26_1(settings, props);
}
if ("saml".equals(Props.text("user.mode"))) {
// Generating required a private key and its certificate if they are missed
// Due they should be unique and private per installation they cannot be defined in openl-default.properties
// So it should be executed always there on startup
// Introduced in 5.26
if (Props.text("security.saml.local-key") == null || Props.text("security.saml.local-certificate") == null) {
Pair pair = KeyPairCertUtils.generateCertificate();
if (pair != null) {
props.put("security.saml.local-key", pair.getKey());
props.put("security.saml.local-certificate", pair.getValue());
}
}
}
try {
settings.save(props);
settings.reloadIfModified();
} catch (IOException e) {
LOG.error("Migration of properties failed.", e);
}
}
private static void migrateTo5_26_1(DynamicPropertySource settings, HashMap props) {
migrateRepositoryFactories(settings, props);
migrateProductionRepository(settings, props);
}
private static void migrateRepositoryFactories(DynamicPropertySource settings, HashMap props) {
String factorySuffix = ".factory";
Arrays.stream(settings.getPropertyNames())
.filter(propertyName -> propertyName.startsWith("repository.") && propertyName.endsWith(factorySuffix))
.forEach(factoryKey -> {
var factory = settings.getProperty(factoryKey);
if (StringUtils.isNotBlank(factory)) {
String refKey = factoryKey.substring(0, factoryKey.length() - factorySuffix.length()) + ".$ref";
props.put(refKey, RepositoryInstatiator.getRefID(factory));
props.put(factoryKey, null);
}
});
}
private static void migrateProductionRepository(DynamicPropertySource settings, HashMap props) {
// Production repository was mandatory in previous versions. In a new version defaults for it were removed.
final String configListProp = "production-repository-configs";
// Absent production repository configs assumes default setting: production-repository-configs = production
var configList = settings.getProperty(configListProp);
// Another case: production-repository-configs = production, production1, production2
List repositories = Optional.ofNullable(configList).map(s -> Arrays
.asList(StringUtils.split(s, ','))).orElse(Collections.emptyList());
boolean severalReposIncludingProduction = repositories.size() > 1 && repositories
.contains("production");
// Default Repository URI and Factory in the previous 5.26.0 version
final var defaultUri = "jdbc:h2:mem:repo;DB_CLOSE_DELAY=-1";
final var defaultFactory = "repo-jdbc";
// Check, if URI for repository with id "production" was changed
String repoUriProp = "repository.production.uri";
var uri = settings.getProperty(repoUriProp);
var factory = settings.getProperty("repository.production.factory");
var repoIsChanged = uri != null && !uri.equals(defaultUri) || factory != null && !factory.equals(defaultFactory);
// 1) If had only defaulted repository and its uri was not changed in configuration, we assume, it
// wasn't used and can be removed in the latest OpenL Studio. Don't restore any defaults.
// 2) If default repository was reconfigured (URI or factory were changed), then it was used,
// we need to restore only absent defaults for repository with id "production".
// 3) If several repositories existed but default repository with id "production" wasn't changed (including
// URI), we restore all its defaults including URI.
if (repoIsChanged || severalReposIncludingProduction) {
if (configList == null) {
// Restore default repository id
props.put(configListProp, "production");
}
final String repoNameProp = "repository.production.name";
if (!settings.containsProperty(repoNameProp)) {
// Restore default repository name
props.put(repoNameProp, "Deployment");
}
// Replace repository factory with repository ref.
if (StringUtils.isBlank(factory)) {
props.put("repository.production.$ref", defaultFactory);
}
// base.path is a mandatory setting for now, need to restore default value.
props.put("repository.production.base.path.$ref", "repo-default.production.base.path");
if (severalReposIncludingProduction && !repoIsChanged) {
// Restore property as it was in previous OpenL Studio.
props.put(repoUriProp, defaultUri);
}
}
}
// 5.26.0
private static void migrateTo5_26_0(DynamicPropertySource settings, HashMap props) {
Arrays.stream(settings.getPropertyNames())
.filter(propertyName -> propertyName.startsWith("repository.") && propertyName.endsWith(".comment-template"))
.forEach(propertyName -> {
var commentTemplate = settings.getProperty(propertyName);
if (commentTemplate != null && commentTemplate.contains("{username}")) {
props.put(propertyName, commentTemplate.replaceAll("(\\s+Author\\s*:?)?\\s*\\{username}\\.?", ""));
}
});
//removing unnecessary SAML properties
props.put("security.saml.app-url", null);
props.put("security.saml.authentication-contexts", null);
props.put("security.saml.local-logout", null);
props.put("security.saml.is-app-after-balancer", null);
props.put("security.saml.scheme", null);
props.put("security.saml.server-name", null);
props.put("security.saml.server-port", null);
props.put("security.saml.include-server-port-in-request-url", null);
props.put("security.saml.context-path", null);
props.put("security.saml.max-authentication-age", null);
props.put("security.saml.metadata-trust-check", null);
props.put("security.saml.request-timeout", null);
props.put("security.saml.keystore-file-path", null);
props.put("security.saml.keystore-password", null);
props.put("security.saml.keystore-sp-alias", null);
props.put("security.saml.keystore-sp-password", null);
Arrays.stream(settings.getPropertyNames())
.filter(propertyName -> propertyName.endsWith(".uri") || propertyName.endsWith(".url"))
.map(settings::getProperty)
.distinct()
.forEach(uri -> {
if (uri != null && uri.startsWith("jdbc:h2:")) {
LOG.warn(
"You have h2 database with uri '{}'. Make sure that it's migrated to v2 or newer version. You need to migrate it yourself. See https://www.h2database.com/html/migration-to-v2.html for details.",
uri);
}
});
}
// 5.24
private static void migrateTo5_24(DynamicPropertySource settings, HashMap props) {
migratePropsTo5_24(settings, props);
// migrate branches and project properties to branches.yaml if repoType is Git
var designRepo = settings.getProperty("repository.design.local-repository-path");
var designRepoPath = designRepo != null ? designRepo : Props.text("openl.home") + "/design-repository";
Map nonFlatProjectPaths = loadProjectsPathes(designRepoPath);
writeProjectPathesToYAML(nonFlatProjectPaths);
migrateBranchesProps(nonFlatProjectPaths);
// migrate NonFlat project settings
migrateNonFlatProjectSettings(nonFlatProjectPaths);
// migrate locks.
migrateLocks(nonFlatProjectPaths);
}
private static Map loadProjectsPathes(String designRepo) {
Map projectPathMap = new HashMap<>();
Path projectProperties = Paths.get(designRepo, "openl-projects.properties");
if (Files.isRegularFile(projectProperties)) {
try {
var projectProps = new HashMap();
PropertiesUtils.load(projectProperties, projectProps::put);
int projectsCount = projectProps.size() / 2;
for (int i = 1; i <= projectsCount; i++) {
String name = projectProps.get("project." + i + ".name");
String path = projectProps.get("project." + i + ".path");
projectPathMap.put(name, path);
}
} catch (IOException e) {
LOG.error("Loading of openl-projects.properties has been failed.", e);
}
}
return projectPathMap;
}
private static void migratePropsTo5_24(DynamicPropertySource settings, HashMap props) {
if (Props.bool("project.history.unlimited")) {
props.put("project.history.count", ""); // Define unlimited
}
String runTestParallel = settings.getProperty("test.run.parallel");
if (runTestParallel != null && !Boolean.parseBoolean(runTestParallel)) {
props.put("test.run.thread.count", "1");
}
props.put("project.history.unlimited", null); // Remove
props.put("test.run.parallel", null); // Remove
props.put("project.history.home", null); // Remove
// migrate deploy-config
if (("true").equals(settings.getProperty("repository.deploy-config.separate-repository")) || ("true")
.equals(Props.text("repository.deploy-config.separate-repository"))) {
props.put("repository.deploy-config.separate-repository", null);
props.put("repository.deploy-config.use-repository", null);
// migrate local repo path if have default value, since the default has changed on 5.24.0
// null means this property have default value from previous OpenL version
var depConfRepo = settings.getProperty("repository.deploy-config.factory");
if (settings.getProperty(
"repository.deploy-config.local-repository-path") == null && (depConfRepo == null || "repo-git"
.equals(depConfRepo)) || "org.openl.rules.repository.git.GitRepository".equals(depConfRepo)) {
props.put("repository.deploy-config.local-repository-path", "${openl.home}/deploy-config-repository");
}
} else {
props.put("repository.deploy-config.use-repository", "design");
}
// migrate design repository path
var desRepo = settings.getProperty("repository.design.factory");
if (settings.getProperty("repository.design.local-repository-path") == null && (desRepo == null || "repo-git"
.equals(desRepo)) || "org.openl.rules.repository.git.GitRepository".equals(desRepo)) {
props.put("repository.design.local-repository-path", "${openl.home}/design-repository");
}
// migrate design new-branch-pattern
var desNewBranchPattern = settings.getProperty("repository.design.new-branch-pattern");
if (desNewBranchPattern != null) {
String migratedNewBranchPattern = desNewBranchPattern
.replace("{0}", "{project-name}")
.replace("{1}", "{username}")
.replace("{2}", "{current-date}");
props.put("repository.design.new-branch.pattern", migratedNewBranchPattern);
props.put("repository.design.new-branch-pattern", null);
}
rename(settings,
props,
"repository.deploy-config.comment-validation-pattern",
"repository.deploy-config.comment-template.comment-validation-pattern");
rename(settings,
props,
"repository.deploy-config.invalid-comment-message",
"repository.deploy-config.comment-template.invalid-comment-message");
rename(settings,
props,
"repository.design.comment-validation-pattern",
"repository.design.comment-template.comment-validation-pattern");
rename(settings,
props,
"repository.design.invalid-comment-message",
"repository.design.comment-template.invalid-comment-message");
// migrate deployment repository path
var productionFactory = settings.getProperty("repository.production.factory");
if (settings.getProperty("repository.production.local-repository-path") == null && ("repo-git".equals(
productionFactory) || "org.openl.rules.repository.git.GitRepositoryrepo-git".equals(productionFactory))) {
props.put("repository.production.local-repository-path", "${openl.home}/production-repository");
}
}
private static void rename(DynamicPropertySource settings,
HashMap props,
String oldKey,
String newKey) {
if (settings.containsProperty(oldKey)) {
String value = (String) settings.getProperty(oldKey);
props.put(oldKey, null);
props.put(newKey, value);
}
}
private static void migrateNonFlatProjectSettings(Map nonFlatProjectPaths) {
String workspacePath = Props.text(AdministrationSettings.USER_WORKSPACE_HOME);
Path workspace = Paths.get(workspacePath);
try {
// depth 3 - WorkSpace/UserDir/ProjectName
Files.walkFileTree(workspace, EnumSet.noneOf(FileVisitOption.class), 3, new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path version = dir.resolve(".studioProps/.version");
if (Files.isRegularFile(version)) {
String prName = dir.getFileName().toString();
String projectPath = nonFlatProjectPaths.getOrDefault(prName, "DESIGN/rules/" + prName);
Files.write(version,
("\nrepository-id=design\npath-in-repository=" + projectPath + "\n").getBytes(),
StandardOpenOption.APPEND);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
LOG.error("Migration of locks failed.", e);
}
}
private static void migrateBranchesProps(Map projectPathMap) {
Path branchesProperties = Paths.get(Props.text("openl.home") + "/git-settings/branches.properties");
if (Files.isRegularFile(branchesProperties)) {
try {
var branchProps = new HashMap();
PropertiesUtils.load(branchesProperties, branchProps::put);
String numStr = branchProps.get("projects.number");
BranchesData branches = new BranchesData();
if (numStr != null) {
int num = Integer.parseInt(numStr);
for (int i = 1; i <= num; i++) {
String name = branchProps.get("project." + i + ".name");
String branchesStr = branchProps.get("project." + i + ".branches");
if (StringUtils.isBlank(name) || StringUtils.isBlank(branchesStr)) {
continue;
}
String namePath = projectPathMap.getOrDefault(name, "DESIGN/rules/" + name);
for (String branch : branchesStr.split(",")) {
branches.addBranch(namePath, branch, null);
}
}
Path config = Paths.get(Props.text("openl.home"), "repositories/settings/design/branches.yaml");
createYaml(branches, config);
}
} catch (IOException e) {
LOG.error("Migration of branches.properties has been failed.", e);
}
}
}
private static void writeProjectPathesToYAML(Map projectPathMap) {
if (projectPathMap.isEmpty()) {
return;
}
List projects = new ArrayList<>(projectPathMap.size());
for (Map.Entry entry : projectPathMap.entrySet()) {
projects.add(new ProjectInfo(entry.getKey(), entry.getValue()));
}
ProjectIndex index = new ProjectIndex();
index.setProjects(projects);
Path config = Paths.get(Props.text("openl.home"), "repositories/settings/design/openl-projects.yaml");
createYaml(index, config);
}
private static void createYaml(Object data, Path filePath) {
try {
Files.createDirectories(filePath.getParent());
Files.write(filePath, YamlMapperFactory.getYamlMapper().writeValueAsBytes(data));
} catch (IOException e) {
LOG.error("Writing to file has been failed.", e);
}
}
private static void migrateLocks(Map projectPathMap) {
Path projectLocks = Paths.get(Props.text(AdministrationSettings.USER_WORKSPACE_HOME), ".locks/rules");
if (Files.exists(projectLocks)) {
try {
Files.walkFileTree(projectLocks, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
Path lockPath = projectLocks.relativize(file);
String branchName = "";
// if lockPath does not contains lockBranchPath - repository has no branches
if (lockPath.startsWith("branches/")) {
// ./branches/{Project Name}/{branch/name}/{Project Name}
Path branchPath = lockPath.subpath(2, lockPath.getNameCount() - 1);
branchName = "[branches]/" + branchPath;
}
String projectName = lockPath.getFileName().toString();
String projectPath = projectPathMap.getOrDefault(projectName, "/DESIGN/rules/" + projectName);
Path newLock = Paths.get(Props.text(AdministrationSettings.USER_WORKSPACE_HOME),
".locks/projects/design",
projectPath,
branchName,
"ready.lock");
newLock.getParent().toFile().mkdirs();
Files.copy(file, newLock);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
LOG.error("Migration of locks failed.", e);
}
}
}
}