From 2515b05ee3b61920546673a8a7b271ad79bdaf8e Mon Sep 17 00:00:00 2001 From: Jimmy Tanagra Date: Sun, 27 Apr 2025 22:43:14 +1000 Subject: [PATCH] Upgrade Tool: Split upgrade commands into individual classes Signed-off-by: Jimmy Tanagra --- .../org/openhab/core/tools/UpgradeTool.java | 148 +++++++++++++---- .../java/org/openhab/core/tools/Upgrader.java | 40 +++++ ...r.java => ItemUnitToMetadataUpgrader.java} | 153 +++--------------- .../tools/internal/JSProfileUpgrader.java | 84 ++++++++++ .../tools/internal/ScriptProfileUpgrader.java | 85 ++++++++++ 5 files changed, 349 insertions(+), 161 deletions(-) create mode 100644 tools/upgradetool/src/main/java/org/openhab/core/tools/Upgrader.java rename tools/upgradetool/src/main/java/org/openhab/core/tools/internal/{Upgrader.java => ItemUnitToMetadataUpgrader.java} (55%) create mode 100644 tools/upgradetool/src/main/java/org/openhab/core/tools/internal/JSProfileUpgrader.java create mode 100644 tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ScriptProfileUpgrader.java 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 063f11718b..a72c8b199c 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 @@ -12,8 +12,10 @@ */ package org.openhab.core.tools; -import static org.openhab.core.tools.internal.Upgrader.*; - +import java.io.PrintStream; +import java.nio.file.Path; +import java.time.ZonedDateTime; +import java.util.List; import java.util.Set; import org.apache.commons.cli.CommandLine; @@ -23,27 +25,47 @@ import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.tools.internal.Upgrader; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.storage.json.internal.JsonStorage; +import org.openhab.core.tools.internal.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The {@link UpgradeTool} is a tool for upgrading openHAB to mitigate breaking changes * * @author Jan N. Klug - Initial contribution + * @author Jimmy Tanagra - Refactor upgraders into individual classes */ @NonNullByDefault public class UpgradeTool { private static final Set LOG_LEVELS = Set.of("TRACE", "DEBUG", "INFO", "WARN", "ERROR"); private static final String OPT_COMMAND = "command"; - private static final String OPT_DIR = "dir"; + private static final String OPT_LIST_COMMANDS = "list-commands"; + private static final String OPT_USERDATA_DIR = "userdata"; + private static final String OPT_CONF_DIR = "conf"; private static final String OPT_LOG = "log"; private static final String OPT_FORCE = "force"; + private static final List UPGRADERS = List.of( // + new ItemUnitToMetadataUpgrader(), // + new JSProfileUpgrader(), // + new ScriptProfileUpgrader() // + ); + + private static final Logger logger = LoggerFactory.getLogger(UpgradeTool.class); + private static @Nullable JsonStorage upgradeRecords = null; + private static Options getOptions() { Options options = new Options(); - options.addOption(Option.builder().longOpt(OPT_DIR).desc("directory to process").numberOfArgs(1).build()); + options.addOption(Option.builder().longOpt(OPT_USERDATA_DIR).desc("USERDATA directory to process") + .numberOfArgs(1).build()); + options.addOption( + Option.builder().longOpt(OPT_CONF_DIR).desc("CONF directory to process").numberOfArgs(1).build()); options.addOption(Option.builder().longOpt(OPT_COMMAND).numberOfArgs(1) .desc("command to execute (executes all if omitted)").build()); + options.addOption(Option.builder().longOpt(OPT_LIST_COMMANDS).desc("list available commands").build()); options.addOption(Option.builder().longOpt(OPT_LOG).numberOfArgs(1).desc("log verbosity").build()); options.addOption(Option.builder().longOpt(OPT_FORCE).desc("force execution (even if already done)").build()); @@ -58,42 +80,110 @@ public class UpgradeTool { String loglevel = commandLine.hasOption(OPT_LOG) ? commandLine.getOptionValue(OPT_LOG).toUpperCase() : "INFO"; if (!LOG_LEVELS.contains(loglevel)) { - System.out.println("Allowed log-levels are " + LOG_LEVELS); + println("Allowed log-levels are " + LOG_LEVELS); + System.exit(0); + } + + if (commandLine.hasOption(OPT_LIST_COMMANDS)) { + println("Available commands:"); + UPGRADERS.stream().forEach(upgrader -> { + println(" - " + upgrader.getName() + ": " + upgrader.getDescription()); + }); System.exit(0); } System.setProperty(org.slf4j.simple.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, loglevel); - String baseDir = commandLine.hasOption(OPT_DIR) ? commandLine.getOptionValue(OPT_DIR) + String userdataDir = commandLine.hasOption(OPT_USERDATA_DIR) ? commandLine.getOptionValue(OPT_USERDATA_DIR) : System.getenv("OPENHAB_USERDATA"); - if (baseDir == null || baseDir.isBlank()) { - System.out.println( - "Please either set the environment variable ${OPENHAB_USERDATA} or provide a directory through the --dir option."); + if (userdataDir == null || userdataDir.isBlank()) { + println("Please either set the environment variable ${OPENHAB_USERDATA} or provide a directory through the --userdata option."); System.exit(0); - } else { - boolean force = commandLine.hasOption(OPT_FORCE); - - Upgrader upgrader = new Upgrader(baseDir, force); - if (!commandLine.hasOption(OPT_COMMAND) - || ITEM_COPY_UNIT_TO_METADATA.equals(commandLine.getOptionValue(OPT_COMMAND))) { - upgrader.itemCopyUnitToMetadata(); - } - if (!commandLine.hasOption(OPT_COMMAND) - || LINK_UPGRADE_JS_PROFILE.equals(commandLine.getOptionValue(OPT_COMMAND))) { - upgrader.linkUpgradeJsProfile(); - } - if (!commandLine.hasOption(OPT_COMMAND) - || LINK_UPGRADE_SCRIPT_PROFILE.equals(commandLine.getOptionValue(OPT_COMMAND))) { - upgrader.linkUpgradeScriptProfile(); - } + return; } + + 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; + } + + boolean force = commandLine.hasOption(OPT_FORCE); + String command = commandLine.hasOption(OPT_COMMAND) ? commandLine.getOptionValue(OPT_COMMAND) : null; + + if (command != null && UPGRADERS.stream().filter(u -> u.getName().equals(command)).findAny().isEmpty()) { + println("Unknown command: " + command); + System.exit(0); + } + + Path upgradeJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.tools.UpgradeTool"); + upgradeRecords = new JsonStorage<>(upgradeJsonDatabasePath.toFile(), null, 5, 0, 0, List.of()); + + UPGRADERS.forEach(upgrader -> { + String upgraderName = upgrader.getName(); + if (command != null && !upgraderName.equals(command)) { + return; + } + if (!force && lastExecuted(upgraderName) instanceof String executionDate) { + logger.info("Already executed '{}' on {}. Use '--force' to execute it again.", upgraderName, + executionDate); + return; + } + try { + logger.info("Executing {}: {}", upgraderName, upgrader.getDescription()); + if (upgrader.execute(userdataDir, confDir)) { + updateUpgradeRecord(upgraderName); + } + } catch (Exception e) { + logger.error("Error executing upgrader {}: {}", upgraderName, e.getMessage()); + } + }); } catch (ParseException e) { HelpFormatter formatter = new HelpFormatter(); - String commands = Set.of(ITEM_COPY_UNIT_TO_METADATA, LINK_UPGRADE_JS_PROFILE, LINK_UPGRADE_SCRIPT_PROFILE) - .toString(); - formatter.printHelp("upgradetool", "", options, "Available commands: " + commands, true); + formatter.printHelp("upgradetool", "", options, "", true); } System.exit(0); } + + private static @Nullable String lastExecuted(String upgrader) { + JsonStorage records = upgradeRecords; + if (records != null) { + UpgradeRecord upgradeRecord = records.get(upgrader); + if (upgradeRecord != null) { + return upgradeRecord.executionDate; + } + } else { + logger.error("Upgrade records storage is not initialized."); + } + return null; + } + + private static void updateUpgradeRecord(String upgrader) { + JsonStorage records = upgradeRecords; + if (records != null) { + records.put(upgrader, new UpgradeRecord(ZonedDateTime.now())); + records.flush(); + } else { + logger.error("Upgrade records storage is not initialized."); + } + } + + // to avoid compiler's null pointer warnings + private static void println(String message) { + PrintStream out = System.out; + if (out != null) { + out.println(message); + } + } + + private static class UpgradeRecord { + public final String executionDate; + + public UpgradeRecord(ZonedDateTime executionDate) { + this.executionDate = executionDate.toString(); + } + } } 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 new file mode 100644 index 0000000000..3683b7b37e --- /dev/null +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/Upgrader.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.tools; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link Upgrader} provides an interface for upgrading openHAB configuration files. + * + * Implementing class MUST provide a no-argument constructor. + * + * @author Jimmy Tanagra - Initial contribution + */ +@NonNullByDefault +public interface Upgrader { + String getName(); + + String getDescription(); + + /** + * Executes the upgrade process. + * + * @param userdataDir 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, + * 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); +} diff --git a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/Upgrader.java b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ItemUnitToMetadataUpgrader.java similarity index 55% rename from tools/upgradetool/src/main/java/org/openhab/core/tools/internal/Upgrader.java rename to tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ItemUnitToMetadataUpgrader.java index 20bca3443d..853daa65ec 100644 --- a/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/Upgrader.java +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ItemUnitToMetadataUpgrader.java @@ -18,77 +18,57 @@ import static org.openhab.core.thing.DefaultSystemChannelTypeProvider.SYSTEM_CHA import java.nio.file.Files; import java.nio.file.Path; -import java.time.ZonedDateTime; import java.util.List; import java.util.Objects; import javax.measure.Unit; import org.eclipse.jdt.annotation.NonNullByDefault; -import org.openhab.core.config.core.Configuration; import org.openhab.core.items.ManagedItemProvider; import org.openhab.core.items.Metadata; import org.openhab.core.items.MetadataKey; import org.openhab.core.library.items.NumberItem; import org.openhab.core.storage.json.internal.JsonStorage; import org.openhab.core.thing.dto.ThingDTO; -import org.openhab.core.thing.internal.link.ItemChannelLinkConfigDescriptionProvider; import org.openhab.core.thing.link.ItemChannelLink; +import org.openhab.core.tools.Upgrader; import org.openhab.core.types.util.UnitUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * The {@link Upgrader} contains the implementation of the upgrade methods + * The {@link ItemUnitToMetadataUpgrader} copies the unit from the item to the metadata. * * @author Jan N. Klug - Initial contribution - * @author Florian Hotze - Add script profile upgrade + * @author Jimmy Tanagra - Refactored into a separate class */ @NonNullByDefault -public class Upgrader { - public static final String ITEM_COPY_UNIT_TO_METADATA = "itemCopyUnitToMetadata"; - public static final String LINK_UPGRADE_JS_PROFILE = "linkUpgradeJsProfile"; - public static final String LINK_UPGRADE_SCRIPT_PROFILE = "linkUpgradeScriptProfile"; +public class ItemUnitToMetadataUpgrader implements Upgrader { + private final Logger logger = LoggerFactory.getLogger(ItemUnitToMetadataUpgrader.class); - private final Logger logger = LoggerFactory.getLogger(Upgrader.class); - private final String baseDir; - private final boolean force; - private final JsonStorage upgradeRecords; - - public Upgrader(String baseDir, boolean force) { - this.baseDir = baseDir; - this.force = force; - - Path upgradeJsonDatabasePath = Path.of(baseDir, "jsondb", "org.openhab.core.tools.UpgradeTool"); - - upgradeRecords = new JsonStorage<>(upgradeJsonDatabasePath.toFile(), null, 5, 0, 0, List.of()); + @Override + public String getName() { + return "itemCopyUnitToMetadata"; // keep the old name for backwards compatibility } - private boolean checkUpgradeRecord(String key) { - UpgradeRecord upgradeRecord = upgradeRecords.get(key); - if (upgradeRecord != null && !force) { - logger.info("Already executed '{}' on {}. Use '--force' to execute it again.", key, - upgradeRecord.executionDate); - return false; - } - return true; + @Override + public String getDescription() { + return "Copy item unit from state description to metadata"; } - public void itemCopyUnitToMetadata() { + @Override + public boolean execute(String userdataDir, String confDir) { boolean noLink; - if (!checkUpgradeRecord(ITEM_COPY_UNIT_TO_METADATA)) { - return; - } - Path itemJsonDatabasePath = Path.of(baseDir, "jsondb", "org.openhab.core.items.Item.json"); - Path metadataJsonDatabasePath = Path.of(baseDir, "jsondb", "org.openhab.core.items.Metadata.json"); - Path linkJsonDatabasePath = Path.of(baseDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); - Path thingJsonDatabasePath = Path.of(baseDir, "jsondb", "org.openhab.core.thing.Thing.json"); + 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"); logger.info("Copying item unit from state description to metadata in database '{}'", itemJsonDatabasePath); if (!Files.isReadable(itemJsonDatabasePath)) { logger.error("Cannot access item database '{}', check path and access rights.", itemJsonDatabasePath); - return; + return false; } if (!Files.isReadable(linkJsonDatabasePath) || !Files.isReadable(thingJsonDatabasePath)) { @@ -102,7 +82,7 @@ public class Upgrader { if (!Files.isWritable(metadataJsonDatabasePath) && Files.exists(metadataJsonDatabasePath)) { logger.error("Cannot access metadata database '{}', check path and access rights.", metadataJsonDatabasePath); - return; + return false; } JsonStorage itemStorage = new JsonStorage<>(itemJsonDatabasePath.toFile(), @@ -121,7 +101,7 @@ public class Upgrader { logger.debug("{}: Already contains a 'unit' metadata, skipping it", itemName); } else { String unit = null; - if (!noLink) { + if (linkStorage != null && thingStorage != null) { List links = linkStorage.getValues().stream().map(Objects::requireNonNull) .filter(link -> itemName.equals(link.getItemName())).toList(); // check if we can find the channel for these links @@ -192,98 +172,7 @@ public class Upgrader { }); metadataStorage.flush(); - upgradeRecords.put(ITEM_COPY_UNIT_TO_METADATA, new UpgradeRecord(ZonedDateTime.now())); - upgradeRecords.flush(); - } - public void linkUpgradeJsProfile() { - if (!checkUpgradeRecord(LINK_UPGRADE_JS_PROFILE)) { - return; - } - - Path linkJsonDatabasePath = Path.of(baseDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); - logger.info("Upgrading JS profile configuration in database '{}'", linkJsonDatabasePath); - - if (!Files.isWritable(linkJsonDatabasePath)) { - logger.error("Cannot access link database '{}', check path and access rights.", linkJsonDatabasePath); - return; - } - JsonStorage linkStorage = new JsonStorage<>(linkJsonDatabasePath.toFile(), null, 5, 0, 0, - List.of()); - - List.copyOf(linkStorage.getKeys()).forEach(linkUid -> { - ItemChannelLink link = Objects.requireNonNull(linkStorage.get(linkUid)); - Configuration configuration = link.getConfiguration(); - String profileName = (String) configuration.get(ItemChannelLinkConfigDescriptionProvider.PARAM_PROFILE); - if ("transform:JS".equals(profileName)) { - String function = (String) configuration.get("function"); - if (function != null) { - configuration.put("toItemScript", function); - configuration.put("toHandlerScript", "|input"); - configuration.remove("function"); - configuration.remove("sourceFormat"); - - linkStorage.put(linkUid, link); - logger.info("{}: rewrote JS profile link to new format", linkUid); - } else { - logger.info("{}: link already has correct configuration", linkUid); - } - } - }); - - linkStorage.flush(); - upgradeRecords.put(LINK_UPGRADE_JS_PROFILE, new UpgradeRecord(ZonedDateTime.now())); - upgradeRecords.flush(); - } - - /** - * Upgrades the ItemChannelLink database for the separation of {@code toHandlerScript} into - * {@code commandFromItemScript} and {@code stateFromItemScript}. - * See openhab/openhab-core#4058. - */ - public void linkUpgradeScriptProfile() { - if (!checkUpgradeRecord(LINK_UPGRADE_SCRIPT_PROFILE)) { - return; - } - - Path linkJsonDatabasePath = Path.of(baseDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); - logger.info("Upgrading script profile configuration in database '{}'", linkJsonDatabasePath); - - if (!Files.isWritable(linkJsonDatabasePath)) { - logger.error("Cannot access link database '{}', check path and access rights.", linkJsonDatabasePath); - return; - } - JsonStorage linkStorage = new JsonStorage<>(linkJsonDatabasePath.toFile(), null, 5, 0, 0, - List.of()); - - List.copyOf(linkStorage.getKeys()).forEach(linkUid -> { - ItemChannelLink link = Objects.requireNonNull(linkStorage.get(linkUid)); - Configuration configuration = link.getConfiguration(); - String profileName = (String) configuration.get(ItemChannelLinkConfigDescriptionProvider.PARAM_PROFILE); - if (profileName != null && profileName.startsWith("transform:")) { - String toHandlerScript = (String) configuration.get("toHandlerScript"); - if (toHandlerScript != null) { - configuration.put("commandFromItemScript", toHandlerScript); - configuration.remove("toHandlerScript"); - - linkStorage.put(linkUid, link); - logger.info("{}: rewrote script profile link to new format", linkUid); - } else { - logger.info("{}: link already has correct configuration", linkUid); - } - } - }); - - linkStorage.flush(); - upgradeRecords.put(LINK_UPGRADE_SCRIPT_PROFILE, new UpgradeRecord(ZonedDateTime.now())); - upgradeRecords.flush(); - } - - private static class UpgradeRecord { - public final String executionDate; - - public UpgradeRecord(ZonedDateTime executionDate) { - this.executionDate = executionDate.toString(); - } + return true; } } 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 new file mode 100644 index 0000000000..fa26315fae --- /dev/null +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/JSProfileUpgrader.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.tools.internal; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.storage.json.internal.JsonStorage; +import org.openhab.core.thing.internal.link.ItemChannelLinkConfigDescriptionProvider; +import org.openhab.core.thing.link.ItemChannelLink; +import org.openhab.core.tools.Upgrader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link JSProfileUpgrader} upgrades JS Profile configurations + * + * @author Jan N. Klug - Initial contribution + * @author Jimmy Tanagra - Refactored into a separate class + */ +@NonNullByDefault +public class JSProfileUpgrader implements Upgrader { + private final Logger logger = LoggerFactory.getLogger(JSProfileUpgrader.class); + + @Override + public String getName() { + return "linkUpgradeJSProfile"; // keep the old name for backwards compatibility + } + + @Override + public String getDescription() { + return "Upgrade JS profile configuration to new format"; + } + + @Override + public boolean execute(String userdataDir, String confDir) { + Path linkJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); + logger.info("Upgrading JS profile configuration in database '{}'", linkJsonDatabasePath); + + if (!Files.isWritable(linkJsonDatabasePath)) { + logger.error("Cannot access link database '{}', check path and access rights.", linkJsonDatabasePath); + return false; + } + JsonStorage linkStorage = new JsonStorage<>(linkJsonDatabasePath.toFile(), null, 5, 0, 0, + List.of()); + + List.copyOf(linkStorage.getKeys()).forEach(linkUid -> { + ItemChannelLink link = Objects.requireNonNull(linkStorage.get(linkUid)); + Configuration configuration = link.getConfiguration(); + String profileName = (String) configuration.get(ItemChannelLinkConfigDescriptionProvider.PARAM_PROFILE); + if ("transform:JS".equals(profileName)) { + String function = (String) configuration.get("function"); + if (function != null) { + configuration.put("toItemScript", function); + configuration.put("toHandlerScript", "|input"); + configuration.remove("function"); + configuration.remove("sourceFormat"); + + linkStorage.put(linkUid, link); + logger.info("{}: rewrote JS profile link to new format", linkUid); + } else { + logger.info("{}: link already has correct configuration", linkUid); + } + } + }); + + linkStorage.flush(); + return true; + } +} 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 new file mode 100644 index 0000000000..699739ce21 --- /dev/null +++ b/tools/upgradetool/src/main/java/org/openhab/core/tools/internal/ScriptProfileUpgrader.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.core.tools.internal; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.storage.json.internal.JsonStorage; +import org.openhab.core.thing.internal.link.ItemChannelLinkConfigDescriptionProvider; +import org.openhab.core.thing.link.ItemChannelLink; +import org.openhab.core.tools.Upgrader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link ScriptProfileUpgrader} upgrades the ItemChannelLink database + * 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 + */ +@NonNullByDefault +public class ScriptProfileUpgrader implements Upgrader { + private final Logger logger = LoggerFactory.getLogger(ScriptProfileUpgrader.class); + + @Override + public String getName() { + return "linkUpgradeScriptProfile"; // keep the old name for backwards compatibility + } + + @Override + public String getDescription() { + return "Upgrade script profile configuration toHandlerScript to commandFromItemScript"; + } + + @Override + public boolean execute(String userdataDir, String confDir) { + Path linkJsonDatabasePath = Path.of(userdataDir, "jsondb", "org.openhab.core.thing.link.ItemChannelLink.json"); + logger.info("Upgrading script profile configuration in database '{}'", linkJsonDatabasePath); + + if (!Files.isWritable(linkJsonDatabasePath)) { + logger.error("Cannot access link database '{}', check path and access rights.", linkJsonDatabasePath); + return false; + } + JsonStorage linkStorage = new JsonStorage<>(linkJsonDatabasePath.toFile(), null, 5, 0, 0, + List.of()); + + List.copyOf(linkStorage.getKeys()).forEach(linkUid -> { + ItemChannelLink link = Objects.requireNonNull(linkStorage.get(linkUid)); + Configuration configuration = link.getConfiguration(); + String profileName = (String) configuration.get(ItemChannelLinkConfigDescriptionProvider.PARAM_PROFILE); + if (profileName != null && profileName.startsWith("transform:")) { + String toHandlerScript = (String) configuration.get("toHandlerScript"); + if (toHandlerScript != null) { + configuration.put("commandFromItemScript", toHandlerScript); + configuration.remove("toHandlerScript"); + + linkStorage.put(linkUid, link); + logger.info("{}: rewrote script profile link to new format", linkUid); + } else { + logger.info("{}: link already has correct configuration", linkUid); + } + } + }); + + linkStorage.flush(); + return true; + } +}