From 76aff683ee9729f54228a9bd43e693d4aee2b330 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Tue, 26 May 2020 17:37:57 +0200 Subject: [PATCH] [Major] I18nMessage and LogMessage refactorings, added state, JSON parsers, etc. --- .../agent/impl/ComponentContainerImpl.java | 5 +- .../agent/impl/DefaultObserverHandler.java | 3 +- .../impl/EventCollectingObserverHandler.java | 3 +- .../handler/operationslog/LogMessage.java | 93 +++++--- .../operationslog/LogMessageState.java | 7 + .../handler/operationslog/OperationsLog.java | 101 ++++++++- .../main/java/li/strolch/job/StrolchJob.java | 3 +- .../persistence/api/AbstractTransaction.java | 3 +- .../persistence/api/LogMessageDao.java | 6 +- .../service/api/DefaultServiceHandler.java | 5 +- .../model/i18n/I18nMessageJsonParser.java | 34 ++- .../model/i18n/I18nMessageToJsonVisitor.java | 7 +- .../postgresql/PostgreSqlLogMessageDao.java | 71 ++++-- .../strolch_db_schema_0.9.0_drop.sql | 16 ++ .../strolch_db_schema_0.9.0_initial.sql | 207 ++++++++++++++++++ .../strolch_db_schema_0.9.0_migration.sql | 26 +++ .../resources/strolch_db_version.properties | 2 +- .../persistence/xml/XmlLogMessageDao.java | 11 + .../xml/model/LogMessageSaxReader.java | 13 +- .../model/LogMessageToSaxWriterVisitor.java | 1 + .../java/li/strolch/execution/Controller.java | 5 +- .../execution/EventBasedExecutionHandler.java | 13 +- .../SimpleDurationExecutionTimer.java | 3 +- .../policy/ToErrorReservationExecution.java | 3 +- .../li/strolch/migrations/Migrations.java | 3 +- .../strolch/migrations/MigrationsHandler.java | 3 +- .../runtime/LogMessagesTestRunner.java | 51 ++++- .../java/li/strolch/utils/I18nMessage.java | 14 +- 28 files changed, 608 insertions(+), 104 deletions(-) create mode 100644 li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessageState.java create mode 100644 li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_drop.sql create mode 100644 li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_initial.sql create mode 100644 li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_migration.sql diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/impl/ComponentContainerImpl.java b/li.strolch.agent/src/main/java/li/strolch/agent/impl/ComponentContainerImpl.java index 9f7c44063..ebeda5852 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/impl/ComponentContainerImpl.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/impl/ComponentContainerImpl.java @@ -29,6 +29,7 @@ import java.util.*; import li.strolch.agent.api.*; import li.strolch.exception.StrolchException; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -287,7 +288,7 @@ public class ComponentContainerImpl implements ComponentContainer { for (String realmName : getRealmNames()) { getComponent(OperationsLog.class).addMessage(new LogMessage(realmName, SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info, - ResourceBundle.getBundle("strolch-agent"), "agent.started") // + LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.started") // .value("applicationName", applicationName) // .value("environment", environment) // .value("components", "" + this.controllerMap.size()) // @@ -311,7 +312,7 @@ public class ComponentContainerImpl implements ComponentContainer { for (String realmName : getRealmNames()) { getComponent(OperationsLog.class).addMessage(new LogMessage(realmName, SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info, - ResourceBundle.getBundle("strolch-agent"), "agent.stopping") // + LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.stopping") // .value("applicationName", applicationName) // .value("environment", environment) // .value("components", "" + this.controllerMap.size())); diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultObserverHandler.java b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultObserverHandler.java index 837a55dc6..ccbb72d74 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultObserverHandler.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/impl/DefaultObserverHandler.java @@ -26,6 +26,7 @@ import java.util.concurrent.ScheduledExecutorService; import li.strolch.agent.api.*; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -162,7 +163,7 @@ public class DefaultObserverHandler implements ObserverHandler { OperationsLog operationsLog = container.getComponent(OperationsLog.class); operationsLog.addMessage(new LogMessage(this.realm.getRealm(), SYSTEM_USER_AGENT, Locator.valueOf(AGENT, ObserverHandler.class.getName(), type, StrolchAgent.getUniqueId()), - LogSeverity.Exception, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed") + LogSeverity.Exception, LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed") .withException(e).value("type", type).value("reason", e)); } } diff --git a/li.strolch.agent/src/main/java/li/strolch/agent/impl/EventCollectingObserverHandler.java b/li.strolch.agent/src/main/java/li/strolch/agent/impl/EventCollectingObserverHandler.java index e64f384c9..b26f58b6a 100644 --- a/li.strolch.agent/src/main/java/li/strolch/agent/impl/EventCollectingObserverHandler.java +++ b/li.strolch.agent/src/main/java/li/strolch/agent/impl/EventCollectingObserverHandler.java @@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit; import li.strolch.agent.api.*; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -193,7 +194,7 @@ public class EventCollectingObserverHandler implements ObserverHandler { OperationsLog operationsLog = container.getComponent(OperationsLog.class); operationsLog.addMessage(new LogMessage(this.realm.getRealm(), SYSTEM_USER_AGENT, Locator.valueOf(AGENT, ObserverHandler.class.getName(), type, StrolchAgent.getUniqueId()), - LogSeverity.Exception, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed") + LogSeverity.Exception, LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.observers.update.failed") .withException(e).value("type", type).value("reason", e)); } } diff --git a/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessage.java b/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessage.java index 2894b93b1..199300865 100644 --- a/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessage.java +++ b/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessage.java @@ -2,6 +2,7 @@ package li.strolch.handler.operationslog; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.Objects; import java.util.Properties; import java.util.ResourceBundle; @@ -11,32 +12,36 @@ import li.strolch.model.Locator; import li.strolch.model.Tags.Json; import li.strolch.utils.I18nMessage; import li.strolch.utils.helper.ExceptionHelper; +import li.strolch.utils.iso8601.ISO8601; public class LogMessage extends I18nMessage { private final String id; private final String username; - private ZonedDateTime zonedDateTime; + private final ZonedDateTime zonedDateTime; private final String realm; private final Locator locator; private final LogSeverity severity; + private LogMessageState state; private String stackTrace; - public LogMessage(String realm, String username, Locator locator, LogSeverity severity, ResourceBundle bundle, - String key) { + public LogMessage(String realm, String username, Locator locator, LogSeverity severity, LogMessageState state, + ResourceBundle bundle, String key) { super(bundle, key); this.id = StrolchAgent.getUniqueId(); - this.zonedDateTime = ZonedDateTime.now(); // persisting in the DB only handles millisecond precision, not nano precision - this.zonedDateTime = this.zonedDateTime.withNano((this.zonedDateTime.getNano() / 1000000) * 1000000); + ZonedDateTime now = ZonedDateTime.now(); + this.zonedDateTime = now.withNano((now.getNano() / 1000000) * 1000000); this.realm = realm; this.username = username; this.locator = locator; this.severity = severity; + this.state = state; } public LogMessage(String id, ZonedDateTime zonedDateTime, String realm, String username, Locator locator, - LogSeverity severity, String key, Properties values, String message, String stackTrace) { + LogSeverity severity, LogMessageState state, String key, Properties values, String message, + String stackTrace) { super(key, values, message); this.id = id; this.zonedDateTime = zonedDateTime; @@ -44,6 +49,7 @@ public class LogMessage extends I18nMessage { this.username = username; this.locator = locator; this.severity = severity; + this.state = state; this.stackTrace = stackTrace; } @@ -71,6 +77,14 @@ public class LogMessage extends I18nMessage { return this.severity; } + public LogMessageState getState() { + return this.state; + } + + public void setState(LogMessageState state) { + this.state = state; + } + public LogMessage withException(Throwable t) { this.stackTrace = ExceptionHelper.formatException(t); return this; @@ -100,10 +114,12 @@ public class LogMessage extends I18nMessage { jsonObject.addProperty(Json.KEY, getKey()); jsonObject.addProperty(Json.MESSAGE, formatMessage()); jsonObject.addProperty(Json.SEVERITY, this.severity.name()); + jsonObject.addProperty(Json.STATE, this.state.name()); jsonObject.addProperty(Json.USERNAME, this.username); jsonObject.addProperty(Json.REALM, this.realm); jsonObject.addProperty(Json.LOCATOR, this.locator.toString()); - jsonObject.addProperty(Json.EXCEPTION, this.stackTrace); + if (this.stackTrace != null) + jsonObject.addProperty(Json.EXCEPTION, this.stackTrace); JsonObject values = new JsonObject(); for (String key : getValues().stringPropertyNames()) { values.addProperty(key, getValues().getProperty(key)); @@ -113,37 +129,48 @@ public class LogMessage extends I18nMessage { return jsonObject; } - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + ((this.locator == null) ? 0 : this.locator.hashCode()); - result = prime * result + ((this.realm == null) ? 0 : this.realm.hashCode()); - result = prime * result + ((this.severity == null) ? 0 : this.severity.hashCode()); - return result; + public static LogMessage fromJson(JsonObject messageJ) { + + String id = messageJ.get(Json.ID).getAsString(); + ZonedDateTime zonedDateTime = ISO8601.parseToZdt(messageJ.get(Json.DATE).getAsString()); + String realm = messageJ.get(Json.REALM).getAsString(); + String username = messageJ.get(Json.USERNAME).getAsString(); + Locator locator = Locator.valueOf(messageJ.get(Json.LOCATOR).getAsString()); + LogSeverity severity = LogSeverity.valueOf(messageJ.get(Json.SEVERITY).getAsString()); + LogMessageState state = LogMessageState.valueOf(messageJ.get(Json.STATE).getAsString()); + String key = messageJ.get(Json.KEY).getAsString(); + String message = messageJ.get(Json.MESSAGE).getAsString(); + String stackTrace = messageJ.has(Json.EXCEPTION) ? messageJ.get(Json.EXCEPTION).getAsString() : ""; + + Properties properties = new Properties(); + if (messageJ.has(Json.VALUES)) { + JsonObject valuesJ = messageJ.getAsJsonObject(Json.VALUES); + for (String propertyName : valuesJ.keySet()) { + properties.setProperty(propertyName, valuesJ.get(propertyName).getAsString()); + } + } + + return new LogMessage(id, zonedDateTime, realm, username, locator, severity, state, key, properties, message, + stackTrace); } @Override - public boolean equals(Object obj) { - if (this == obj) + public boolean equals(Object o) { + if (this == o) return true; - if (!super.equals(obj)) + if (o == null || getClass() != o.getClass()) return false; - if (getClass() != obj.getClass()) + if (!super.equals(o)) return false; - LogMessage other = (LogMessage) obj; - if (this.locator == null) { - if (other.locator != null) - return false; - } else if (!this.locator.equals(other.locator)) - return false; - if (this.realm == null) { - if (other.realm != null) - return false; - } else if (!this.realm.equals(other.realm)) - return false; - if (this.severity != other.severity) - return false; - return true; + + LogMessage that = (LogMessage) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (id != null ? id.hashCode() : 0); + return result; } } diff --git a/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessageState.java b/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessageState.java new file mode 100644 index 000000000..e5bff1542 --- /dev/null +++ b/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/LogMessageState.java @@ -0,0 +1,7 @@ +package li.strolch.handler.operationslog; + +public enum LogMessageState { + Active, + Inactive, + Information +} diff --git a/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/OperationsLog.java b/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/OperationsLog.java index c2e53f2fc..4696d5ca1 100644 --- a/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/OperationsLog.java +++ b/li.strolch.agent/src/main/java/li/strolch/handler/operationslog/OperationsLog.java @@ -5,6 +5,7 @@ import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT; import java.util.*; import java.util.concurrent.ExecutorService; +import java.util.stream.Collectors; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchAgent; @@ -94,6 +95,72 @@ public class OperationsLog extends StrolchComponent { this.executorService.submit(() -> persist(realm, logMessage, messagesToRemove)); } + public void removeMessage(LogMessage message) { + + String realmName = message.getRealm(); + LinkedHashMap> byLocator = this.logMessagesByLocator.get(realmName); + if (byLocator != null) + byLocator.remove(message.getLocator()); + + List messages = this.logMessagesByRealmAndId.get(realmName); + if (messages != null) { + messages.remove(message); + + // persist changes for non-transient realms + StrolchRealm realm = getContainer().getRealm(realmName); + if (!realm.getMode().isTransient()) + this.executorService.submit(() -> persist(realm, null, Collections.singletonList(message))); + } + } + + public synchronized void removeMessages(Collection logMessages) { + + Map> messagesByRealm = logMessages.stream() + .collect(Collectors.groupingBy(LogMessage::getRealm)); + + messagesByRealm.forEach((realmName, messages) -> { + + LinkedHashMap> byLocator = this.logMessagesByLocator.get(realmName); + if (byLocator != null) + messages.forEach(logMessage -> byLocator.remove(logMessage.getLocator())); + + List byRealm = this.logMessagesByRealmAndId.get(realmName); + if (byRealm != null) { + messages.removeIf(logMessage -> !byRealm.remove(logMessage)); + + // persist changes for non-transient realms + StrolchRealm realm = getContainer().getRealm(realmName); + if (!realm.getMode().isTransient()) + this.executorService.submit(() -> persist(realm, null, messages)); + } + }); + } + + public synchronized void updateState(String realmName, Locator locator, LogMessageState state) { + getMessagesFor(realmName, locator).ifPresent(logMessages -> { + logMessages.forEach(logMessage -> logMessage.setState(state)); + + StrolchRealm realm = getContainer().getRealm(realmName); + if (!realm.getMode().isTransient()) { + persist(realm, logMessages); + } + }); + } + + public synchronized void updateState(String realmName, String id, LogMessageState state) { + List logMessages = this.logMessagesByRealmAndId.get(realmName); + for (LogMessage logMessage : logMessages) { + if (logMessage.getId().equals(id)) { + logMessage.setState(state); + + StrolchRealm realm = getContainer().getRealm(realmName); + if (!realm.getMode().isTransient()) { + persist(realm, logMessages); + } + } + } + } + private List pruneMessages(List logMessages) { if (logMessages.size() < this.maxMessages) return Collections.emptyList(); @@ -124,7 +191,8 @@ public class OperationsLog extends StrolchComponent { LogMessageDao logMessageDao = tx.getPersistenceHandler().getLogMessageDao(tx); if (messagesToRemove != null && !messagesToRemove.isEmpty()) logMessageDao.removeAll(messagesToRemove); - logMessageDao.save(logMessage); + if (logMessage != null) + logMessageDao.save(logMessage); tx.commitOnClose(); } }); @@ -134,7 +202,31 @@ public class OperationsLog extends StrolchComponent { this.logMessagesByRealmAndId.computeIfAbsent(realm.getRealm(), r -> new ArrayList<>()) .add(new LogMessage(realm.getRealm(), SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info, - ResourceBundle.getBundle("strolch-agent"), "operationsLog.persist.failed") // + LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), + "operationsLog.persist.failed") // + .value("reason", e.getMessage()) // + .withException(e)); + } + } + } + + private void persist(StrolchRealm realm, Collection logMessages) { + try { + runAsAgent(ctx -> { + try (StrolchTransaction tx = realm.openTx(ctx.getCertificate(), getClass(), false)) { + LogMessageDao logMessageDao = tx.getPersistenceHandler().getLogMessageDao(tx); + logMessageDao.updateStates(logMessages); + tx.commitOnClose(); + } + }); + } catch (Exception e) { + logger.error("Failed to persist operations logs!", e); + synchronized (this) { + this.logMessagesByRealmAndId.computeIfAbsent(realm.getRealm(), r -> new ArrayList<>()) + .add(new LogMessage(realm.getRealm(), SYSTEM_USER_AGENT, + Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), LogSeverity.Info, + LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), + "operationsLog.persist.failed") // .value("reason", e.getMessage()) // .withException(e)); } @@ -170,9 +262,4 @@ public class OperationsLog extends StrolchComponent { } }; } - - public void removeMessage(String realm, LogMessage message) { - List messages = this.logMessagesByRealmAndId.get(realm); - messages.remove(message); - } } diff --git a/li.strolch.agent/src/main/java/li/strolch/job/StrolchJob.java b/li.strolch.agent/src/main/java/li/strolch/job/StrolchJob.java index 3b54073fb..57e4ab259 100644 --- a/li.strolch.agent/src/main/java/li/strolch/job/StrolchJob.java +++ b/li.strolch.agent/src/main/java/li/strolch/job/StrolchJob.java @@ -21,6 +21,7 @@ import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchComponent; import li.strolch.agent.api.StrolchRealm; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -266,7 +267,7 @@ public abstract class StrolchJob implements Runnable, Restrictable { operationsLog.addMessage( new LogMessage(this.realmName == null ? StrolchConstants.DEFAULT_REALM : this.realmName, SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "strolch-agent", StrolchAgent.getUniqueId()), - LogSeverity.Exception, ResourceBundle.getBundle("strolch-agent"), "strolchjob.failed") + LogSeverity.Exception, LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "strolchjob.failed") .withException(e).value("jobName", getClass().getName()).value("reason", e)); } } diff --git a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java index b06cff2ce..ca6fb22f2 100644 --- a/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java +++ b/li.strolch.agent/src/main/java/li/strolch/persistence/api/AbstractTransaction.java @@ -32,6 +32,7 @@ import li.strolch.exception.StrolchAccessDeniedException; import li.strolch.exception.StrolchException; import li.strolch.exception.StrolchModelException; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.*; @@ -1692,7 +1693,7 @@ public abstract class AbstractTransaction implements StrolchTransaction { OperationsLog operationsLog = container.getComponent(OperationsLog.class); operationsLog.addMessage(new LogMessage(this.realm.getRealm(), this.certificate.getUsername(), Locator.valueOf(AGENT, "tx", this.action, getUniqueId()), LogSeverity.Exception, - ResourceBundle.getBundle("strolch-agent"), "agent.tx.failed").withException(e).value("reason", e)); + LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.tx.failed").withException(e).value("reason", e)); } String msg = "Strolch Transaction for realm {0} failed due to {1}\n{2}"; //$NON-NLS-1$ diff --git a/li.strolch.agent/src/main/java/li/strolch/persistence/api/LogMessageDao.java b/li.strolch.agent/src/main/java/li/strolch/persistence/api/LogMessageDao.java index 69c0e6f3b..f1a68b6f0 100644 --- a/li.strolch.agent/src/main/java/li/strolch/persistence/api/LogMessageDao.java +++ b/li.strolch.agent/src/main/java/li/strolch/persistence/api/LogMessageDao.java @@ -15,8 +15,8 @@ */ package li.strolch.persistence.api; +import java.util.Collection; import java.util.List; -import java.util.Map; import li.strolch.handler.operationslog.LogMessage; @@ -31,6 +31,10 @@ public interface LogMessageDao { void saveAll(List logMessages); + void updateState(LogMessage logMessage); + + void updateStates(Collection logMessages); + void remove(LogMessage logMessage); void removeAll(List logMessages); diff --git a/li.strolch.agent/src/main/java/li/strolch/service/api/DefaultServiceHandler.java b/li.strolch.agent/src/main/java/li/strolch/service/api/DefaultServiceHandler.java index b796d3c79..929f1af66 100644 --- a/li.strolch.agent/src/main/java/li/strolch/service/api/DefaultServiceHandler.java +++ b/li.strolch.agent/src/main/java/li/strolch/service/api/DefaultServiceHandler.java @@ -28,6 +28,7 @@ import li.strolch.agent.api.StrolchComponent; import li.strolch.exception.StrolchAccessDeniedException; import li.strolch.exception.StrolchException; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -219,11 +220,11 @@ public class DefaultServiceHandler extends StrolchComponent implements ServiceHa ResourceBundle bundle = ResourceBundle.getBundle("strolch-agent"); if (throwable == null) { logMessage = new LogMessage(realmName, username, Locator.valueOf(AGENT, svcName, getUniqueId()), - LogSeverity.Exception, bundle, "agent.service.failed").value("service", svcName) + LogSeverity.Exception, LogMessageState.Information, bundle, "agent.service.failed").value("service", svcName) .value("reason", reason); } else { logMessage = new LogMessage(realmName, username, Locator.valueOf(AGENT, svcName, getUniqueId()), - LogSeverity.Exception, bundle, "agent.service.failed.ex").withException(throwable) + LogSeverity.Exception, LogMessageState.Information, bundle, "agent.service.failed.ex").withException(throwable) .value("service", svcName).value("reason", reason).value("exception", throwable); } diff --git a/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageJsonParser.java b/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageJsonParser.java index d6d8b2566..f208f19e3 100644 --- a/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageJsonParser.java +++ b/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageJsonParser.java @@ -1,24 +1,34 @@ package li.strolch.model.i18n; +import java.util.Properties; +import java.util.Set; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import li.strolch.model.Tags; import li.strolch.utils.I18nMessage; public class I18nMessageJsonParser { - public static I18nMessage parse(JsonObject messageJ) { + public I18nMessage parse(JsonObject messageJ) { - // TODO + String key = messageJ.get(Tags.Json.KEY).getAsString(); + String message = messageJ.get(Tags.Json.MESSAGE).getAsString(); -// String key = json.addProperty("key", message.getKey()); -// json.addProperty("message", message.getMessage()); -// -// Properties values = message.getValues(); -// if (!values.isEmpty()) { -// JsonObject valuesJ = new JsonObject(); -// values.stringPropertyNames().forEach(key -> valuesJ.addProperty(key, values.getProperty(key))); -// json.add("values", valuesJ); -// } + Properties properties = new Properties(); + if (messageJ.has(Tags.Json.VALUES)) { + JsonArray valuesJ = messageJ.getAsJsonArray(Tags.Json.VALUES); + for (JsonElement jsonElement : valuesJ) { + JsonObject valueJ = jsonElement.getAsJsonObject(); - return null; + Set keys = valueJ.keySet(); + for (String propertyName : keys) { + properties.setProperty(propertyName, valueJ.get(propertyName).getAsString()); + } + } + } + + return new I18nMessage(key, properties, message); } } diff --git a/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageToJsonVisitor.java b/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageToJsonVisitor.java index a50b0a5f5..8ccf5ff52 100644 --- a/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageToJsonVisitor.java +++ b/li.strolch.model/src/main/java/li/strolch/model/i18n/I18nMessageToJsonVisitor.java @@ -3,6 +3,7 @@ package li.strolch.model.i18n; import java.util.Properties; import com.google.gson.JsonObject; +import li.strolch.model.Tags; import li.strolch.utils.I18nMessage; import li.strolch.utils.I18nMessageVisitor; @@ -12,14 +13,14 @@ public class I18nMessageToJsonVisitor implements I18nMessageVisitor public JsonObject visit(I18nMessage message) { JsonObject json = new JsonObject(); - json.addProperty("key", message.getKey()); - json.addProperty("message", message.getMessage()); + json.addProperty(Tags.Json.KEY, message.getKey()); + json.addProperty(Tags.Json.MESSAGE, message.getMessage()); Properties values = message.getValues(); if (!values.isEmpty()) { JsonObject valuesJ = new JsonObject(); values.stringPropertyNames().forEach(key -> valuesJ.addProperty(key, values.getProperty(key))); - json.add("values", valuesJ); + json.add(Tags.Json.VALUES, valuesJ); } return json; diff --git a/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlLogMessageDao.java b/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlLogMessageDao.java index f75a117a1..6706eb25a 100644 --- a/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlLogMessageDao.java +++ b/li.strolch.persistence.postgresql/src/main/java/li/strolch/persistence/postgresql/PostgreSqlLogMessageDao.java @@ -12,6 +12,7 @@ import java.time.ZonedDateTime; import java.util.*; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.model.Locator; import li.strolch.persistence.api.LogMessageDao; @@ -28,18 +29,21 @@ public class PostgreSqlLogMessageDao implements LogMessageDao { private static final String KEY = "key"; private static final String MESSAGE = "message"; private static final String STACK_TRACE = "stacktrace"; + private static final String STATE = "state"; - private static final String FIELDS = commaSeparated(ID, REALM, DATE_TIME, USERNAME, SEVERITY, LOCATOR, KEY, MESSAGE, - STACK_TRACE); + private static final String FIELDS = commaSeparated(ID, REALM, DATE_TIME, USERNAME, SEVERITY, STATE, LOCATOR, KEY, + MESSAGE, STACK_TRACE); private static final String queryByRealmMaxSql = "select " + FIELDS + " from operations_log where realm = ? order by id desc limit ?"; private static final String queryValuesSql = "select key, value from operations_log_values where id = ?"; - private static final String insertLogMessageSql = - "insert into operations_log (" + FIELDS + ") values (?, ?, ?, ?, ?::log_severity_type, ?, ?, ?, ?)"; + private static final String insertLogMessageSql = "insert into operations_log (" + FIELDS + + ") values (?, ?, ?, ?, ?::log_severity_type, ?::log_state_type, ?, ?, ?, ?)"; private static final String insertValuesSql = "insert into operations_log_values (id, key, value) values (?, ?, ?)"; + private static final String updateLogMessageStateSql = "update operations_log set state = ?::log_state_type where id = ?"; + private static final String removeSql = "delete from operations_log where id = ?"; private static final String removeValuesSql = "delete from operations_log_values where id = ?"; @@ -106,11 +110,36 @@ public class PostgreSqlLogMessageDao implements LogMessageDao { @Override public void saveAll(List logMessages) { - for (LogMessage logMessage : logMessages) { - save(logMessage); + logMessages.forEach(this::save); + } + + @Override + public void updateState(LogMessage logMessage) { + try (PreparedStatement ps = this.tx.getConnection().prepareStatement(updateLogMessageStateSql)) { + + // update state + ps.setString(1, logMessage.getState().name()); + ps.setString(2, logMessage.getId()); + + int count = ps.executeUpdate(); + if (count != 1) { + throw new StrolchPersistenceException(MessageFormat + .format("Expected to update 1 log_message record, but updated {0} for LogMessage {2}", count, + logMessage.getId())); //$NON-NLS-1$ + } + + } catch (SQLException e) { + throw new StrolchPersistenceException(MessageFormat + .format("Failed to update LogMessage state {0} due to {1}", logMessage.getId(), //$NON-NLS-1$ + e.getLocalizedMessage()), e); } } + @Override + public void updateStates(Collection logMessages) { + logMessages.forEach(this::updateState); + } + @Override public void remove(LogMessage logMessage) { try (PreparedStatement removeStatement = this.tx.getConnection().prepareStatement(removeSql); @@ -237,10 +266,11 @@ public class PostgreSqlLogMessageDao implements LogMessageDao { // 3 dateTime = ?, // 4 username = ?, // 5 severity = ?, - // 6 locator = ?, - // 7 key = ?, - // 8 message = ?, - // 9 stacktrace = ?, + // 6 state = ? + // 7 locator = ?, + // 8 key = ?, + // 9 message = ?, + // 10 stacktrace = ?, ps.setString(1, logMessage.getId()); ps.setString(2, logMessage.getRealm()); @@ -248,10 +278,11 @@ public class PostgreSqlLogMessageDao implements LogMessageDao { Calendar.getInstance()); ps.setString(4, logMessage.getUsername()); ps.setString(5, logMessage.getSeverity().name()); - ps.setString(6, logMessage.getLocator().toString()); - ps.setString(7, logMessage.getKey()); - ps.setString(8, logMessage.getMessage()); - ps.setString(9, logMessage.getStackTrace()); + ps.setString(6, logMessage.getState().name()); + ps.setString(7, logMessage.getLocator().toString()); + ps.setString(8, logMessage.getKey()); + ps.setString(9, logMessage.getMessage()); + ps.setString(10, logMessage.getStackTrace()); } private LogMessage logMessageFrom(ResultSet resultSet, ResultSet valuesResult) throws SQLException { @@ -261,10 +292,11 @@ public class PostgreSqlLogMessageDao implements LogMessageDao { ZonedDateTime dateTime = ZonedDateTime.ofInstant(resultSet.getTimestamp(3).toInstant(), ZoneId.systemDefault()); String username = resultSet.getString(4); LogSeverity severity = LogSeverity.valueOf(resultSet.getString(5)); - Locator locator = Locator.valueOf(resultSet.getString(6)); - String key = resultSet.getString(7); - String message = resultSet.getString(8); - String exception = resultSet.getString(9); + LogMessageState state = LogMessageState.valueOf(resultSet.getString(6)); + Locator locator = Locator.valueOf(resultSet.getString(7)); + String key = resultSet.getString(8); + String message = resultSet.getString(9); + String exception = resultSet.getString(10); Properties properties = new Properties(); while (valuesResult.next()) { @@ -273,6 +305,7 @@ public class PostgreSqlLogMessageDao implements LogMessageDao { properties.setProperty(valueK, valueV); } - return new LogMessage(id, dateTime, realm, username, locator, severity, key, properties, message, exception); + return new LogMessage(id, dateTime, realm, username, locator, severity, state, key, properties, message, + exception); } } diff --git a/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_drop.sql b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_drop.sql new file mode 100644 index 000000000..a255fc971 --- /dev/null +++ b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_drop.sql @@ -0,0 +1,16 @@ + +DROP TABLE IF EXISTS resources; +DROP TABLE IF EXISTS orders; +DROP TABLE IF EXISTS activities; + +DROP TABLE IF EXISTS audits; + +DROP TABLE IF EXISTS operations_log; +DROP TABLE IF EXISTS operations_log_values; + +DROP TABLE IF EXISTS db_version; + +DROP TYPE IF EXISTS order_state; +DROP TYPE IF EXISTS access_type; +DROP TYPE IF EXISTS log_severity_type; +DROP TYPE IF EXISTS log_state_type; diff --git a/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_initial.sql b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_initial.sql new file mode 100644 index 000000000..de4bea96e --- /dev/null +++ b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_initial.sql @@ -0,0 +1,207 @@ + +-- DB_VERSION +CREATE TABLE IF NOT EXISTS db_version ( + id serial primary key not null, + app varchar(255) not null, + version varchar(255) not null, + description varchar(255) not null, + created timestamp with time zone not null +); + +-- RESOURCES +CREATE TABLE IF NOT EXISTS resources ( + id varchar(255) not null, + version integer not null, + created_by varchar(255) not null, + created_at timestamp with time zone not null, + updated_at timestamp with time zone not null, + deleted boolean not null, + latest boolean not null, + name varchar(255) not null, + type varchar(255) not null, + asxml xml, + asjson json, + + PRIMARY KEY (id, version) +); + +-- ORDERS +CREATE TYPE order_state AS ENUM ('CREATED', 'PLANNING', 'PLANNED', 'EXECUTION', 'STOPPED', 'WARNING', 'ERROR', 'EXECUTED', 'CLOSED'); + + +CREATE TABLE IF NOT EXISTS orders ( + id varchar(255) not null, + version integer not null, + created_by varchar(255) not null, + created_at timestamp with time zone not null, + updated_at timestamp with time zone not null, + deleted boolean, + latest boolean not null, + name varchar(255), + type varchar(255), + state order_state, + date timestamp with time zone, + asxml xml, + asjson json, + + PRIMARY KEY (id, version) +); + +-- ACTIVITIES +CREATE TABLE IF NOT EXISTS activities ( + id varchar(255) not null, + version integer not null, + created_by varchar(255) not null, + created_at timestamp with time zone not null, + updated_at timestamp with time zone not null, + deleted boolean not null, + latest boolean not null, + name varchar(255) not null, + type varchar(255) not null, + state order_state, + asxml xml, + asjson json, + + PRIMARY KEY (id, version) +); + +-- AUDITS +CREATE TYPE access_type AS ENUM ('READ', 'CREATE', 'UPDATE', 'DELETE'); +CREATE TABLE IF NOT EXISTS audits ( + id bigint PRIMARY KEY, + username varchar(255) NOT NULL, + firstname varchar(255) NOT NULL, + lastname varchar(255) NOT NULL, + date timestamp with time zone NOT NULL, + + element_type varchar(255) NOT NULL, + element_sub_type varchar(255) NOT NULL, + element_accessed varchar(255) NOT NULL, + new_version timestamp with time zone, + + action varchar(255) NOT NULL, + access_type access_type NOT NULL +); + +-- Operations Log +CREATE TYPE log_severity_type AS ENUM ('Info', 'Notification', 'Warning', 'Error', 'Exception'); +CREATE TYPE log_state_type AS ENUM ('Active', 'Inactive', 'Information'); + +CREATE TABLE IF NOT EXISTS operations_log ( + id varchar(255) PRIMARY KEY, + realm varchar(255), + dateTime timestamp with time zone, + username varchar(255), + severity log_severity_type, + state log_state_type, + locator varchar(1024), + key varchar(255), + message text, + stacktrace text +); + +CREATE TABLE IF NOT EXISTS operations_log_values ( + id varchar(255), + key varchar(255), + value text +); + +-- set version +INSERT INTO db_version + (version, app, description, created) +values( + '0.1.0', + 'strolch', + 'Initial schema version', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.2.0', + 'strolch', + 'Added new table for audits', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.2.1', + 'strolch', + 'Added new column app to table table version', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.3.0', + 'strolch', + 'Added new column element_sub_type to table audits', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.4.0', + 'strolch', + 'Added new table activities', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.5.0', + 'strolch', + 'Added versioning to root elements', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.5.1', + 'strolch', + 'Added state column to activity, and added new states', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.6.0', + 'strolch', + 'Added json column to all tables', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.7.0', + 'strolch', + 'Added persisting of operations log', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.8.0', + 'strolch', + 'Added updated_at column to all tables', + CURRENT_TIMESTAMP +); + +INSERT INTO db_version + (version, app, description, created) +values( + '0.9.0', + 'strolch', + 'Added log_state column to operations_log', + CURRENT_TIMESTAMP +); diff --git a/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_migration.sql b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_migration.sql new file mode 100644 index 000000000..2e1c4b357 --- /dev/null +++ b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_schema_0.9.0_migration.sql @@ -0,0 +1,26 @@ + +-- add new type +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'log_state_type') THEN + CREATE TYPE log_state_type AS ENUM ('Active', 'Inactive', 'Information'); + END IF; +END$$; + +-- add version columns +ALTER TABLE operations_log ADD COLUMN state log_state_type; + +-- set initial values for new columns +UPDATE operations_log SET state = 'Information' where state IS NULL; + +-- make columns not null +ALTER TABLE operations_log ALTER COLUMN state SET NOT NULL; + +INSERT INTO db_version + (version, app, description, created) +values( + '0.9.0', + 'strolch', + 'Added log_state column to operations_log', + CURRENT_TIMESTAMP +); \ No newline at end of file diff --git a/li.strolch.persistence.postgresql/src/main/resources/strolch_db_version.properties b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_version.properties index e9e0496ab..5aab0863d 100644 --- a/li.strolch.persistence.postgresql/src/main/resources/strolch_db_version.properties +++ b/li.strolch.persistence.postgresql/src/main/resources/strolch_db_version.properties @@ -1,2 +1,2 @@ # Property file defining what the currently expected version is supposed to be -db_version=0.8.0 +db_version=0.9.0 diff --git a/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/XmlLogMessageDao.java b/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/XmlLogMessageDao.java index 2c068c725..297266578 100644 --- a/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/XmlLogMessageDao.java +++ b/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/XmlLogMessageDao.java @@ -1,5 +1,6 @@ package li.strolch.persistence.xml; +import java.util.Collection; import java.util.List; import li.strolch.handler.operationslog.LogMessage; @@ -38,6 +39,16 @@ public class XmlLogMessageDao implements LogMessageDao { this.tx.getObjectDao().addAll(logMessages); } + @Override + public void updateState(LogMessage logMessage) { + this.tx.getObjectDao().update(logMessage); + } + + @Override + public void updateStates(Collection logMessages) { + logMessages.forEach(logMessage -> this.tx.getObjectDao().update(logMessage)); + } + @Override public void remove(LogMessage logMessage) { this.tx.getObjectDao().remove(logMessage); diff --git a/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageSaxReader.java b/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageSaxReader.java index e165214d4..6e6e09d58 100644 --- a/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageSaxReader.java +++ b/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageSaxReader.java @@ -21,6 +21,7 @@ import java.util.Properties; import java.util.function.Consumer; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.model.Locator; import li.strolch.model.Tags; @@ -42,6 +43,7 @@ public class LogMessageSaxReader extends DefaultHandler { private String username; private Locator locator; private LogSeverity severity; + private LogMessageState state; private String key; private Properties properties; private String message; @@ -70,6 +72,7 @@ public class LogMessageSaxReader extends DefaultHandler { case Tags.KEY: case Tags.MESSAGE: case Tags.EXCEPTION: + case Tags.STATE: this.sb = new StringBuilder(); break; @@ -96,8 +99,11 @@ public class LogMessageSaxReader extends DefaultHandler { switch (qName) { case Tags.LOG_MESSAGE: + if (this.state == null) + this.state = LogMessageState.Information; + LogMessage logMessage = new LogMessage(this.id, this.dateTime, this.realm, this.username, this.locator, - this.severity, this.key, this.properties, this.message, this.exception); + this.severity, this.state, this.key, this.properties, this.message, this.exception); this.logMessageConsumer.accept(logMessage); break; @@ -116,6 +122,11 @@ public class LogMessageSaxReader extends DefaultHandler { this.sb = null; break; + case Tags.STATE: + this.state = LogMessageState.valueOf(this.sb.toString()); + this.sb = null; + break; + case Tags.KEY: this.key = this.sb.toString(); this.sb = null; diff --git a/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageToSaxWriterVisitor.java b/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageToSaxWriterVisitor.java index 46e6781a6..be2f6a45e 100644 --- a/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageToSaxWriterVisitor.java +++ b/li.strolch.persistence.xml/src/main/java/li/strolch/persistence/xml/model/LogMessageToSaxWriterVisitor.java @@ -44,6 +44,7 @@ public class LogMessageToSaxWriterVisitor { writeElem(Tags.MESSAGE, logMessage.getMessage()); writeElem(Tags.SEVERITY, logMessage.getSeverity().name()); + writeElem(Tags.STATE, logMessage.getState().name()); writeElem(Tags.USERNAME, logMessage.getUsername()); writeElem(Tags.LOCATOR, logMessage.getLocator().toString()); writeElem(Tags.KEY, logMessage.getKey()); diff --git a/li.strolch.service/src/main/java/li/strolch/execution/Controller.java b/li.strolch.service/src/main/java/li/strolch/execution/Controller.java index fe5a56d3a..2d7579b4e 100644 --- a/li.strolch.service/src/main/java/li/strolch/execution/Controller.java +++ b/li.strolch.service/src/main/java/li/strolch/execution/Controller.java @@ -13,6 +13,7 @@ import li.strolch.agent.api.StrolchRealm; import li.strolch.execution.command.*; import li.strolch.execution.policy.ExecutionPolicy; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -300,7 +301,7 @@ public class Controller { if (this.container.hasComponent(OperationsLog.class)) { this.container.getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error") .withException(e).value("reason", e)); } } @@ -323,7 +324,7 @@ public class Controller { if (this.container.hasComponent(OperationsLog.class)) { this.container.getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning") .withException(e).value("reason", e)); } } diff --git a/li.strolch.service/src/main/java/li/strolch/execution/EventBasedExecutionHandler.java b/li.strolch.service/src/main/java/li/strolch/execution/EventBasedExecutionHandler.java index 8a9842e13..fdbd2585b 100644 --- a/li.strolch.service/src/main/java/li/strolch/execution/EventBasedExecutionHandler.java +++ b/li.strolch.service/src/main/java/li/strolch/execution/EventBasedExecutionHandler.java @@ -13,6 +13,7 @@ import li.strolch.agent.api.StrolchRealm; import li.strolch.execution.command.ArchiveActivityCommand; import li.strolch.execution.policy.ExecutionPolicy; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.*; @@ -293,7 +294,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler { if (getContainer().hasComponent(OperationsLog.class)) { getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, controller.getLocator(), LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.execution") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.execution") .withException(e).value("reason", e)); } } @@ -315,7 +316,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler { if (getContainer().hasComponent(OperationsLog.class)) { getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") .withException(e).value("reason", e)); } } @@ -337,7 +338,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler { if (getContainer().hasComponent(OperationsLog.class)) { getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.stopped") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.stopped") .withException(e).value("reason", e)); } } @@ -359,7 +360,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler { if (getContainer().hasComponent(OperationsLog.class)) { getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.error") .withException(e).value("reason", e)); } } @@ -381,7 +382,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler { if (getContainer().hasComponent(OperationsLog.class)) { getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.warning") .withException(e).value("reason", e)); } } @@ -407,7 +408,7 @@ public class EventBasedExecutionHandler extends ExecutionHandler { if (getContainer().hasComponent(OperationsLog.class)) { getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, activity.getLocator(), LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.archive") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.archive") .withException(e).value("reason", e)); } } diff --git a/li.strolch.service/src/main/java/li/strolch/execution/SimpleDurationExecutionTimer.java b/li.strolch.service/src/main/java/li/strolch/execution/SimpleDurationExecutionTimer.java index 52cf1803f..d214de741 100644 --- a/li.strolch.service/src/main/java/li/strolch/execution/SimpleDurationExecutionTimer.java +++ b/li.strolch.service/src/main/java/li/strolch/execution/SimpleDurationExecutionTimer.java @@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchAgent; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -88,7 +89,7 @@ public class SimpleDurationExecutionTimer implements DelayedExecutionTimer { if (this.agent.getContainer().hasComponent(OperationsLog.class)) { this.agent.getContainer().getComponent(OperationsLog.class).addMessage( new LogMessage(realm, SYSTEM_USER_AGENT, locator, LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") .withException(e).value("reason", e)); } } diff --git a/li.strolch.service/src/main/java/li/strolch/execution/policy/ToErrorReservationExecution.java b/li.strolch.service/src/main/java/li/strolch/execution/policy/ToErrorReservationExecution.java index 310607891..265aae0fe 100644 --- a/li.strolch.service/src/main/java/li/strolch/execution/policy/ToErrorReservationExecution.java +++ b/li.strolch.service/src/main/java/li/strolch/execution/policy/ToErrorReservationExecution.java @@ -5,6 +5,7 @@ import static li.strolch.runtime.StrolchConstants.PolicyConstants.TYPE_RESERVE; import java.util.ResourceBundle; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.model.State; import li.strolch.model.activity.Action; @@ -42,7 +43,7 @@ public class ToErrorReservationExecution extends ReservationExecution { if (action.getType().equals(TYPE_RESERVE) && isReserved(tx(), action)) { setActionState(action, State.EXECUTION); toError(new LogMessage(tx().getRealmName(), tx().getCertificate().getUsername(), action.getLocator(), - LogSeverity.Error, ResourceBundle.getBundle("strolch-service"), + LogSeverity.Error, LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.policy.reservation.alreadyReserved") .value("resourceLoc", action.getResourceLocator().toString())); } else { diff --git a/li.strolch.service/src/main/java/li/strolch/migrations/Migrations.java b/li.strolch.service/src/main/java/li/strolch/migrations/Migrations.java index e28332acd..c7026f2e0 100644 --- a/li.strolch.service/src/main/java/li/strolch/migrations/Migrations.java +++ b/li.strolch.service/src/main/java/li/strolch/migrations/Migrations.java @@ -26,6 +26,7 @@ import java.util.Map.Entry; import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchAgent; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -140,7 +141,7 @@ public class Migrations { List list = migrationsRan.getList(realm); for (Version version : list) { LogMessage logMessage = new LogMessage(realm, SYSTEM_USER_AGENT, - locator.append(StrolchAgent.getUniqueId()), LogSeverity.Info, + locator.append(StrolchAgent.getUniqueId()), LogSeverity.Info, LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.migrations.version") .value("version", version.toString()); operationsLog.addMessage(logMessage); diff --git a/li.strolch.service/src/main/java/li/strolch/migrations/MigrationsHandler.java b/li.strolch.service/src/main/java/li/strolch/migrations/MigrationsHandler.java index 3527ecf35..ab34db7b8 100644 --- a/li.strolch.service/src/main/java/li/strolch/migrations/MigrationsHandler.java +++ b/li.strolch.service/src/main/java/li/strolch/migrations/MigrationsHandler.java @@ -28,6 +28,7 @@ import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchComponent; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Tags; @@ -204,7 +205,7 @@ public class MigrationsHandler extends StrolchComponent { if (getContainer().hasComponent(OperationsLog.class)) { getComponent(OperationsLog.class).addMessage(new LogMessage(Tags.AGENT, SYSTEM_USER_AGENT, getLocator().append(StrolchAgent.getUniqueId()), LogSeverity.Exception, - ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") + LogMessageState.Information, ResourceBundle.getBundle("strolch-service"), "execution.handler.failed.executed") .withException(e).value("reason", e)); } } diff --git a/li.strolch.testbase/src/main/java/li/strolch/testbase/runtime/LogMessagesTestRunner.java b/li.strolch.testbase/src/main/java/li/strolch/testbase/runtime/LogMessagesTestRunner.java index c4aedee53..ec8931106 100644 --- a/li.strolch.testbase/src/main/java/li/strolch/testbase/runtime/LogMessagesTestRunner.java +++ b/li.strolch.testbase/src/main/java/li/strolch/testbase/runtime/LogMessagesTestRunner.java @@ -3,8 +3,9 @@ package li.strolch.testbase.runtime; import static java.util.stream.Collectors.toList; import static li.strolch.model.Tags.AGENT; import static li.strolch.runtime.StrolchConstants.SYSTEM_USER_AGENT; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; +import java.util.Arrays; import java.util.List; import java.util.ResourceBundle; @@ -12,6 +13,7 @@ import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchRealm; import li.strolch.handler.operationslog.LogMessage; +import li.strolch.handler.operationslog.LogMessageState; import li.strolch.handler.operationslog.LogSeverity; import li.strolch.handler.operationslog.OperationsLog; import li.strolch.model.Locator; @@ -45,7 +47,7 @@ public class LogMessagesTestRunner { ResourceBundle bundle = ResourceBundle.getBundle("li-strolch-testbase"); LogMessage logMessage = new LogMessage(this.realmName, SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "li.strolch.testbase", StrolchAgent.getUniqueId()), LogSeverity.Exception, - bundle, "test-message").withException(ex).value("reason", ex); + LogMessageState.Information, bundle, "test-message").withException(ex).value("reason", ex); this.operationsLog.addMessage(logMessage); // default is async persisting... @@ -83,7 +85,7 @@ public class LogMessagesTestRunner { for (int i = 0; i < MAX_MESSAGES * 2; i++) { LogMessage m = new LogMessage(this.realmName, SYSTEM_USER_AGENT, Locator.valueOf(AGENT, "li.strolch.testbase", StrolchAgent.getUniqueId()), - LogSeverity.Exception, bundle, "test-message"); + LogSeverity.Exception, LogMessageState.Information, bundle, "test-message"); this.operationsLog.addMessage(m); ids.add(m.getId()); } @@ -108,6 +110,49 @@ public class LogMessagesTestRunner { } } + // add a few more messages + LogMessage logMessage1 = new LogMessage(this.realmName, SYSTEM_USER_AGENT, + Locator.valueOf(AGENT, "test", "@1"), LogSeverity.Error, LogMessageState.Active, bundle, + "test-message"); + LogMessage logMessage2 = new LogMessage(this.realmName, SYSTEM_USER_AGENT, + Locator.valueOf(AGENT, "test", "@2"), LogSeverity.Error, LogMessageState.Active, bundle, + "test-message"); + LogMessage logMessage3 = new LogMessage(this.realmName, SYSTEM_USER_AGENT, + Locator.valueOf(AGENT, "test", "@3"), LogSeverity.Error, LogMessageState.Active, bundle, + "test-message"); + + this.operationsLog.addMessage(logMessage1); + this.operationsLog.addMessage(logMessage2); + this.operationsLog.addMessage(logMessage3); + + // update state of element + this.operationsLog.updateState(this.realmName, logMessage1.getLocator(), LogMessageState.Inactive); + assertEquals(LogMessageState.Inactive, logMessage1.getState()); + + this.operationsLog.updateState(this.realmName, logMessage1.getId(), LogMessageState.Active); + assertEquals(LogMessageState.Active, logMessage1.getState()); + + // now try and remove a single element + this.operationsLog.removeMessage(logMessage1); + assertFalse(this.operationsLog.getMessagesFor(this.realmName, logMessage1.getLocator()).isPresent()); + + // now remove bulk + List toRemove = Arrays.asList(logMessage2, logMessage3); + this.operationsLog.removeMessages(toRemove); + + // default is async persisting... + Thread.sleep(300L); + + // assert all are removed + try (StrolchTransaction tx = realm.openTx(this.certificate, "test", true)) { + LogMessageDao logMessageDao = tx.getPersistenceHandler().getLogMessageDao(tx); + List logMessageIds = logMessageDao.queryLatest(this.realmName, Integer.MAX_VALUE).stream() + .map(LogMessage::getId).sorted().collect(toList()); + assertFalse(logMessageIds.contains(logMessage1.getId())); + assertFalse(logMessageIds.contains(logMessage2.getId())); + assertFalse(logMessageIds.contains(logMessage3.getId())); + } + } catch (InterruptedException e) { throw new IllegalStateException("Interrupted!"); } diff --git a/li.strolch.utils/src/main/java/li/strolch/utils/I18nMessage.java b/li.strolch.utils/src/main/java/li/strolch/utils/I18nMessage.java index 838916f9e..7e574273f 100644 --- a/li.strolch.utils/src/main/java/li/strolch/utils/I18nMessage.java +++ b/li.strolch.utils/src/main/java/li/strolch/utils/I18nMessage.java @@ -15,17 +15,17 @@ public class I18nMessage { private static final Logger logger = LoggerFactory.getLogger(I18nMessage.class); - private ResourceBundle bundle; - private String key; - private Properties values; + private final String key; + private final Properties values; + private final ResourceBundle bundle; private String message; public I18nMessage(ResourceBundle bundle, String key) { DBC.INTERIM.assertNotNull("bundle must be set!", bundle); DBC.INTERIM.assertNotEmpty("key must be set!", key); - this.bundle = bundle; this.key = key; this.values = new Properties(); + this.bundle = bundle; } public I18nMessage(String key, Properties values, String message) { @@ -34,6 +34,7 @@ public class I18nMessage { this.key = key; this.values = values == null ? new Properties() : values; this.message = message; + this.bundle = null; } public String getKey() { @@ -62,6 +63,11 @@ public class I18nMessage { if (this.message != null) return this.message; + if (this.bundle == null) { + this.message = this.key; + return this.message; + } + try { String string = this.bundle.getString(this.key); this.message = StringHelper.replacePropertiesIn(this.values, EMPTY, string);