diff --git a/tools/upgradetool/src/main/java/org/openhab/core/tools/UpgradeTool.java b/tools/upgradetool/src/main/java/org/openhab/core/tools/UpgradeTool.java index 5c21531cd8..83a1889b92 100644 --- a/tools/upgradetool/src/main/java/org/openhab/core/tools/UpgradeTool.java +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/UpgradeTool.java @@ -47,6 +47,9 @@ public class UpgradeTool { private static final String OPT_LOG = "log"; private static final String OPT_FORCE = "force"; + private static final String ENV_USERDATA = "OPENHAB_USERDATA"; + private static final String ENV_CONF = "OPENHAB_CONF"; + private static final List UPGRADERS = List.of( // new ItemUnitToMetadataUpgrader(), // new JSProfileUpgrader(), // @@ -105,26 +108,15 @@ public class UpgradeTool { System.exit(0); } - String userdataDir = commandLine.hasOption(OPT_USERDATA_DIR) ? commandLine.getOptionValue(OPT_USERDATA_DIR) - : System.getenv("OPENHAB_USERDATA"); - if (command == null && (userdataDir == null || userdataDir.isBlank())) { - println("Please either set the environment variable ${OPENHAB_USERDATA} or provide a directory through the --userdata option."); - System.exit(0); - return; - } else if (userdataDir != null) { - logger.info("Using userdataDir: {}", userdataDir); - Path upgradeJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.tools.UpgradeTool"); - upgradeRecords = new JsonStorage<>(upgradeJsonDatabasePath.toFile(), null, 5, 0, 0, List.of()); - } + Path userdataPath = getPath("userdata", commandLine, OPT_USERDATA_DIR, ENV_USERDATA); + Path confPath = getPath("conf", commandLine, OPT_CONF_DIR, ENV_CONF); - String confDir = commandLine.hasOption(OPT_CONF_DIR) ? commandLine.getOptionValue(OPT_CONF_DIR) - : System.getenv("OPENHAB_CONF"); - if (confDir == null || confDir.isBlank()) { - println("Please either set the environment variable ${OPENHAB_CONF} or provide a directory through the --conf option."); - System.exit(0); - return; + if (userdataPath != null) { + Path upgradeJsonDatabasePath = userdataPath + .resolve(Path.of("jsondb", "org.openhab.core.tools.UpgradeTool")); + upgradeRecords = new JsonStorage<>(upgradeJsonDatabasePath.toFile(), null, 5, 0, 0, List.of()); } else { - logger.info("Using confDir: {}", confDir); + logger.warn("Upgrade records storage is not initialized."); } UPGRADERS.forEach(upgrader -> { @@ -139,7 +131,7 @@ public class UpgradeTool { } try { logger.info("Executing {}: {}", upgraderName, upgrader.getDescription()); - if (upgrader.execute(userdataDir, confDir)) { + if (upgrader.execute(userdataPath, confPath)) { updateUpgradeRecord(upgraderName); } } catch (Exception e) { @@ -154,6 +146,40 @@ public class UpgradeTool { System.exit(0); } + /** + * Returns the path to the given directory, either from the command line or from the environment variable. + * If neither is set, it defaults to a relative subdirectory of the given pathName ('./userdata' or './conf'). + * + * @param pathName the name of the directory (e.g., "userdata" or "conf"). + * @param commandLine a CommandLine instance. + * @param option the command line option for the directory (e.g., "userdata" or "conf"). + * @param env the environment variable name for the directory (e.g., "OPENHAB_USERDATA" or "OPENHAB_CONF"). + * @return the absolute path to the directory, or null if it does not exist. + */ + private static @Nullable Path getPath(String pathName, CommandLine commandLine, String option, String env) { + Path path = Path.of(pathName); + + String optionValue = commandLine.getOptionValue(option); + String envValue = System.getenv(env); + + if (optionValue != null && !optionValue.isBlank()) { + path = Path.of(optionValue); + } else if (envValue != null && !envValue.isBlank()) { + path = Path.of(envValue); + } + + path = path.toAbsolutePath(); + + if (path.toFile().isDirectory()) { + return path; + } else { + logger.warn( + "The '{}' directory '{}' does not exist. Some tasks may fail. To set it, either set the environment variable ${{}} or provide a directory through the --{} option.", + pathName, path, env, option); + return null; + } + } + private static @Nullable String lastExecuted(String upgrader) { JsonStorage records = upgradeRecords; if (records != null) { @@ -161,8 +187,6 @@ public class UpgradeTool { if (upgradeRecord != null) { return upgradeRecord.executionDate; } - } else { - logger.error("Upgrade records storage is not initialized."); } return null; } @@ -172,8 +196,6 @@ public class UpgradeTool { if (records != null) { records.put(upgrader, new UpgradeRecord(ZonedDateTime.now())); records.flush(); - } else { - logger.error("Upgrade records storage is not initialized."); } } diff --git a/tools/upgradetool/src/main/java/org/openhab/core/tools/Upgrader.java b/tools/upgradetool/src/main/java/org/openhab/core/tools/Upgrader.java index 3683b7b37e..17c9e8c7c0 100644 --- a/tools/upgradetool/src/main/java/org/openhab/core/tools/Upgrader.java +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/Upgrader.java @@ -12,11 +12,14 @@ */ package org.openhab.core.tools; +import java.nio.file.Path; + import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; /** * The {@link Upgrader} provides an interface for upgrading openHAB configuration files. - * + * * Implementing class MUST provide a no-argument constructor. * * @author Jimmy Tanagra - Initial contribution @@ -30,11 +33,11 @@ public interface Upgrader { /** * Executes the upgrade process. * - * @param userdataDir the OPENHAB_USERDATA directory for the upgrade, + * @param userdataPath the OPENHAB_USERDATA directory for the upgrade, * or a custom path given by the user as --userdata argument - * @param confDir the OPENHAB_CONF directory for the upgrade, + * @param confPath the OPENHAB_CONF directory for the upgrade, * or a custom path given by the user as --conf argument * @return true if the upgrade was successful, false otherwise */ - boolean execute(String userdataDir, String confDir); + boolean execute(@Nullable Path userdataPath, @Nullable Path confPath); } diff --git a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ItemUnitToMetadataUpgrader.java b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ItemUnitToMetadataUpgrader.java index 853daa65ec..09f4dd1646 100644 --- a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ItemUnitToMetadataUpgrader.java +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ItemUnitToMetadataUpgrader.java @@ -24,6 +24,7 @@ import java.util.Objects; import javax.measure.Unit; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.items.ManagedItemProvider; import org.openhab.core.items.Metadata; import org.openhab.core.items.MetadataKey; @@ -57,13 +58,19 @@ public class ItemUnitToMetadataUpgrader implements Upgrader { } @Override - public boolean execute(String userdataDir, String confDir) { + public boolean execute(@Nullable Path userdataPath, @Nullable Path confPath) { + if (userdataPath == null) { + logger.error("{} skipped: no userdata directory found.", getName()); + return false; + } + + userdataPath = userdataPath.resolve("jsondb"); boolean noLink; - Path itemJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.items.Item.json"); - Path metadataJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.items.Metadata.json"); - Path linkJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); - Path thingJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.thing.Thing.json"); + Path itemJsonDatabasePath = userdataPath.resolve("org.openhab.core.items.Item.json"); + Path metadataJsonDatabasePath = userdataPath.resolve("org.openhab.core.items.Metadata.json"); + Path linkJsonDatabasePath = userdataPath.resolve("org.openhab.core.thing.link.ItemChannelLink.json"); + Path thingJsonDatabasePath = userdataPath.resolve("org.openhab.core.thing.Thing.json"); logger.info("Copying item unit from state description to metadata in database '{}'", itemJsonDatabasePath); if (!Files.isReadable(itemJsonDatabasePath)) { diff --git a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/JSProfileUpgrader.java b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/JSProfileUpgrader.java index fa26315fae..0c428ca8ec 100644 --- a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/JSProfileUpgrader.java +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/JSProfileUpgrader.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Objects; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.config.core.Configuration; import org.openhab.core.storage.json.internal.JsonStorage; import org.openhab.core.thing.internal.link.ItemChannelLinkConfigDescriptionProvider; @@ -47,8 +48,15 @@ public class JSProfileUpgrader implements Upgrader { } @Override - public boolean execute(String userdataDir, String confDir) { - Path linkJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); + public boolean execute(@Nullable Path userdataPath, @Nullable Path confPath) { + if (userdataPath == null) { + logger.error("{} skipped: no userdata directory found.", getName()); + return false; + } + + userdataPath = userdataPath.resolve("jsondb"); + + Path linkJsonDatabasePath = userdataPath.resolve("org.openhab.core.thing.link.ItemChannelLink.json"); logger.info("Upgrading JS profile configuration in database '{}'", linkJsonDatabasePath); if (!Files.isWritable(linkJsonDatabasePath)) { diff --git a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ScriptProfileUpgrader.java b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ScriptProfileUpgrader.java index 699739ce21..daf15cce7e 100644 --- a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ScriptProfileUpgrader.java +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ScriptProfileUpgrader.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Objects; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.config.core.Configuration; import org.openhab.core.storage.json.internal.JsonStorage; import org.openhab.core.thing.internal.link.ItemChannelLinkConfigDescriptionProvider; @@ -31,7 +32,7 @@ import org.slf4j.LoggerFactory; * for the separation of {@code toHandlerScript} into * {@code commandFromItemScript} and {@code stateFromItemScript}. * See openhab/openhab-core#4058. - * + * * @author Florian Hotze - Initial contribution * @author Jimmy Tanagra - Refactored into a separate class */ @@ -50,8 +51,14 @@ public class ScriptProfileUpgrader implements Upgrader { } @Override - public boolean execute(String userdataDir, String confDir) { - Path linkJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); + public boolean execute(@Nullable Path userdataPath, @Nullable Path confPath) { + if (userdataPath == null) { + logger.error("{} skipped: no userdata directory found.", getName()); + return false; + } + + Path linkJsonDatabasePath = userdataPath + .resolve(Path.of("jsondb", "org.openhab.core.thing.link.ItemChannelLink.json")); logger.info("Upgrading script profile configuration in database '{}'", linkJsonDatabasePath); if (!Files.isWritable(linkJsonDatabasePath)) { diff --git a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/YamlConfigurationV1TagsUpgrader.java b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/YamlConfigurationV1TagsUpgrader.java index 1cbf97f0d2..cfdc917998 100644 --- a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/YamlConfigurationV1TagsUpgrader.java +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/YamlConfigurationV1TagsUpgrader.java @@ -20,6 +20,7 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; import org.openhab.core.tools.Upgrader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,16 +38,16 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLParser; /** * The {@link YamlConfigurationV1TagsUpgrader} upgrades YAML Tags Configuration from List to Map. - * + * * Convert list to map format for tags in V1 configuration files. - * + * * Input file criteria: * - Search only in CONF/tags/, or in the given directory, and its subdirectories * - Contains a version key with value 1 * - it must contain a tags key that is a list * - The tags list must contain a uid key * - If the above criteria are not met, the file will not be modified - * + * * Output file will * - Retain `version: 1` * - convert tags list to a map with uid as key and the rest as map @@ -54,9 +55,9 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLParser; * - other keys will be unchanged * - A backup of the original file will be created with the extension `.yaml.org` * - If an .org file already exists, append a number to the end, e.g. `.org.1` - * + * * @since 5.0.0 - * + * * @author Jimmy Tanagra - Initial contribution */ @NonNullByDefault @@ -96,16 +97,22 @@ public class YamlConfigurationV1TagsUpgrader implements Upgrader { } @Override - public boolean execute(String userdataDir, String confDir) { - String confEnv = System.getenv("OPENHAB_CONF"); - // If confDir is set to OPENHAB_CONF, look inside /tags/ subdirectory - // otherwise use the given confDir as is - if (confEnv != null && !confEnv.isBlank() && confEnv.equals(confDir)) { - confDir = Path.of(confEnv, "tags").toString(); + public boolean execute(@Nullable Path userdataPath, @Nullable Path confPath) { + if (confPath == null) { + logger.error("{} skipped: no conf directory found.", getName()); + return false; } - Path configPath = Path.of(confDir).toAbsolutePath(); - logger.info("Upgrading YAML tags configurations in '{}'", configPath); + String confEnv = System.getenv("OPENHAB_CONF"); + // If confPath is set to OPENHAB_CONF, look inside /tags/ subdirectory + // otherwise use the given confPath as is + if (confEnv != null && !confEnv.isBlank() && Path.of(confEnv).toAbsolutePath().equals(confPath)) { + confPath = confPath.resolve("tags"); + } + + logger.info("Upgrading YAML tags configurations in '{}'", confPath); + + Path configPath = confPath; // make configPath "effectively final" inside the lambda below try { Files.walkFileTree(configPath, new SimpleFileVisitor<>() { @Override @@ -115,7 +122,6 @@ public class YamlConfigurationV1TagsUpgrader implements Upgrader { Path relativePath = configPath.relativize(file); String modelName = relativePath.toString(); if (!relativePath.startsWith("automation") && modelName.endsWith(".yaml")) { - logger.info("Checking {}", file); convertTagsListToMap(file); } } @@ -142,15 +148,17 @@ public class YamlConfigurationV1TagsUpgrader implements Upgrader { JsonNode versionNode = fileContent.get(VERSION); if (versionNode == null || !versionNode.canConvertToInt() || versionNode.asInt() != 1) { + logger.debug("{} skipped: it doesn't contain a version key", filePath); return; } JsonNode tagsNode = fileContent.get("tags"); if (tagsNode == null || !tagsNode.isArray()) { + logger.debug("{} skipped: it doesn't contain a 'tags' array.", filePath); return; } - logger.info("Found v1 yaml file with tags list {}", filePath); + logger.debug("{} found containing v1 yaml file with a 'tags' array", filePath); fileContent.properties().forEach(entry -> { String key = entry.getKey(); JsonNode node = entry.getValue(); @@ -187,7 +195,7 @@ public class YamlConfigurationV1TagsUpgrader implements Upgrader { try { Files.move(filePath, backupPath); Files.writeString(filePath, content); - logger.info("Converted {} to map format, and the original file saved as {}", filePath, backupPath); + logger.info("{} converted to map format, and the original file saved as {}", filePath, backupPath); } catch (IOException e) { logger.error("Failed to save YAML file {}: {}", filePath, e.getMessage()); }