]> git.basschouten.com Git - openhab-addons.git/blob
14f674eef131d94a755034fa42e0d7e1b6f9490a
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2022 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.persistence.jdbc.internal.console;
14
15 import java.util.Arrays;
16 import java.util.Comparator;
17 import java.util.List;
18 import java.util.stream.Stream;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.core.io.console.Console;
23 import org.openhab.core.io.console.ConsoleCommandCompleter;
24 import org.openhab.core.io.console.StringsCompleter;
25 import org.openhab.core.io.console.extensions.AbstractConsoleCommandExtension;
26 import org.openhab.core.io.console.extensions.ConsoleCommandExtension;
27 import org.openhab.core.persistence.PersistenceService;
28 import org.openhab.core.persistence.PersistenceServiceRegistry;
29 import org.openhab.persistence.jdbc.internal.ItemTableCheckEntry;
30 import org.openhab.persistence.jdbc.internal.ItemTableCheckEntryStatus;
31 import org.openhab.persistence.jdbc.internal.JdbcPersistenceService;
32 import org.openhab.persistence.jdbc.internal.JdbcPersistenceServiceConstants;
33 import org.openhab.persistence.jdbc.internal.exceptions.JdbcSQLException;
34 import org.osgi.service.component.annotations.Activate;
35 import org.osgi.service.component.annotations.Component;
36 import org.osgi.service.component.annotations.Reference;
37
38 /**
39  * The {@link JdbcCommandExtension} is responsible for handling console commands
40  *
41  * @author Jacob Laursen - Initial contribution
42  */
43 @NonNullByDefault
44 @Component(service = ConsoleCommandExtension.class)
45 public class JdbcCommandExtension extends AbstractConsoleCommandExtension implements ConsoleCommandCompleter {
46
47     private static final String CMD_TABLES = "tables";
48     private static final String CMD_RELOAD = "reload";
49     private static final String SUBCMD_TABLES_LIST = "list";
50     private static final String SUBCMD_TABLES_CLEAN = "clean";
51     private static final String PARAMETER_ALL = "all";
52     private static final String PARAMETER_FORCE = "force";
53     private static final StringsCompleter CMD_COMPLETER = new StringsCompleter(List.of(CMD_TABLES, CMD_RELOAD), false);
54     private static final StringsCompleter SUBCMD_TABLES_COMPLETER = new StringsCompleter(
55             List.of(SUBCMD_TABLES_LIST, SUBCMD_TABLES_CLEAN), false);
56
57     private final PersistenceServiceRegistry persistenceServiceRegistry;
58
59     @Activate
60     public JdbcCommandExtension(final @Reference PersistenceServiceRegistry persistenceServiceRegistry) {
61         super(JdbcPersistenceServiceConstants.SERVICE_ID, "Interact with the JDBC persistence service.");
62         this.persistenceServiceRegistry = persistenceServiceRegistry;
63     }
64
65     @Override
66     public void execute(String[] args, Console console) {
67         if (args.length < 1 || args.length > 4) {
68             printUsage(console);
69             return;
70         }
71         JdbcPersistenceService persistenceService = getPersistenceService();
72         if (persistenceService == null) {
73             return;
74         }
75         try {
76             if (!execute(persistenceService, args, console)) {
77                 printUsage(console);
78                 return;
79             }
80         } catch (JdbcSQLException e) {
81             console.println(e.toString());
82         }
83     }
84
85     private @Nullable JdbcPersistenceService getPersistenceService() {
86         for (PersistenceService persistenceService : persistenceServiceRegistry.getAll()) {
87             if (persistenceService instanceof JdbcPersistenceService) {
88                 return (JdbcPersistenceService) persistenceService;
89             }
90         }
91         return null;
92     }
93
94     private boolean execute(JdbcPersistenceService persistenceService, String[] args, Console console)
95             throws JdbcSQLException {
96         if (args.length > 1 && CMD_TABLES.equalsIgnoreCase(args[0])) {
97             if (SUBCMD_TABLES_LIST.equalsIgnoreCase(args[1])) {
98                 listTables(persistenceService, console, args.length == 3 && PARAMETER_ALL.equalsIgnoreCase(args[2]));
99                 return true;
100             } else if (SUBCMD_TABLES_CLEAN.equalsIgnoreCase(args[1])) {
101                 if (args.length == 3) {
102                     cleanupItem(persistenceService, console, args[2], false);
103                     return true;
104                 } else if (args.length == 4 && PARAMETER_FORCE.equalsIgnoreCase(args[3])) {
105                     cleanupItem(persistenceService, console, args[2], true);
106                     return true;
107                 } else {
108                     cleanupTables(persistenceService, console);
109                     return true;
110                 }
111             }
112         } else if (args.length == 1 && CMD_RELOAD.equalsIgnoreCase(args[0])) {
113             reload(persistenceService, console);
114             return true;
115         }
116         return false;
117     }
118
119     private void listTables(JdbcPersistenceService persistenceService, Console console, Boolean all)
120             throws JdbcSQLException {
121         List<ItemTableCheckEntry> entries = persistenceService.getCheckedEntries();
122         if (!all) {
123             entries.removeIf(t -> t.getStatus() == ItemTableCheckEntryStatus.VALID);
124         }
125         entries.sort(Comparator.comparing(ItemTableCheckEntry::getTableName));
126         int itemNameMaxLength = Math
127                 .max(entries.stream().map(t -> t.getItemName().length()).max(Integer::compare).orElse(0), 4);
128         int tableNameMaxLength = Math
129                 .max(entries.stream().map(t -> t.getTableName().length()).max(Integer::compare).orElse(0), 5);
130         int statusMaxLength = Stream.of(ItemTableCheckEntryStatus.values()).map(t -> t.toString().length())
131                 .max(Integer::compare).get();
132         console.println(String.format(
133                 "%1$-" + (tableNameMaxLength + 2) + "sRow Count  %2$-" + (itemNameMaxLength + 2) + "s%3$s", "Table",
134                 "Item", "Status"));
135         console.println("-".repeat(tableNameMaxLength) + "  " + "---------  " + "-".repeat(itemNameMaxLength) + "  "
136                 + "-".repeat(statusMaxLength));
137         for (ItemTableCheckEntry entry : entries) {
138             String tableName = entry.getTableName();
139             ItemTableCheckEntryStatus status = entry.getStatus();
140             long rowCount = status == ItemTableCheckEntryStatus.VALID
141                     || status == ItemTableCheckEntryStatus.ITEM_MISSING ? persistenceService.getRowCount(tableName) : 0;
142             console.println(String.format(
143                     "%1$-" + (tableNameMaxLength + 2) + "s%2$9d  %3$-" + (itemNameMaxLength + 2) + "s%4$s", tableName,
144                     rowCount, entry.getItemName(), status));
145         }
146     }
147
148     private void cleanupTables(JdbcPersistenceService persistenceService, Console console) throws JdbcSQLException {
149         console.println("Cleaning up all inconsistent items...");
150         List<ItemTableCheckEntry> entries = persistenceService.getCheckedEntries();
151         entries.removeIf(t -> t.getStatus() == ItemTableCheckEntryStatus.VALID || t.getItemName().isEmpty());
152         for (ItemTableCheckEntry entry : entries) {
153             console.print(entry.getItemName() + " -> ");
154             if (persistenceService.cleanupItem(entry)) {
155                 console.println("done.");
156             } else {
157                 console.println("skipped/failed.");
158             }
159         }
160     }
161
162     private void cleanupItem(JdbcPersistenceService persistenceService, Console console, String itemName, boolean force)
163             throws JdbcSQLException {
164         console.print("Cleaning up item " + itemName + "... ");
165         if (persistenceService.cleanupItem(itemName, force)) {
166             console.println("done.");
167         } else {
168             console.println("skipped/failed.");
169         }
170     }
171
172     private void reload(JdbcPersistenceService persistenceService, Console console) throws JdbcSQLException {
173         persistenceService.populateItemNameToTableNameMap();
174         console.println("Item index reloaded.");
175     }
176
177     @Override
178     public List<String> getUsages() {
179         return Arrays.asList(
180                 buildCommandUsage(CMD_TABLES + " " + SUBCMD_TABLES_LIST + " [" + PARAMETER_ALL + "]",
181                         "list tables (all = include valid)"),
182                 buildCommandUsage(
183                         CMD_TABLES + " " + SUBCMD_TABLES_CLEAN + " [<itemName>]" + " [" + PARAMETER_FORCE + "]",
184                         "clean inconsistent items (remove from index and drop tables)"),
185                 buildCommandUsage(CMD_RELOAD, "reload item index/schema"));
186     }
187
188     @Override
189     public @Nullable ConsoleCommandCompleter getCompleter() {
190         return this;
191     }
192
193     @Override
194     public boolean complete(String[] args, int cursorArgumentIndex, int cursorPosition, List<String> candidates) {
195         if (cursorArgumentIndex <= 0) {
196             return CMD_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
197         } else if (cursorArgumentIndex == 1) {
198             if (CMD_TABLES.equalsIgnoreCase(args[0])) {
199                 return SUBCMD_TABLES_COMPLETER.complete(args, cursorArgumentIndex, cursorPosition, candidates);
200             }
201         } else if (cursorArgumentIndex == 2) {
202             if (CMD_TABLES.equalsIgnoreCase(args[0])) {
203                 if (SUBCMD_TABLES_CLEAN.equalsIgnoreCase(args[1])) {
204                     JdbcPersistenceService persistenceService = getPersistenceService();
205                     if (persistenceService != null) {
206                         return new StringsCompleter(persistenceService.getItemNames(), true).complete(args,
207                                 cursorArgumentIndex, cursorPosition, candidates);
208                     }
209                 } else if (SUBCMD_TABLES_LIST.equalsIgnoreCase(args[1])) {
210                     new StringsCompleter(List.of(PARAMETER_ALL), false).complete(args, cursorArgumentIndex,
211                             cursorPosition, candidates);
212                 }
213             }
214         }
215         return false;
216     }
217 }