[Major] I18nMessage and LogMessage refactorings, added state, JSON parsers, etc.

This commit is contained in:
Robert von Burg 2020-05-26 17:37:57 +02:00
parent 006508ff2e
commit 76aff683ee
28 changed files with 608 additions and 104 deletions

View File

@ -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()));

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,7 @@
package li.strolch.handler.operationslog;
public enum LogMessageState {
Active,
Inactive,
Information
}

View File

@ -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<Locator, LinkedHashSet<LogMessage>> byLocator = this.logMessagesByLocator.get(realmName);
if (byLocator != null)
byLocator.remove(message.getLocator());
List<LogMessage> 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<LogMessage> logMessages) {
Map<String, List<LogMessage>> messagesByRealm = logMessages.stream()
.collect(Collectors.groupingBy(LogMessage::getRealm));
messagesByRealm.forEach((realmName, messages) -> {
LinkedHashMap<Locator, LinkedHashSet<LogMessage>> byLocator = this.logMessagesByLocator.get(realmName);
if (byLocator != null)
messages.forEach(logMessage -> byLocator.remove(logMessage.getLocator()));
List<LogMessage> 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<LogMessage> 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<LogMessage> pruneMessages(List<LogMessage> 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<LogMessage> 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<LogMessage> messages = this.logMessagesByRealmAndId.get(realm);
messages.remove(message);
}
}

View File

@ -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));
}
}

View File

@ -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$

View File

@ -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<LogMessage> logMessages);
void updateState(LogMessage logMessage);
void updateStates(Collection<LogMessage> logMessages);
void remove(LogMessage logMessage);
void removeAll(List<LogMessage> logMessages);

View File

@ -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);
}

View File

@ -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<String> keys = valueJ.keySet();
for (String propertyName : keys) {
properties.setProperty(propertyName, valueJ.get(propertyName).getAsString());
}
}
}
return new I18nMessage(key, properties, message);
}
}

View File

@ -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<JsonObject>
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;

View File

@ -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<LogMessage> 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<LogMessage> 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);
}
}

View File

@ -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;

View File

@ -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
);

View File

@ -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
);

View File

@ -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

View File

@ -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<LogMessage> logMessages) {
logMessages.forEach(logMessage -> this.tx.getObjectDao().update(logMessage));
}
@Override
public void remove(LogMessage logMessage) {
this.tx.getObjectDao().remove(logMessage);

View File

@ -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;

View File

@ -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());

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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 {

View File

@ -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<Version> 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);

View File

@ -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));
}
}

View File

@ -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<LogMessage> 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<String> 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!");
}

View File

@ -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);