Compare commits
102 Commits
Author | SHA1 | Date |
---|---|---|
Robert von Burg | 3054d5fca5 | |
Robert von Burg | 19825b37a8 | |
Robert von Burg | c65c6624fb | |
Robert von Burg | 4d7b3faf3f | |
Robert von Burg | dc93800fd1 | |
Robert von Burg | 4b78b30600 | |
Robert von Burg | 3edcec4d08 | |
Robert von Burg | cf4ae20d0b | |
Robert von Burg | 68bd3dd6d6 | |
Robert von Burg | c4ac21ec98 | |
Robert von Burg | a45475f783 | |
Robert von Burg | baa7a0af4e | |
Robert von Burg | 1080096549 | |
Robert von Burg | 393659ff19 | |
Robert von Burg | a7258f0d06 | |
Robert von Burg | 2aca456729 | |
Robert von Burg | f4f9e2c798 | |
Robert von Burg | f051008d68 | |
Robert von Burg | 1e6ac0c042 | |
Robert von Burg | a103864eb8 | |
Robert von Burg | 125dfe9b03 | |
Robert von Burg | 2914889172 | |
Robert von Burg | d3c261b750 | |
Robert von Burg | de8d35480d | |
Robert von Burg | 1158acfd90 | |
Robert von Burg | faf05126b4 | |
Robert von Burg | d00b00d234 | |
Robert von Burg | 76d38e9af0 | |
Robert von Burg | f850367d2b | |
Robert von Burg | a4119ef1da | |
Robert von Burg | abe089f95c | |
Robert von Burg | 15b2788b9a | |
Robert von Burg | f02b541848 | |
Robert von Burg | 5526f20220 | |
Robert von Burg | a9c393f02a | |
Robert von Burg | 22359e51d8 | |
Robert von Burg | 7b9f2f867f | |
Robert von Burg | a9067bf161 | |
dependabot[bot] | e91a96b549 | |
Robert von Burg | 5c2bcc1620 | |
Robert von Burg | f3adc63af2 | |
Robert von Burg | a07bd83249 | |
Robert von Burg | c46c60c8fe | |
Robert von Burg | a2c720ce48 | |
Robert von Burg | c66446391a | |
Robert von Burg | 69d1f77364 | |
Robert von Burg | e33950b7a1 | |
Robert von Burg | 56098c96fa | |
Robert von Burg | 63670a77b6 | |
Robert von Burg | 9c15d0d1d2 | |
Robert von Burg | 77cd9264d0 | |
Robert von Burg | bf01876f7c | |
Robert von Burg | 373235f5e2 | |
Robert von Burg | 2eeda52f86 | |
Robert von Burg | ce22f180af | |
Robert von Burg | 8b20e4392d | |
Robert von Burg | 83f575de41 | |
Robert von Burg | 23054a5bad | |
Robert von Burg | d0008f9951 | |
Robert von Burg | 1e2965d588 | |
Robert von Burg | 7a8edc7cc6 | |
Robert von Burg | 22e2e5cc68 | |
Robert von Burg | f6a8746069 | |
Robert von Burg | fd5f344764 | |
Robert von Burg | ec8965eee2 | |
Robert von Burg | c17b23b72f | |
Robert von Burg | 5064249e72 | |
Robert von Burg | 269bd83ded | |
Robert von Burg | 529c1d0e3b | |
Robert von Burg | 974c3955ee | |
Robert von Burg | 0d674cf947 | |
Robert von Burg | ecb4db2377 | |
Robert von Burg | 48968972be | |
Robert von Burg | 4e0e0ab22a | |
Robert von Burg | 92f87cbc77 | |
Robert von Burg | 3c2aac3b9e | |
Robert von Burg | 004c6775c0 | |
Robert von Burg | 4b0b031b2b | |
Robert von Burg | c5402ba4fb | |
Robert von Burg | 30086f36cc | |
Robert von Burg | 792221e190 | |
Robert von Burg | 91a2f5651c | |
Robert von Burg | 579cd0b0e5 | |
Robert von Burg | 2ab8d95c0b | |
Robert von Burg | ed16e969f2 | |
Robert von Burg | dc0836773c | |
dependabot[bot] | 3fbb0acc4c | |
Robert von Burg | 74ba243b2f | |
Robert von Burg | ec72200d15 | |
Robert von Burg | 7867f206a1 | |
Robert von Burg | 4bee5990ac | |
Robert von Burg | bd03144b6a | |
Robert von Burg | 4c8dd9b0d1 | |
Robert von Burg | 4f419f02e5 | |
Robert von Burg | d2627d13e7 | |
Robert von Burg | d6cfc6711a | |
Robert von Burg | 6ee6326390 | |
Robert von Burg | 589f251aa2 | |
Robert von Burg | 68a13d0e77 | |
Robert von Burg | 52ae1574f4 | |
Robert von Burg | 1bd986d755 | |
Robert von Burg | f105147ec1 |
|
@ -51,7 +51,7 @@ jobs:
|
|||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
java-version: '21'
|
||||
cache: 'maven'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
[![Stars](https://img.shields.io/github/stars/strolch-li/strolch?style=flat-square "Stars")](https://github.com/strolch-li/strolch/stargazers)
|
||||
[![Issues](https://img.shields.io/github/issues/strolch-li/strolch?style=flat-square "Issues")](https://github.com/strolch-li/strolch/issues)
|
||||
[![Build Status](https://ci.atexxi.ch/buildStatus/icon?job=strolch)](https://ci.atexxi.ch/job/strolch/)
|
||||
[![Qodana](https://github.com/strolch-li/strolch/actions/workflows/code_quality.yml/badge.svg)](https://github.com/strolch-li/strolch/actions/workflows/code_quality.yml)
|
||||
[![Project Map](https://sourcespy.com/shield.svg)](https://sourcespy.com/github/strolchlistrolch/)
|
||||
|
||||
The main repository which contains all of Strolch.
|
||||
|
|
13
SECURITY.md
13
SECURITY.md
|
@ -4,15 +4,14 @@
|
|||
|
||||
The current branches are supported with security fixes:
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.0.x | :white_check_mark: |
|
||||
| 1.8.x | :white_check_mark: |
|
||||
| 1.7.x | :white_check_mark: |
|
||||
| <= 1.6.x | :x: |
|
||||
| Version | Supported |
|
||||
|----------|--------------------|
|
||||
| 2.2.x | :white_check_mark: |
|
||||
| 2.1.x | :white_check_mark: |
|
||||
| <= 2.0.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find a security vulnerability, then please send an email to eitch@eitchnet.ch. You might encrypt it using KeyOxide with my public GPG key: https://kxd.eitchnet.ch/b1359c320a72a2907f1a7f7875db9c85c74331f7
|
||||
If you find a security vulnerability, then please email eitch@eitchnet.ch. You might encrypt it using KeyOxide with my public GPG key: https://kxd.eitchnet.ch/b1359c320a72a2907f1a7f7875db9c85c74331f7
|
||||
|
||||
We will then consider the vulnerability and shall try our best to close the vulnerability as soon as possible if it is part of an active branch and a fix is possible.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@ package li.strolch.agent.api;
|
|||
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.util.ContextInitializer;
|
||||
import ch.qos.logback.classic.util.DefaultJoranConfigurator;
|
||||
import ch.qos.logback.core.joran.spi.JoranException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
public class LoggingLoader {
|
||||
|
||||
|
@ -25,22 +25,25 @@ public class LoggingLoader {
|
|||
"Not changing logback configuration as " + logConfigFile.getAbsolutePath() + " does not exist.");
|
||||
} else {
|
||||
if (!(LoggerFactory.getILoggerFactory() instanceof LoggerContext loggerContext)) {
|
||||
logger.error(logConfigFile.getAbsolutePath() +
|
||||
" exists, but LoggerFactory is not instance of ch.qos.logback.classic.LoggerContext. Ignoring.");
|
||||
logger.error(logConfigFile.getAbsolutePath()
|
||||
+ " exists, but LoggerFactory is not instance of ch.qos.logback.classic.LoggerContext. Ignoring.");
|
||||
} else {
|
||||
logger.info(logConfigFile.getAbsolutePath() + " file exists. Reloading logging configuration from " +
|
||||
logConfigFile);
|
||||
logger.info(logConfigFile.getAbsolutePath()
|
||||
+ " file exists. Reloading logging configuration from "
|
||||
+ logConfigFile);
|
||||
try {
|
||||
loggerContext.reset();
|
||||
new ContextInitializer(loggerContext).configureByResource(logConfigFile.toURI().toURL());
|
||||
DefaultJoranConfigurator configurator = new DefaultJoranConfigurator();
|
||||
configurator.setContext(loggerContext);
|
||||
configurator.configureByResource(logConfigFile.toURI().toURL());
|
||||
logger.info("Reloaded logger configuration from " + logConfigFile.getAbsolutePath());
|
||||
lastConfigFile = logConfigFile;
|
||||
} catch (MalformedURLException | JoranException e) {
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
new ContextInitializer(loggerContext).autoConfig();
|
||||
} catch (JoranException e1) {
|
||||
logger.error("Failed to reload original config after failure to load new config from " +
|
||||
logConfigFile.getAbsolutePath(), e);
|
||||
logger.error("Failed to reload original config after failure to load new config from "
|
||||
+ logConfigFile.getAbsolutePath(), e);
|
||||
}
|
||||
logger.error("Failed to reload logback configuration from file " + logConfigFile, e);
|
||||
}
|
||||
|
@ -55,10 +58,12 @@ public class LoggingLoader {
|
|||
reloadLogging(lastConfigFile.getParentFile());
|
||||
} else {
|
||||
if (!(LoggerFactory.getILoggerFactory() instanceof LoggerContext loggerContext)) {
|
||||
logger.error("LoggerFactory is not instance of " + LoggerContext.class.getName() +
|
||||
". Ignoring request to reload configuration!");
|
||||
System.out.println("LoggerFactory is not instance of " + LoggerContext.class.getName() +
|
||||
". Ignoring request to reload configuration!");
|
||||
logger.error("LoggerFactory is not instance of "
|
||||
+ LoggerContext.class.getName()
|
||||
+ ". Ignoring request to reload configuration!");
|
||||
System.out.println("LoggerFactory is not instance of "
|
||||
+ LoggerContext.class.getName()
|
||||
+ ". Ignoring request to reload configuration!");
|
||||
} else {
|
||||
logger.info(
|
||||
"Resetting logging configuration using auto config as no previous config fila available...");
|
||||
|
@ -69,6 +74,7 @@ public class LoggingLoader {
|
|||
} catch (JoranException e) {
|
||||
logger.error("Failed to do logging auto configuration", e);
|
||||
System.out.println("Failed to do logging auto configuration");
|
||||
//noinspection CallToPrintStackTrace
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,20 +81,14 @@ public class StrolchAgent {
|
|||
this.appVersion = appVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link StrolchConfiguration}
|
||||
*
|
||||
* @return the {@link StrolchConfiguration}
|
||||
*/
|
||||
public StrolchConfiguration getStrolchConfiguration() {
|
||||
return this.strolchConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the container
|
||||
*
|
||||
* @return the container
|
||||
*/
|
||||
public RuntimeConfiguration getRuntimeConfiguration() {
|
||||
return this.strolchConfiguration.getRuntimeConfiguration();
|
||||
}
|
||||
|
||||
public ComponentContainer getContainer() {
|
||||
return this.container;
|
||||
}
|
||||
|
@ -411,8 +405,9 @@ public class StrolchAgent {
|
|||
|
||||
public JsonObject getSystemState(long updateInterval, TimeUnit updateIntervalUnit) {
|
||||
|
||||
if (this.systemState == null ||
|
||||
System.currentTimeMillis() - this.systemStateUpdateTime > updateIntervalUnit.toMillis(updateInterval)) {
|
||||
if (this.systemState == null
|
||||
|| System.currentTimeMillis() - this.systemStateUpdateTime > updateIntervalUnit.toMillis(
|
||||
updateInterval)) {
|
||||
this.systemState = new JsonObject();
|
||||
|
||||
JsonObject osJ = new JsonObject();
|
||||
|
@ -474,8 +469,8 @@ public class StrolchAgent {
|
|||
File configPathF = runtimeConfig.getConfigPath();
|
||||
File dataPathF = runtimeConfig.getDataPath();
|
||||
File tempPathF = runtimeConfig.getTempPath();
|
||||
StrolchConfiguration newConfig = parseConfiguration(runtimeConfig.getEnvironment(), configPathF,
|
||||
dataPathF, tempPathF);
|
||||
StrolchConfiguration newConfig = parseConfiguration(runtimeConfig.getEnvironment(), configPathF, dataPathF,
|
||||
tempPathF);
|
||||
|
||||
for (String name : this.container.getComponentNames()) {
|
||||
ComponentConfiguration newComponentConfig = newConfig.getComponentConfiguration(name);
|
||||
|
|
|
@ -31,6 +31,8 @@ import li.strolch.runtime.configuration.ComponentConfiguration;
|
|||
import li.strolch.runtime.privilege.PrivilegeHandler;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.runtime.StrolchConstants.*;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -70,9 +72,9 @@ public class DefaultRealmHandler extends StrolchComponent implements RealmHandle
|
|||
@Override
|
||||
public void setup(ComponentConfiguration configuration) {
|
||||
this.realms = new HashMap<>(1);
|
||||
String[] realms = configuration.getStringArray(PROP_REALMS, StrolchConstants.DEFAULT_REALM);
|
||||
String[] realms = configuration.getStringArray(PROP_REALMS, DEFAULT_REALM);
|
||||
for (String realmName : realms) {
|
||||
String dataStoreModeKey = StrolchConstants.makeRealmKey(realmName, PREFIX_DATA_STORE_MODE);
|
||||
String dataStoreModeKey = makeRealmKey(realmName, PREFIX_DATA_STORE_MODE);
|
||||
String realmMode = configuration.getString(dataStoreModeKey, null);
|
||||
InternalStrolchRealm realm = buildRealm(realmName, realmMode);
|
||||
this.realms.put(realmName, realm);
|
||||
|
|
|
@ -30,6 +30,8 @@ import li.strolch.runtime.configuration.StrolchConfigurationException;
|
|||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
||||
import static li.strolch.agent.impl.DefaultRealmHandler.*;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -87,7 +89,7 @@ public class TransientRealm extends InternalStrolchRealm {
|
|||
public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
|
||||
super.initialize(container, configuration);
|
||||
|
||||
String key = StrolchConstants.makeRealmKey(getRealm(), DefaultRealmHandler.PREFIX_DATA_STORE_FILE);
|
||||
String key = StrolchConstants.makeRealmKey(getRealm(), PREFIX_DATA_STORE_FILE);
|
||||
if (!configuration.hasProperty(key)) {
|
||||
String msg = "There is no data store file for realm {0}. Set a property with key {1}";
|
||||
msg = MessageFormat.format(msg, getRealm(), key);
|
||||
|
|
|
@ -38,11 +38,11 @@ import li.strolch.utils.objectfilter.ObjectFilterStatistics;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static li.strolch.agent.api.StrolchAgent.getUniqueId;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.model.Tags.*;
|
||||
|
@ -515,7 +515,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (locator.getSize() < 3) {
|
||||
String msg
|
||||
= "The locator is invalid as it does not have at least three path elements (e.g. Resource/MyType/@id): {0}";
|
||||
msg = MessageFormat.format(msg, locator.toString());
|
||||
msg = format(msg, locator.toString());
|
||||
throw new StrolchModelException(msg);
|
||||
}
|
||||
|
||||
|
@ -535,7 +535,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (allowNull)
|
||||
return null;
|
||||
String msg = "No top level object could be found with locator {0}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator));
|
||||
throw new StrolchModelException(format(msg, locator));
|
||||
}
|
||||
|
||||
if (elements.size() == 3)
|
||||
|
@ -551,7 +551,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (allowNull)
|
||||
return null;
|
||||
String msg = "Could not find ParameterBag for locator {0} on element {1}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, rootElement.getLocator()));
|
||||
throw new StrolchModelException(format(msg, locator, rootElement.getLocator()));
|
||||
}
|
||||
|
||||
if (elements.size() == 5)
|
||||
|
@ -563,7 +563,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (allowNull)
|
||||
return null;
|
||||
String msg = "Could not find Parameter for locator {0} on element {1}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, bag.getLocator()));
|
||||
throw new StrolchModelException(format(msg, locator, bag.getLocator()));
|
||||
}
|
||||
return (T) parameter;
|
||||
|
||||
|
@ -571,7 +571,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (elements.size() != 5) {
|
||||
String msg = "Missing state Id on locator {0}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator));
|
||||
throw new StrolchModelException(format(msg, locator));
|
||||
}
|
||||
|
||||
Resource resource = rootElement.asResource();
|
||||
|
@ -589,7 +589,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (!(element instanceof Activity)) {
|
||||
String msg = "Invalid locator {0} with part {1} as not an Activity but deeper element specified";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, next));
|
||||
throw new StrolchModelException(format(msg, locator, next));
|
||||
}
|
||||
|
||||
element = ((Activity) element).getElement(next);
|
||||
|
@ -602,7 +602,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
return null;
|
||||
|
||||
String msg = "Invalid locator {0} with part {1}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, stateOrBagOrActivity));
|
||||
throw new StrolchModelException(format(msg, locator, stateOrBagOrActivity));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -641,8 +641,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
break;
|
||||
if (!parents.add(parent))
|
||||
throw new IllegalStateException(
|
||||
"circular dependencies from " + element.getLocator() + " to " + parent.getLocator() +
|
||||
" on relations parameter " + parentParamKey);
|
||||
format("circular dependencies from {0} to {1} on relations parameter {2}", element.getLocator(),
|
||||
parent.getLocator(), parentParamKey));
|
||||
|
||||
t = parent.getParameter(bagKey, paramKey);
|
||||
}
|
||||
|
@ -787,7 +787,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (assertExists && refP.isEmpty()) {
|
||||
String msg = "The Order with type \"{0}\" and id \"{1}\" does not exist for param \"{2}\"";
|
||||
throw new StrolchException(MessageFormat.format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
throw new StrolchException(format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
}
|
||||
|
||||
if (refP.isEmpty())
|
||||
|
@ -901,7 +901,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (assertExists && refP.isEmpty()) {
|
||||
String msg = "The Resource with type \"{0}\" and id \"{1}\" does not exist for param \"{2}\"";
|
||||
throw new StrolchException(MessageFormat.format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
throw new StrolchException(format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
}
|
||||
|
||||
if (refP.isEmpty())
|
||||
|
@ -1031,7 +1031,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (assertExists && refP.isEmpty()) {
|
||||
String msg = "The Activity with type \"{0}\" and id \"{1}\" does not exist for param \"{2}\"";
|
||||
throw new StrolchException(MessageFormat.format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
throw new StrolchException(format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
}
|
||||
|
||||
if (refP.isEmpty())
|
||||
|
@ -1134,6 +1134,21 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
this.objectFilter.removeObjectCache(locator.get(0), locator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceCached(String type, String id) {
|
||||
return this.resourceCache.containsElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOrderCached(String type, String id) {
|
||||
return this.orderCache.containsElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActivityCached(String type, String id) {
|
||||
return this.activityCache.containsElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getCachedResource(String type, String id) {
|
||||
return this.resourceCache.getElement(type, id);
|
||||
|
@ -1149,6 +1164,66 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
return this.activityCache.getElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Resource> streamCachedResources(String... types) {
|
||||
if (types.length == 0)
|
||||
return this.resourceCache.values().stream();
|
||||
if (types.length == 1) {
|
||||
String type = types[0];
|
||||
if (!this.resourceCache.containsMap(type))
|
||||
return Stream.empty();
|
||||
return new ArrayList<>(this.resourceCache.getMap(type).values()).stream();
|
||||
}
|
||||
return this.resourceCache.values().stream().filter(element -> {
|
||||
String resType = element.getType();
|
||||
for (String type : types) {
|
||||
if (resType.equals(type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Order> streamCachedOrders(String... types) {
|
||||
if (types.length == 0)
|
||||
return this.orderCache.values().stream();
|
||||
if (types.length == 1) {
|
||||
String type = types[0];
|
||||
if (!this.orderCache.containsMap(type))
|
||||
return Stream.empty();
|
||||
return new ArrayList<>(this.orderCache.getMap(type).values()).stream();
|
||||
}
|
||||
return this.orderCache.values().stream().filter(element -> {
|
||||
String resType = element.getType();
|
||||
for (String type : types) {
|
||||
if (resType.equals(type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Activity> streamCachedActivities(String... types) {
|
||||
if (types.length == 0)
|
||||
return this.activityCache.values().stream();
|
||||
if (types.length == 1) {
|
||||
String type = types[0];
|
||||
if (!this.activityCache.containsMap(type))
|
||||
return Stream.empty();
|
||||
return new ArrayList<>(this.activityCache.getMap(type).values()).stream();
|
||||
}
|
||||
return this.activityCache.values().stream().filter(element -> {
|
||||
String resType = element.getType();
|
||||
for (String type : types) {
|
||||
if (resType.equals(type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasResource(String type, String id) {
|
||||
boolean inFilter = hasElementInFilter(Tags.RESOURCE, Resource.locatorFor(type, id));
|
||||
|
@ -1253,6 +1328,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
lock(resource);
|
||||
this.objectFilter.add(Tags.RESOURCE, resource.getLocator(), resource);
|
||||
}
|
||||
|
||||
this.resourceCache.addElement(resource.getType(), resource.getId(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1271,6 +1348,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
lock(order);
|
||||
this.objectFilter.add(Tags.ORDER, order.getLocator(), order);
|
||||
}
|
||||
|
||||
this.orderCache.addElement(order.getType(), order.getId(), order);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1289,6 +1368,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
lock(activity);
|
||||
this.objectFilter.add(Tags.ACTIVITY, activity.getLocator(), activity);
|
||||
}
|
||||
|
||||
this.activityCache.addElement(activity.getType(), activity.getId(), activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1298,6 +1379,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
resource.assertNotReadonly();
|
||||
lock(resource);
|
||||
this.objectFilter.add(Tags.RESOURCE, resource.getLocator(), resource);
|
||||
this.resourceCache.addElement(resource.getType(), resource.getId(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1307,6 +1389,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
order.assertNotReadonly();
|
||||
lock(order);
|
||||
this.objectFilter.add(Tags.ORDER, order.getLocator(), order);
|
||||
this.orderCache.addElement(order.getType(), order.getId(), order);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1316,6 +1399,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
activity.assertNotReadonly();
|
||||
lock(activity);
|
||||
this.objectFilter.add(Tags.ACTIVITY, activity.getLocator(), activity);
|
||||
this.activityCache.addElement(activity.getType(), activity.getId(), activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1324,6 +1408,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
DBC.PRE.assertNotNull("resource must not be null", resource);
|
||||
resource.assertNotReadonly();
|
||||
this.objectFilter.update(Tags.RESOURCE, resource.getLocator(), resource);
|
||||
this.resourceCache.addElement(resource.getType(), resource.getId(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1332,6 +1417,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
DBC.PRE.assertNotNull("order must not be null", order);
|
||||
order.assertNotReadonly();
|
||||
this.objectFilter.update(Tags.ORDER, order.getLocator(), order);
|
||||
this.orderCache.addElement(order.getType(), order.getId(), order);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1340,6 +1426,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
DBC.PRE.assertNotNull("activity must not be null", activity);
|
||||
activity.assertNotReadonly();
|
||||
this.objectFilter.update(Tags.ACTIVITY, activity.getLocator(), activity);
|
||||
this.activityCache.addElement(activity.getType(), activity.getId(), activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1383,7 +1470,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
List<Resource> changedR = this.objectFilter.getRemoved(Resource.class, Tags.RESOURCE);
|
||||
if (changedR.size() == 1) {
|
||||
RemoveResourceCommand cmd = new RemoveResourceCommand(this);
|
||||
cmd.setResource(changedR.get(0));
|
||||
cmd.setResource(changedR.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedR.size() > 1) {
|
||||
RemoveResourcesCommand cmd = new RemoveResourcesCommand(this);
|
||||
|
@ -1395,7 +1482,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedR = this.objectFilter.getUpdated(Resource.class, Tags.RESOURCE);
|
||||
if (changedR.size() == 1) {
|
||||
UpdateResourceCommand cmd = new UpdateResourceCommand(this);
|
||||
cmd.setResource(changedR.get(0));
|
||||
cmd.setResource(changedR.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedR.size() > 1) {
|
||||
UpdateResourcesCommand cmd = new UpdateResourcesCommand(this);
|
||||
|
@ -1407,7 +1494,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedR = this.objectFilter.getAdded(Resource.class, Tags.RESOURCE);
|
||||
if (changedR.size() == 1) {
|
||||
AddResourceCommand cmd = new AddResourceCommand(this);
|
||||
cmd.setResource(changedR.get(0));
|
||||
cmd.setResource(changedR.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedR.size() > 1) {
|
||||
AddResourcesCommand cmd = new AddResourcesCommand(this);
|
||||
|
@ -1422,7 +1509,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
List<Order> changedO = this.objectFilter.getRemoved(Order.class, Tags.ORDER);
|
||||
if (changedO.size() == 1) {
|
||||
RemoveOrderCommand cmd = new RemoveOrderCommand(this);
|
||||
cmd.setOrder(changedO.get(0));
|
||||
cmd.setOrder(changedO.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedO.size() > 1) {
|
||||
RemoveOrdersCommand cmd = new RemoveOrdersCommand(this);
|
||||
|
@ -1434,7 +1521,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedO = this.objectFilter.getUpdated(Order.class, Tags.ORDER);
|
||||
if (changedO.size() == 1) {
|
||||
UpdateOrderCommand cmd = new UpdateOrderCommand(this);
|
||||
cmd.setOrder(changedO.get(0));
|
||||
cmd.setOrder(changedO.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedO.size() > 1) {
|
||||
UpdateOrdersCommand cmd = new UpdateOrdersCommand(this);
|
||||
|
@ -1446,7 +1533,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedO = this.objectFilter.getAdded(Order.class, Tags.ORDER);
|
||||
if (changedO.size() == 1) {
|
||||
AddOrderCommand cmd = new AddOrderCommand(this);
|
||||
cmd.setOrder(changedO.get(0));
|
||||
cmd.setOrder(changedO.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedO.size() > 1) {
|
||||
AddOrdersCommand cmd = new AddOrdersCommand(this);
|
||||
|
@ -1461,7 +1548,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
List<Activity> changedA = this.objectFilter.getRemoved(Activity.class, Tags.ACTIVITY);
|
||||
if (changedA.size() == 1) {
|
||||
RemoveActivityCommand cmd = new RemoveActivityCommand(this);
|
||||
cmd.setActivity(changedA.get(0));
|
||||
cmd.setActivity(changedA.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedA.size() > 1) {
|
||||
RemoveActivitiesCommand cmd = new RemoveActivitiesCommand(this);
|
||||
|
@ -1473,7 +1560,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedA = this.objectFilter.getUpdated(Activity.class, Tags.ACTIVITY);
|
||||
if (changedA.size() == 1) {
|
||||
UpdateActivityCommand cmd = new UpdateActivityCommand(this);
|
||||
cmd.setActivity(changedA.get(0));
|
||||
cmd.setActivity(changedA.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedA.size() > 1) {
|
||||
UpdateActivitiesCommand cmd = new UpdateActivitiesCommand(this);
|
||||
|
@ -1485,7 +1572,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedA = this.objectFilter.getAdded(Activity.class, Tags.ACTIVITY);
|
||||
if (changedA.size() == 1) {
|
||||
AddActivityCommand cmd = new AddActivityCommand(this);
|
||||
cmd.setActivity(changedA.get(0));
|
||||
cmd.setActivity(changedA.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedA.size() > 1) {
|
||||
AddActivitiesCommand cmd = new AddActivitiesCommand(this);
|
||||
|
@ -1531,7 +1618,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
this.closeStrategy = TransactionCloseStrategy.ROLLBACK;
|
||||
|
||||
String msg = "Strolch Transaction for realm {0} failed due to {1}";
|
||||
msg = MessageFormat.format(msg, getRealmName(), getExceptionMessage(e));
|
||||
msg = format(msg, getRealmName(), getExceptionMessage(e));
|
||||
throw new StrolchTransactionException(msg, e);
|
||||
}
|
||||
}
|
||||
|
@ -1595,7 +1682,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
@Override
|
||||
public void autoCloseableRollback() {
|
||||
long start = System.nanoTime();
|
||||
logger.warn(MessageFormat.format("Rolling back TX for realm {0}...", getRealmName()));
|
||||
logger.warn(format("Rolling back TX for realm {0}...", getRealmName()));
|
||||
try {
|
||||
this.txResult.setState(TransactionState.ROLLING_BACK);
|
||||
undoCommands();
|
||||
|
@ -1808,12 +1895,13 @@ 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,
|
||||
LogMessageState.Information, 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}";
|
||||
msg = MessageFormat.format(msg, getRealmName(), getExceptionMessage(e), sb.toString());
|
||||
msg = format(msg, getRealmName(), getExceptionMessage(e), sb.toString());
|
||||
StrolchTransactionException ex = new StrolchTransactionException(msg, e);
|
||||
|
||||
if (throwEx)
|
||||
|
@ -1922,10 +2010,10 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (this.auditTrail != null && !isSuppressAuditsForAudits()) {
|
||||
if (this.realm.isAuditTrailEnabledForRead())
|
||||
auditsForAudits(audits, AccessType.READ, Tags.AUDIT, this.auditTrail.getRead());
|
||||
auditsForAudits(audits, AccessType.CREATE, Tags.AUDIT, this.auditTrail.getCreated());
|
||||
auditsForAudits(audits, AccessType.UPDATE, Tags.AUDIT, this.auditTrail.getUpdated());
|
||||
auditsForAudits(audits, AccessType.DELETE, Tags.AUDIT, this.auditTrail.getDeleted());
|
||||
auditsForAudits(audits, AccessType.READ, this.auditTrail.getRead());
|
||||
auditsForAudits(audits, AccessType.CREATE, this.auditTrail.getCreated());
|
||||
auditsForAudits(audits, AccessType.UPDATE, this.auditTrail.getUpdated());
|
||||
auditsForAudits(audits, AccessType.DELETE, this.auditTrail.getDeleted());
|
||||
}
|
||||
|
||||
if (!audits.isEmpty())
|
||||
|
@ -1940,9 +2028,9 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
}
|
||||
}
|
||||
|
||||
private void auditsForAudits(List<Audit> audits, AccessType accessType, String elementType, Set<Audit> elements) {
|
||||
private void auditsForAudits(List<Audit> audits, AccessType accessType, Set<Audit> elements) {
|
||||
for (Audit element : elements) {
|
||||
audits.add(auditFrom(accessType, elementType, StringHelper.DASH, element.getId().toString()));
|
||||
audits.add(auditFrom(accessType, Tags.AUDIT, StringHelper.DASH, element.getId().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1571,6 +1571,21 @@ public interface StrolchTransaction extends AutoCloseable {
|
|||
*/
|
||||
void removeFromCache(Locator locator);
|
||||
|
||||
/**
|
||||
* Returns true if the given resource is currently cached
|
||||
*/
|
||||
boolean isResourceCached(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns true if the given order is currently cached
|
||||
*/
|
||||
boolean isOrderCached(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns true if the given activity is currently cached
|
||||
*/
|
||||
boolean isActivityCached(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns the cached resource with the given type and id, or null if not yet fetched
|
||||
*
|
||||
|
@ -1607,6 +1622,21 @@ public interface StrolchTransaction extends AutoCloseable {
|
|||
*/
|
||||
Activity getCachedActivity(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns a stream of resources in the cache
|
||||
*/
|
||||
Stream<Resource> streamCachedResources(String... types);
|
||||
|
||||
/**
|
||||
* Returns a stream of orders in the cache
|
||||
*/
|
||||
Stream<Order> streamCachedOrders(String... types);
|
||||
|
||||
/**
|
||||
* Returns a stream of activities in the cache
|
||||
*/
|
||||
Stream<Activity> streamCachedActivities(String... types);
|
||||
|
||||
/**
|
||||
* Returns true if the @{@link Resource} exists with the given type and ID
|
||||
*
|
||||
|
|
|
@ -61,7 +61,8 @@ public class InMemoryStrolchDao<T extends StrolchRootElement> implements Strolch
|
|||
public List<T> queryAll(String... types) throws StrolchPersistenceException {
|
||||
List<T> values = new ArrayList<>();
|
||||
for (String type : types) {
|
||||
values.addAll(this.elements.getList(type));
|
||||
if (this.elements.containsList(type))
|
||||
values.addAll(this.elements.getList(type));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package li.strolch.policy.notifications;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchModelConstants;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.utils.collections.DateRange;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
|
||||
public class DefaultNotificationsPolicy extends NotificationsPolicy {
|
||||
public DefaultNotificationsPolicy(StrolchTransaction tx) {
|
||||
super(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findUserNotifications() {
|
||||
return tx().streamResources(StrolchModelConstants.TYPE_NOTIFICATION).filter(this::isForUser).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canView(Resource notification) {
|
||||
return isForAll(notification) || isForRole(notification) || isForGroup(notification);
|
||||
}
|
||||
|
||||
protected boolean isForUser(Resource notification) {
|
||||
if (!isActive(notification))
|
||||
return false;
|
||||
return isEnabled(notification) && (
|
||||
isForAll(notification) || isForRole(notification) || isForGroup(notification));
|
||||
}
|
||||
|
||||
protected boolean isActive(Resource notification) {
|
||||
return new DateRange()
|
||||
.from(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_FROM), true)
|
||||
.to(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_TO), true)
|
||||
.contains(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
protected boolean isEnabled(Resource notification) {
|
||||
return notification.getBoolean(BAG_VISIBILITY, PARAM_ENABLED);
|
||||
}
|
||||
|
||||
protected boolean isForAll(Resource notification) {
|
||||
return notification.getBoolean(BAG_VISIBILITY, PARAM_FOR_ALL);
|
||||
}
|
||||
|
||||
protected boolean isForRole(Resource notification) {
|
||||
List<String> roles = notification.getStringList(BAG_VISIBILITY, PARAM_ROLES);
|
||||
return roles.stream().anyMatch(r -> tx().getCertificate().hasRole(r));
|
||||
}
|
||||
|
||||
protected boolean isForGroup(Resource notification) {
|
||||
List<String> groups = notification.getStringList(BAG_VISIBILITY, PARAM_GROUPS);
|
||||
return groups.stream().anyMatch(r -> tx().getCertificate().hasGroup(r));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package li.strolch.policy.notifications;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.policy.PolicyDef;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.policy.StrolchPolicy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.POLICY_DEFAULT;
|
||||
import static li.strolch.model.policy.PolicyDef.getJavaPolicy;
|
||||
import static li.strolch.model.policy.PolicyDef.getKeyPolicy;
|
||||
|
||||
public abstract class NotificationsPolicy extends StrolchPolicy {
|
||||
|
||||
public NotificationsPolicy(StrolchTransaction tx) {
|
||||
super(tx);
|
||||
}
|
||||
|
||||
public abstract List<Resource> findUserNotifications();
|
||||
public abstract boolean canView(Resource notification);
|
||||
|
||||
public static NotificationsPolicy getDefaultPolicy(StrolchTransaction tx) {
|
||||
PolicyDef defaultDef = getKeyPolicy(NotificationsPolicy.class, POLICY_DEFAULT);
|
||||
PolicyDef fallbackDef = getJavaPolicy(NotificationsPolicy.class, DefaultNotificationsPolicy.class);
|
||||
return tx.getPolicy(NotificationsPolicy.class, defaultDef, fallbackDef);
|
||||
}
|
||||
}
|
|
@ -87,6 +87,8 @@ public class StrolchConstants extends StrolchModelConstants {
|
|||
public static final String PRIVILEGE_SET_USER_STATE = PrivilegeHandler.PRIVILEGE_SET_USER_STATE;
|
||||
public static final String PRIVILEGE_SET_USER_PASSWORD = PrivilegeHandler.PRIVILEGE_SET_USER_PASSWORD;
|
||||
|
||||
public static final String PRIVILEGE_INSPECTOR = "Inspector";
|
||||
|
||||
public static final String PRIVILEGE_INVALIDATE_SESSION = "InvalidateSession";
|
||||
public static final String PRIVILEGE_GET_SESSION = "GetSession";
|
||||
|
||||
|
@ -94,5 +96,8 @@ public class StrolchConstants extends StrolchModelConstants {
|
|||
public static final String PRIVILEGE_UPDATE_PREFIX = "Update";
|
||||
public static final String PRIVILEGE_REMOVE_PREFIX = "Remove";
|
||||
public static final String PRIVILEGE_GET_PREFIX = "Get";
|
||||
public static final String PRIVILEGE_GET_NOTIFICATIONS = "GetNotifications";
|
||||
public static final String PRIVILEGE_GET_NOTIFICATION = "GetNotification";
|
||||
public static final String PRIVILEGE_GET_NOTIFICATIONS_ALL = "GetNotificationsAll";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,6 @@
|
|||
*/
|
||||
package li.strolch.runtime.privilege;
|
||||
|
||||
import static java.lang.Boolean.parseBoolean;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS_PATH;
|
||||
import static li.strolch.privilege.helper.XmlConstants.PARAM_BASE_PATH;
|
||||
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.agent.api.ComponentContainer;
|
||||
import li.strolch.agent.api.StrolchComponent;
|
||||
import li.strolch.agent.api.StrolchRealm;
|
||||
|
@ -48,6 +35,21 @@ import li.strolch.runtime.configuration.ComponentConfiguration;
|
|||
import li.strolch.runtime.configuration.RuntimeConfiguration;
|
||||
import li.strolch.utils.helper.XmlHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.Boolean.parseBoolean;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static li.strolch.persistence.api.TransactionThreadLocal.getTx;
|
||||
import static li.strolch.persistence.api.TransactionThreadLocal.hasTx;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS_PATH;
|
||||
import static li.strolch.privilege.helper.XmlConstants.PARAM_BASE_PATH;
|
||||
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*;
|
||||
|
||||
public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements PrivilegeHandler {
|
||||
|
||||
public static final String PROP_PRIVILEGE_CONFIG_FILE = "privilegeConfigFile";
|
||||
|
@ -89,8 +91,7 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
|
|||
/**
|
||||
* Initializes the {@link DefaultPrivilegeHandler} from the configuration file
|
||||
*
|
||||
* @param privilegeXmlFile
|
||||
* a {@link File} reference to the XML file containing the configuration for Privilege
|
||||
* @param privilegeXmlFile a {@link File} reference to the XML file containing the configuration for Privilege
|
||||
*
|
||||
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and
|
||||
* {@link PersistenceHandler} are set and initialized as well
|
||||
|
@ -185,13 +186,17 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
|
|||
|
||||
private void writeAudit(Certificate certificate, String login, AccessType accessType, String username) {
|
||||
StrolchRealm realm = getContainer().getRealm(certificate);
|
||||
try (StrolchTransaction tx = realm.openTx(certificate, login, false).silentThreshold(1, NANOSECONDS)) {
|
||||
try (StrolchTransaction tx = hasTx() ? getTx() : openTx(certificate, login, realm)) {
|
||||
tx.setSuppressAudits(true);
|
||||
Audit audit = tx.auditFrom(accessType, PRIVILEGE, CERTIFICATE, username);
|
||||
tx.getAuditTrail().add(tx, audit);
|
||||
}
|
||||
}
|
||||
|
||||
private static StrolchTransaction openTx(Certificate certificate, String login, StrolchRealm realm) {
|
||||
return realm.openTx(certificate, login, false).silentThreshold(1, NANOSECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivilegeContext validate(Certificate certificate) throws PrivilegeException {
|
||||
return this.privilegeHandler.validate(certificate);
|
||||
|
|
|
@ -302,7 +302,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
|
|||
return certificate;
|
||||
}
|
||||
|
||||
private void checkSessionsForTimeout() {
|
||||
protected void checkSessionsForTimeout() {
|
||||
ZonedDateTime maxKeepAliveTime = ZonedDateTime.now().minusMinutes(this.maxKeepAliveMinutes);
|
||||
ZonedDateTime timeOutTime = ZonedDateTime.now().minusMinutes(this.sessionTtlMinutes);
|
||||
|
||||
|
@ -326,7 +326,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
|
|||
}
|
||||
}
|
||||
|
||||
private void sessionTimeout(Certificate certificate) {
|
||||
protected void sessionTimeout(Certificate certificate) {
|
||||
DBC.PRE.assertNotNull("Certificate must be given!", certificate);
|
||||
|
||||
Certificate removedCert = this.certificateMap.remove(certificate.getAuthToken());
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package li.strolch.search;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ORDER;
|
||||
|
||||
import li.strolch.model.Order;
|
||||
import li.strolch.model.activity.Activity;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ORDER;
|
||||
|
||||
/**
|
||||
* Performs a search for {@link Activity} elements
|
||||
*/
|
||||
|
@ -20,7 +22,13 @@ public class ActivitySearch extends StrolchSearch<Activity> {
|
|||
|
||||
@Override
|
||||
public ActivitySearch types(String... types) {
|
||||
this.navigator = tx -> tx.streamActivities(types);
|
||||
this.navigator = tx -> {
|
||||
Stream<Activity> cachedStream = tx.streamCachedActivities(types);
|
||||
Stream<Activity> stream = tx
|
||||
.streamActivities(types)
|
||||
.filter(e -> !tx.isActivityCached(e.getType(), e.getId()));
|
||||
return Stream.concat(cachedStream, stream);
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,10 +63,16 @@ public interface ExpressionBuilder {
|
|||
default <T extends StrolchRootElement> SearchExpression<T> containsIgnoreCase(Object right) {
|
||||
return element -> PredicatesSupport.containsIgnoreCase(right).matches(extract(element));
|
||||
}
|
||||
default <T extends StrolchRootElement> SearchExpression<T> containsIgnoreCaseMatchAny(Object right) {
|
||||
return element -> PredicatesSupport.containsIgnoreCaseMatchAny(right).matches(extract(element));
|
||||
}
|
||||
|
||||
default <T extends StrolchRootElement> SearchExpression<T> isIn(Object right) {
|
||||
return element -> PredicatesSupport.isIn(right).matches(extract(element));
|
||||
}
|
||||
default <T extends StrolchRootElement> SearchExpression<T> isInArray(Object... right) {
|
||||
return element -> PredicatesSupport.isIn(right).matches(extract(element));
|
||||
}
|
||||
|
||||
default <T extends StrolchRootElement> SearchExpression<T> isInIgnoreCase(Object right) {
|
||||
return element -> PredicatesSupport.isInIgnoreCase(right).matches(extract(element));
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
package li.strolch.search;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import li.strolch.model.*;
|
||||
import li.strolch.model.activity.Activity;
|
||||
import li.strolch.model.parameter.Parameter;
|
||||
|
@ -12,6 +7,12 @@ import li.strolch.model.parameter.StringParameter;
|
|||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.utils.iso8601.ISO8601FormatFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
|
||||
/**
|
||||
* Implements search expressions to be statically imported when writing searches
|
||||
*/
|
||||
|
@ -148,6 +149,26 @@ public class ExpressionsSupport {
|
|||
};
|
||||
}
|
||||
|
||||
public static <T extends StrolchRootElement> ExpressionBuilder paramOnBagType(String bagType, String paramId) {
|
||||
return element -> {
|
||||
List<Object> result = element
|
||||
.streamOfParameterBagsByType(bagType)
|
||||
.filter(b -> b.isParamSet(paramId))
|
||||
.map(b -> b.getParameter(paramId, true).getValue())
|
||||
.toList();
|
||||
if (result.size() == 1)
|
||||
return result.getFirst();
|
||||
return result.toArray();
|
||||
};
|
||||
}
|
||||
|
||||
public static <T extends StrolchRootElement> ExpressionBuilder extract(Function<T, Object> extractor) {
|
||||
return element -> {
|
||||
@SuppressWarnings("unchecked") T e = (T) element;
|
||||
return extractor.apply(e);
|
||||
};
|
||||
}
|
||||
|
||||
public static <T extends StrolchRootElement> SearchExpression<T> paramNull(String paramId) {
|
||||
return paramNull(BAG_PARAMETERS, paramId);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import li.strolch.persistence.api.StrolchTransaction;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
|
@ -23,7 +24,11 @@ public class OrderSearch extends StrolchSearch<Order> {
|
|||
|
||||
@Override
|
||||
public OrderSearch types(String... types) {
|
||||
this.navigator = tx -> tx.streamOrders(types);
|
||||
this.navigator = tx -> {
|
||||
Stream<Order> cachedStream = tx.streamCachedOrders(types);
|
||||
Stream<Order> stream = tx.streamOrders(types).filter(e -> !tx.isOrderCached(e.getType(), e.getId()));
|
||||
return Stream.concat(cachedStream, stream);
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,11 @@ public class PredicatesSupport {
|
|||
}
|
||||
|
||||
public static SearchPredicate contains(Object right) {
|
||||
return new ContainsPredicate(right, false);
|
||||
return new ContainsPredicate(right, false, false);
|
||||
}
|
||||
|
||||
public static SearchPredicate containsMatchAny(Object right) {
|
||||
return new ContainsPredicate(right, false, true);
|
||||
}
|
||||
|
||||
public static SearchPredicate collectionContains(Object right) {
|
||||
|
@ -55,7 +59,11 @@ public class PredicatesSupport {
|
|||
}
|
||||
|
||||
public static SearchPredicate containsIgnoreCase(Object right) {
|
||||
return new ContainsPredicate(right, true);
|
||||
return new ContainsPredicate(right, true, false);
|
||||
}
|
||||
|
||||
public static SearchPredicate containsIgnoreCaseMatchAny(Object right) {
|
||||
return new ContainsPredicate(right, true, true);
|
||||
}
|
||||
|
||||
public static SearchPredicate isIn(Object right) {
|
||||
|
|
|
@ -3,6 +3,8 @@ package li.strolch.search;
|
|||
import li.strolch.model.Resource;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Performs a search for {@link Resource} elements
|
||||
*/
|
||||
|
@ -17,7 +19,13 @@ public class ResourceSearch extends StrolchSearch<Resource> {
|
|||
|
||||
@Override
|
||||
public ResourceSearch types(String... types) {
|
||||
this.navigator = tx -> tx.streamResources(types);
|
||||
this.navigator = tx -> {
|
||||
Stream<Resource> cachedStream = tx.streamCachedResources(types);
|
||||
Stream<Resource> stream = tx
|
||||
.streamResources(types)
|
||||
.filter(e -> !tx.isResourceCached(e.getType(), e.getId()));
|
||||
return Stream.concat(cachedStream, stream);
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package li.strolch.search;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_RELATIONS;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import li.strolch.model.Order;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
import li.strolch.model.activity.Activity;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_RELATIONS;
|
||||
|
||||
/**
|
||||
* Declares specific search expressions, i.e. extracting the relevant data for a where clause
|
||||
*/
|
||||
|
@ -77,6 +77,10 @@ public interface SearchExpressions {
|
|||
return ExpressionsSupport.param(BAG_PARAMETERS, paramId);
|
||||
}
|
||||
|
||||
default <T extends StrolchRootElement> ExpressionBuilder extract(Function<T, Object> extractor) {
|
||||
return ExpressionsSupport.extract(extractor);
|
||||
}
|
||||
|
||||
default ExpressionBuilder relation(String paramId) {
|
||||
return ExpressionsSupport.param(BAG_RELATIONS, paramId);
|
||||
}
|
||||
|
@ -106,6 +110,10 @@ public interface SearchExpressions {
|
|||
return ExpressionsSupport.paramNull(bagId, paramId);
|
||||
}
|
||||
|
||||
default ExpressionBuilder paramOnBagType(String bagType, String paramId) {
|
||||
return ExpressionsSupport.paramOnBagType(bagType, paramId);
|
||||
}
|
||||
|
||||
default ExpressionBuilder relationName(StrolchTransaction tx, String paramId) {
|
||||
return ExpressionsSupport.relationName(tx, paramId);
|
||||
}
|
||||
|
|
|
@ -3,18 +3,20 @@ package li.strolch.search.predicates;
|
|||
import li.strolch.utils.ObjectHelper;
|
||||
|
||||
/**
|
||||
* Implements the contains predicate, delegating to {@link ObjectHelper#contains(Object, Object, boolean)}
|
||||
* Implements the contains predicate, delegating to {@link ObjectHelper#contains(Object, Object, boolean, boolean)}
|
||||
*/
|
||||
public class ContainsPredicate extends AbstractSearchPredicate {
|
||||
private final boolean ignoreCase;
|
||||
private final boolean matchAny;
|
||||
|
||||
public ContainsPredicate(Object right, boolean ignoreCase) {
|
||||
public ContainsPredicate(Object right, boolean ignoreCase, boolean matchAny) {
|
||||
super(right);
|
||||
this.ignoreCase = ignoreCase;
|
||||
this.matchAny = matchAny;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object left) {
|
||||
return ObjectHelper.contains(left, this.right, this.ignoreCase);
|
||||
return ObjectHelper.contains(left, this.right, this.ignoreCase, !this.matchAny);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.Locale;
|
|||
import java.util.ResourceBundle;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.exception.StrolchException;
|
||||
import li.strolch.exception.StrolchUserMessageException;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.model.i18n.I18nMessageToJsonVisitor;
|
||||
|
@ -229,10 +230,8 @@ public class ServiceResult {
|
|||
json.addProperty(EXCEPTION_MSG, getExceptionMessageWithCauses(this.throwable, false));
|
||||
json.addProperty(THROWABLE, formatException(this.throwable));
|
||||
|
||||
if (this.throwable instanceof StrolchUserMessageException
|
||||
&& ((StrolchUserMessageException) this.throwable).hasI18n())
|
||||
json.add(I_18_N, ((StrolchUserMessageException) this.throwable).getI18n()
|
||||
.accept(new I18nMessageToJsonVisitor()));
|
||||
if (this.throwable instanceof StrolchException ex && ex.hasI18n())
|
||||
json.add(I_18_N, ex.getI18n().accept(new I18nMessageToJsonVisitor()));
|
||||
}
|
||||
|
||||
if (!json.has(I_18_N) && this.i18nMessage != null)
|
||||
|
|
|
@ -66,6 +66,11 @@ public class StrolchSearchTest {
|
|||
bag.addParameter(new StringParameter("status", "Status", "bla"));
|
||||
bag.addParameter(new StringParameter("color", "Color", "yellow"));
|
||||
|
||||
ball.addParameterBag(new ParameterBag("owner1", "Owner", "Owner"));
|
||||
ball.setString("owner1", "name", "Felix");
|
||||
ball.addParameterBag(new ParameterBag("owner2", "Owner", "Owner"));
|
||||
ball.setString("owner2", "name", "Fox");
|
||||
|
||||
tx.add(ball);
|
||||
}
|
||||
|
||||
|
@ -82,6 +87,11 @@ public class StrolchSearchTest {
|
|||
bag.addParameter(new StringListParameter("stateList", "Status",
|
||||
asList(State.EXECUTION.name(), State.EXECUTED.name())));
|
||||
|
||||
ball.addParameterBag(new ParameterBag("owner1", "Owner", "Owner"));
|
||||
ball.setString("owner1", "name", "Jill");
|
||||
ball.addParameterBag(new ParameterBag("owner2", "Owner", "Owner"));
|
||||
ball.setString("owner2", "name", "Jane");
|
||||
|
||||
tx.add(ball);
|
||||
}
|
||||
|
||||
|
@ -139,8 +149,10 @@ public class StrolchSearchTest {
|
|||
|
||||
try (StrolchTransaction tx = realm.openTx(cert, StrolchSearchTest.class, true)) {
|
||||
|
||||
List<JsonObject> result = new BallSearch("the-id", "STATUS", "yellow").where(
|
||||
element -> element.hasTimedState(STATE_FLOAT_ID)).search(tx).map(a -> a.accept(toJsonVisitor))
|
||||
List<JsonObject> result = new BallSearch("the-id", "STATUS", "yellow")
|
||||
.where(element -> element.hasTimedState(STATE_FLOAT_ID))
|
||||
.search(tx)
|
||||
.map(a -> a.accept(toJsonVisitor))
|
||||
.toList();
|
||||
|
||||
assertEquals(2, result.size());
|
||||
|
@ -174,12 +186,18 @@ public class StrolchSearchTest {
|
|||
StrolchRealm realm = runtimeMock.getAgent().getContainer().getRealm(cert);
|
||||
try (StrolchTransaction tx = realm.openTx(cert, StrolchSearchTest.class, true)) {
|
||||
|
||||
assertEquals(4,
|
||||
new ResourceSearch().types().where(param(BAG_ID, PARAM_STRING_ID, contains("rol"))).search(tx)
|
||||
.toList().size());
|
||||
assertEquals(4,
|
||||
new ResourceSearch().types().where(param(BAG_ID, PARAM_STRING_ID, startsWithIgnoreCase("STR")))
|
||||
.search(tx).toList().size());
|
||||
assertEquals(4, new ResourceSearch()
|
||||
.types()
|
||||
.where(param(BAG_ID, PARAM_STRING_ID, contains("rol")))
|
||||
.search(tx)
|
||||
.toList()
|
||||
.size());
|
||||
assertEquals(4, new ResourceSearch()
|
||||
.types()
|
||||
.where(param(BAG_ID, PARAM_STRING_ID, startsWithIgnoreCase("STR")))
|
||||
.search(tx)
|
||||
.toList()
|
||||
.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,11 +248,14 @@ public class StrolchSearchTest {
|
|||
@Override
|
||||
public void define() {
|
||||
|
||||
DateRange dateRange = new DateRange().from(ISO8601.parseToZdt("2012-01-01T00:00:00.000+01:00"),
|
||||
true).to(ISO8601.parseToZdt("2013-01-01T00:00:00.000+01:00"), true);
|
||||
DateRange dateRange = new DateRange()
|
||||
.from(ISO8601.parseToZdt("2012-01-01T00:00:00.000+01:00"), true)
|
||||
.to(ISO8601.parseToZdt("2013-01-01T00:00:00.000+01:00"), true);
|
||||
|
||||
types().where(date().isEqualTo(Instant.ofEpochMilli(1384929777699L).atZone(systemDefault()))
|
||||
.or(state().isEqualTo(State.CREATED)
|
||||
types().where(date()
|
||||
.isEqualTo(Instant.ofEpochMilli(1384929777699L).atZone(systemDefault()))
|
||||
.or(state()
|
||||
.isEqualTo(State.CREATED)
|
||||
.and(param(BAG_ID, PARAM_STRING_ID).isEqualTo("Strolch"))
|
||||
.and(param(BAG_ID, PARAM_DATE_ID).inRange(dateRange))));
|
||||
}
|
||||
|
@ -286,18 +307,30 @@ public class StrolchSearchTest {
|
|||
try (StrolchTransaction tx = realm.openTx(cert, StrolchSearchTest.class, true)) {
|
||||
|
||||
ZonedDateTime dateTime = ISO8601.parseToZdt("2013-11-20T07:42:57.699+01:00");
|
||||
assertEquals(0,
|
||||
new OrderSearch().types("TestType").where(date().isBefore(dateTime, false)).search(tx).toList()
|
||||
.size());
|
||||
assertEquals(1,
|
||||
new OrderSearch().types("TestType").where(date().isBefore(dateTime, true)).search(tx).toList()
|
||||
.size());
|
||||
assertEquals(0,
|
||||
new OrderSearch().types("TestType").where(date().isAfter(dateTime, false)).search(tx).toList()
|
||||
.size());
|
||||
assertEquals(1,
|
||||
new OrderSearch().types("TestType").where(date().isAfter(dateTime, true)).search(tx).toList()
|
||||
.size());
|
||||
assertEquals(0, new OrderSearch()
|
||||
.types("TestType")
|
||||
.where(date().isBefore(dateTime, false))
|
||||
.search(tx)
|
||||
.toList()
|
||||
.size());
|
||||
assertEquals(1, new OrderSearch()
|
||||
.types("TestType")
|
||||
.where(date().isBefore(dateTime, true))
|
||||
.search(tx)
|
||||
.toList()
|
||||
.size());
|
||||
assertEquals(0, new OrderSearch()
|
||||
.types("TestType")
|
||||
.where(date().isAfter(dateTime, false))
|
||||
.search(tx)
|
||||
.toList()
|
||||
.size());
|
||||
assertEquals(1, new OrderSearch()
|
||||
.types("TestType")
|
||||
.where(date().isAfter(dateTime, true))
|
||||
.search(tx)
|
||||
.toList()
|
||||
.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,8 +382,12 @@ public class StrolchSearchTest {
|
|||
StrolchRealm realm = runtimeMock.getAgent().getContainer().getRealm(cert);
|
||||
try (StrolchTransaction tx = realm.openTx(cert, StrolchSearchTest.class, true)) {
|
||||
|
||||
assertEquals(1, new ActivitySearch().types("sdf", "ActivityType")
|
||||
.where(element -> element.getActionsByType("Use").size() == 4).search(tx).toList().size());
|
||||
assertEquals(1, new ActivitySearch()
|
||||
.types("sdf", "ActivityType")
|
||||
.where(element -> element.getActionsByType("Use").size() == 4)
|
||||
.search(tx)
|
||||
.toList()
|
||||
.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -405,8 +442,8 @@ public class StrolchSearchTest {
|
|||
public void define() {
|
||||
types("Ball").where(id(isEqualTo(this.id)).or( //
|
||||
|
||||
param("parameters", "status", isEqualTo(this.status)).and(
|
||||
not(param("parameters", "color", isEqualTo(this.color))))
|
||||
param("parameters", "status", isEqualTo(this.status))
|
||||
.and(not(param("parameters", "color", isEqualTo(this.color))))
|
||||
|
||||
.and(param("parameters", "state", isEqualTo(State.EXECUTION)))
|
||||
.and(param("parameters", "state", isEqualToIgnoreCase(State.EXECUTION)))
|
||||
|
@ -469,6 +506,8 @@ public class StrolchSearchTest {
|
|||
.and(param(BAG_ID, PARAM_LIST_STRING_ID, listContains("World")))
|
||||
.and(param(BAG_ID, PARAM_LIST_STRING_ID, listContains("World1")).not())
|
||||
|
||||
.and(paramOnBagType("Owner", "name").isInArray("Felix", "Jill"))
|
||||
|
||||
.and(paramNull(BAG_ID, "non-existant"))
|
||||
//
|
||||
));
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -26,14 +26,12 @@ public class StrolchAccessDeniedException extends StrolchException {
|
|||
|
||||
private final Certificate certificate;
|
||||
private final Restrictable restrictable;
|
||||
private final I18nMessage i18n;
|
||||
|
||||
public StrolchAccessDeniedException(Certificate certificate, Restrictable restrictable, I18nMessage i18n,
|
||||
Throwable cause) {
|
||||
super(i18n.getMessage(), cause);
|
||||
super(i18n, cause);
|
||||
this.certificate = certificate;
|
||||
this.restrictable = restrictable;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
public Certificate getCertificate() {
|
||||
|
@ -43,8 +41,4 @@ public class StrolchAccessDeniedException extends StrolchException {
|
|||
public Restrictable getRestrictable() {
|
||||
return restrictable;
|
||||
}
|
||||
|
||||
public I18nMessage getI18n() {
|
||||
return this.i18n;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,16 +15,17 @@
|
|||
*/
|
||||
package li.strolch.exception;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import li.strolch.utils.I18nMessage;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
public class StrolchException extends RuntimeException {
|
||||
|
||||
private I18nMessage i18n;
|
||||
protected I18nMessage i18n;
|
||||
|
||||
public StrolchException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
|
@ -36,6 +37,41 @@ public class StrolchException extends RuntimeException {
|
|||
|
||||
public StrolchException(I18nMessage i18n) {
|
||||
super(i18n.getMessage(Locale.getDefault()));
|
||||
this.i18n = i18n;
|
||||
if (i18n.hasException())
|
||||
initCause(i18n.getException());
|
||||
}
|
||||
|
||||
public StrolchException(I18nMessage i18n, Throwable cause) {
|
||||
super(i18n.getMessage(Locale.getDefault()), cause);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key) {
|
||||
this(new I18nMessage(bundle, key));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop, Object value) {
|
||||
this(new I18nMessage(bundle, key).value(prop, value));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2) {
|
||||
this(new I18nMessage(bundle, key).value(prop1, value1).value(prop2, value2));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2, Object value2,
|
||||
String prop3, Object value3) {
|
||||
this(new I18nMessage(bundle, key).value(prop1, value1).value(prop2, value2).value(prop3, value3));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2, Object value2,
|
||||
String prop3, Object value3, String prop4, Object value4) {
|
||||
this(new I18nMessage(bundle, key)
|
||||
.value(prop1, value1)
|
||||
.value(prop2, value2)
|
||||
.value(prop3, value3)
|
||||
.value(prop4, value4));
|
||||
}
|
||||
|
||||
public boolean hasI18n() {
|
||||
|
@ -54,4 +90,9 @@ public class StrolchException extends RuntimeException {
|
|||
this.i18n = i18n;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StrolchException cause(Throwable cause) {
|
||||
initCause(cause);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +1,40 @@
|
|||
package li.strolch.exception;
|
||||
|
||||
import li.strolch.utils.I18nMessage;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import li.strolch.utils.I18nMessage;
|
||||
|
||||
public class StrolchUserMessageException extends StrolchException {
|
||||
|
||||
private I18nMessage i18n;
|
||||
|
||||
public StrolchUserMessageException(I18nMessage i18n) {
|
||||
super(i18n.getMessage(Locale.getDefault()));
|
||||
this.i18n = i18n;
|
||||
if (i18n.hasException())
|
||||
initCause(i18n.getException());
|
||||
super(i18n);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(I18nMessage i18n, Throwable cause) {
|
||||
super(i18n.getMessage(Locale.getDefault()), cause);
|
||||
this.i18n = i18n;
|
||||
super(i18n, cause);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key) {
|
||||
this(new I18nMessage(bundle, key));
|
||||
super(bundle, key);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop, Object value) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop, value));
|
||||
super(bundle, key, prop, value);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop1, value1) //
|
||||
.value(prop2, value2));
|
||||
super(bundle, key, prop1, value1, prop2, value2);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2, String prop3, Object value3) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop1, value1) //
|
||||
.value(prop2, value2) //
|
||||
.value(prop3, value3));
|
||||
super(bundle, key, prop1, value1, prop2, value2, prop3, value3);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2, String prop3, Object value3, String prop4, Object value4) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop1, value1) //
|
||||
.value(prop2, value2) //
|
||||
.value(prop3, value3) //
|
||||
.value(prop4, value4));
|
||||
}
|
||||
|
||||
public StrolchUserMessageException cause(Throwable cause) {
|
||||
initCause(cause);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasI18n() {
|
||||
return this.i18n != null;
|
||||
}
|
||||
|
||||
public I18nMessage getI18n() {
|
||||
return this.i18n;
|
||||
}
|
||||
|
||||
public void setI18n(I18nMessage i18n) {
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
public StrolchUserMessageException i18n(I18nMessage i18n) {
|
||||
this.i18n = i18n;
|
||||
return this;
|
||||
super(bundle, key, prop1, value1, prop2, value2, prop3, value3, prop4, value4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,8 @@ public abstract class AbstractStrolchElement implements StrolchElement {
|
|||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param id
|
||||
* id of this {@link StrolchElement}
|
||||
* @param name
|
||||
* name of this {@link StrolchElement}
|
||||
* @param id id of this {@link StrolchElement}
|
||||
* @param name name of this {@link StrolchElement}
|
||||
*/
|
||||
public AbstractStrolchElement(String id, String name) {
|
||||
setId(id);
|
||||
|
@ -98,16 +96,15 @@ public abstract class AbstractStrolchElement implements StrolchElement {
|
|||
* Used to build a {@link Locator} for this {@link StrolchElement}. It must be implemented by the concrete
|
||||
* implemented as parents must first add their {@link Locator} information
|
||||
*
|
||||
* @param locatorBuilder
|
||||
* the {@link LocatorBuilder} to which the {@link StrolchElement} must add its locator information
|
||||
* @param locatorBuilder the {@link LocatorBuilder} to which the {@link StrolchElement} must add its locator
|
||||
* information
|
||||
*/
|
||||
protected abstract void fillLocator(LocatorBuilder locatorBuilder);
|
||||
|
||||
/**
|
||||
* fills the {@link StrolchElement} clone with the id, name and type
|
||||
*
|
||||
* @param clone
|
||||
* the clone to fill
|
||||
* @param clone the clone to fill
|
||||
*/
|
||||
protected void fillClone(AbstractStrolchElement clone) {
|
||||
clone.id = this.id;
|
||||
|
@ -139,5 +136,7 @@ public abstract class AbstractStrolchElement implements StrolchElement {
|
|||
public abstract int hashCode();
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
public String toString() {
|
||||
return getLocator().toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import li.strolch.model.policy.PolicyDefs;
|
|||
import li.strolch.model.visitor.StrolchElementVisitor;
|
||||
import li.strolch.model.xml.StrolchXmlHelper;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.iso8601.ISO8601;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.time.*;
|
||||
|
@ -331,9 +330,9 @@ public class Order extends AbstractStrolchRootElement implements StrolchRootElem
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "Order [id=" + this.id + ", name=" + this.name + ", type=" + this.type + ", state=" + this.state +
|
||||
", date=" + ISO8601.toString(this.date) + ", version=" + this.version + "]";
|
||||
if (this.version == null)
|
||||
return getLocator().toString();
|
||||
return getLocator() + ", Version: " + this.version.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -191,7 +191,9 @@ public class Resource extends AbstractStrolchRootElement implements StrolchRootE
|
|||
if (this.timedStateMap == null || this.timedStateMap.isEmpty())
|
||||
return Stream.empty();
|
||||
|
||||
return this.timedStateMap.values().stream()
|
||||
return this.timedStateMap
|
||||
.values()
|
||||
.stream()
|
||||
.filter(s -> s.getInterpretation().equals(interpretation) && s.getUom().equals(uom));
|
||||
}
|
||||
|
||||
|
@ -399,8 +401,9 @@ public class Resource extends AbstractStrolchRootElement implements StrolchRootE
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "Resource [id=" + this.id + ", name=" + this.name + ", type=" + this.type + ", version=" + this.version;
|
||||
if (this.version == null)
|
||||
return getLocator().toString();
|
||||
return getLocator() + ", Version: " + this.version.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -72,6 +72,7 @@ public class StrolchModelConstants {
|
|||
|
||||
public static final String BAG_RELATIONS = "relations";
|
||||
public static final String BAG_PARAMETERS = "parameters";
|
||||
public static final String BAG_VISIBILITY = "visibility";
|
||||
public static final String TYPE_PARAMETERS = "Parameters";
|
||||
public static final String TYPE_VERSION = "Version";
|
||||
public static final String TYPE_MEMORY = "Memory";
|
||||
|
@ -82,6 +83,10 @@ public class StrolchModelConstants {
|
|||
public static final String TYPE_CONFIGURATION = "Configuration";
|
||||
public static final String TYPE_OBJECTIVES = "Objectives";
|
||||
public static final String TYPE_METRIC = "Metric";
|
||||
public static final String TYPE_NOTIFICATION = "Notification";
|
||||
public static final String TYPE_VISIBILITY = "Visibility";
|
||||
public static final String TYPE_TEXT = "Text";
|
||||
public static final String TYPE_LOCATION = "Location";
|
||||
|
||||
public static final String RES_CONFIGURATION = "configuration";
|
||||
|
||||
|
@ -99,6 +104,17 @@ public class StrolchModelConstants {
|
|||
public static final String PARAM_START_DATE = "startDate";
|
||||
public static final String PARAM_MODE = "mode";
|
||||
public static final String PARAM_GROUP = "group";
|
||||
public static final String PARAM_ENABLED = "enabled";
|
||||
public static final String PARAM_VISIBLE_FROM = "visibleFrom";
|
||||
public static final String PARAM_VISIBLE_TO = "visibleTo";
|
||||
public static final String PARAM_TITLE = "title";
|
||||
public static final String PARAM_TEXT = "text";
|
||||
public static final String PARAM_FOR_ALL = "forAll";
|
||||
public static final String PARAM_ROLES = "roles";
|
||||
public static final String PARAM_LOCATIONS = "locations";
|
||||
public static final String PARAM_LOCATION_NAMES = "locationNames";
|
||||
public static final String PARAM_LANGUAGES = "languages";
|
||||
public static final String PARAM_GROUPS = "groups";
|
||||
|
||||
public static class PolicyConstants {
|
||||
public static final String POLICY_DEFAULT = "Default";
|
||||
|
|
|
@ -16,15 +16,6 @@
|
|||
|
||||
package li.strolch.model.activity;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_RELATIONS;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
|
||||
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
import li.strolch.exception.StrolchModelException;
|
||||
import li.strolch.exception.StrolchPolicyException;
|
||||
import li.strolch.model.*;
|
||||
|
@ -37,6 +28,15 @@ import li.strolch.model.timevalue.IValueChange;
|
|||
import li.strolch.model.visitor.StrolchElementVisitor;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_RELATIONS;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
|
||||
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
|
||||
|
||||
/**
|
||||
* An {@link Action} represents a single step within an {@link Activity}, that is, one that is not further decomposed
|
||||
* within the {@link Activity}. A {@link Activity} applies {@link IValueChange} objects at the start and end time of the
|
||||
|
@ -107,8 +107,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resourceId
|
||||
* the id of the {@link Resource} the {@link Action} acts on
|
||||
* @param resourceId the id of the {@link Resource} the {@link Action} acts on
|
||||
*/
|
||||
public void setResourceId(String resourceId) {
|
||||
assertNotReadonly();
|
||||
|
@ -124,8 +123,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param state
|
||||
* the target {@code State} of the a {@code Action}
|
||||
* @param state the target {@code State} of the a {@code Action}
|
||||
*/
|
||||
public void setState(State state) {
|
||||
assertNotReadonly();
|
||||
|
@ -140,8 +138,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resourceType
|
||||
* the resource type
|
||||
* @param resourceType the resource type
|
||||
*/
|
||||
public void setResourceType(String resourceType) {
|
||||
assertNotReadonly();
|
||||
|
@ -151,8 +148,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
/**
|
||||
* Sets the resource type and id from the given {@link Resource}
|
||||
*
|
||||
* @param resource
|
||||
* the resource from which to get the type and id
|
||||
* @param resource the resource from which to get the type and id
|
||||
*/
|
||||
public void setResource(Resource resource) {
|
||||
assertNotReadonly();
|
||||
|
@ -182,8 +178,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
*
|
||||
* @return the {@link Locator} for the {@link Resource} for this action
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if the resource is not defined
|
||||
* @throws IllegalStateException if the resource is not defined
|
||||
*/
|
||||
public Locator getResourceLocator() {
|
||||
if (!isResourceDefined())
|
||||
|
@ -201,8 +196,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param change
|
||||
* {@code IValueChange} to be applied to the {@code Resource}
|
||||
* @param change {@code IValueChange} to be applied to the {@code Resource}
|
||||
*
|
||||
* @return <tt>true</tt> (as specified by {@link Collection#add})
|
||||
*/
|
||||
|
@ -383,14 +377,14 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Action [id=" + this.id + ", name=" + this.name + ", type=" + this.type + ", resourceId="
|
||||
+ this.resourceId + ", state=" + this.state + "]";
|
||||
return getLocator() + ", resourceId: " + this.resourceId + ", state=" + this.state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParent(Activity activity) {
|
||||
assertNotReadonly();
|
||||
this.parent = activity;
|
||||
this.locator = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -495,7 +495,8 @@ public class Activity extends AbstractStrolchRootElement
|
|||
|
||||
public <T extends IActivityElement> T findElement(Predicate<IActivityElement> predicate,
|
||||
Supplier<String> msgSupplier) {
|
||||
@SuppressWarnings("unchecked") T t = (T) streamElements().filter(predicate)
|
||||
@SuppressWarnings("unchecked") T t = (T) streamElements()
|
||||
.filter(predicate)
|
||||
.collect(singletonCollector(msgSupplier));
|
||||
return t;
|
||||
}
|
||||
|
@ -778,23 +779,17 @@ public class Activity extends AbstractStrolchRootElement
|
|||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("Activity [id=");
|
||||
builder.append(this.id);
|
||||
builder.append(", name=");
|
||||
builder.append(this.name);
|
||||
builder.append(", type=");
|
||||
builder.append(this.type);
|
||||
builder.append(getLocator());
|
||||
builder.append(", state=");
|
||||
builder.append(getState());
|
||||
builder.append(", start=");
|
||||
builder.append(getStart());
|
||||
builder.append(", end=");
|
||||
builder.append(getEnd());
|
||||
if (isRootElement()) {
|
||||
if (isRootElement() && this.version != null) {
|
||||
builder.append(", version=");
|
||||
builder.append(this.version);
|
||||
builder.append(this.version.getVersion());
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
@ -892,6 +887,7 @@ public class Activity extends AbstractStrolchRootElement
|
|||
public void setParent(Activity activity) {
|
||||
assertNotReadonly();
|
||||
this.parent = activity;
|
||||
this.locator = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,8 @@ import li.strolch.model.PolicyContainer;
|
|||
import li.strolch.model.activity.Action;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public class ActionBuilder extends PolicyContainerBuilder<ActionBuilder> implements ActivityElementBuilder {
|
||||
|
||||
private final ActivityBuilder builder;
|
||||
|
@ -11,11 +13,19 @@ public class ActionBuilder extends PolicyContainerBuilder<ActionBuilder> impleme
|
|||
private String resourceId;
|
||||
private String resourceType;
|
||||
|
||||
public ActionBuilder(String id, String type) {
|
||||
this(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ActionBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
}
|
||||
|
||||
public ActionBuilder(ActivityBuilder builder, String id, String type) {
|
||||
this(builder, id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ActionBuilder(ActivityBuilder builder, String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = builder;
|
||||
|
|
|
@ -7,6 +7,8 @@ import li.strolch.model.activity.Activity;
|
|||
import li.strolch.model.activity.TimeOrdering;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.*;
|
||||
|
||||
public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> implements ActivityElementBuilder {
|
||||
|
||||
private final StrolchElementBuilder builder;
|
||||
|
@ -15,6 +17,10 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
|
||||
private final List<ActivityElementBuilder> builders;
|
||||
|
||||
public ActivityBuilder(String id, String type, TimeOrdering timeOrdering) {
|
||||
this(id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder(String id, String name, String type, TimeOrdering timeOrdering) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
|
@ -23,6 +29,10 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
this.builders = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, String id, String type, TimeOrdering timeOrdering) {
|
||||
this(builder, id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, String id, String name, String type,
|
||||
TimeOrdering timeOrdering) {
|
||||
super(id, name, type);
|
||||
|
@ -32,6 +42,11 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
this.builders = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, ActivityBuilder parentBuilder, String id, String type,
|
||||
TimeOrdering timeOrdering) {
|
||||
this(builder, parentBuilder, id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, ActivityBuilder parentBuilder, String id, String name,
|
||||
String type, TimeOrdering timeOrdering) {
|
||||
super(id, name, type);
|
||||
|
@ -41,12 +56,20 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
this.builders = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ActivityBuilder subActivity(String id, String type, TimeOrdering timeOrdering) {
|
||||
return subActivity(id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder subActivity(String id, String name, String type, TimeOrdering timeOrdering) {
|
||||
ActivityBuilder builder = new ActivityBuilder(this.builder, this, id, name, type, timeOrdering);
|
||||
this.builders.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public ActionBuilder action(String id, String type) {
|
||||
return action(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ActionBuilder action(String id, String name, String type) {
|
||||
ActionBuilder builder = new ActionBuilder(this, id, name, type);
|
||||
this.builders.add(builder);
|
||||
|
|
|
@ -7,6 +7,8 @@ import li.strolch.model.ParameterBag;
|
|||
import li.strolch.model.ParameterBagContainer;
|
||||
import li.strolch.model.builder.params.*;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public class BagBuilder<T extends ParameterBagContainerBuilder<T>> {
|
||||
|
||||
private final T builder;
|
||||
|
@ -15,6 +17,10 @@ public class BagBuilder<T extends ParameterBagContainerBuilder<T>> {
|
|||
private final String type;
|
||||
private final List<ParameterBuilder<?, ?, ?>> parameters;
|
||||
|
||||
public BagBuilder(T builder, String id, String type) {
|
||||
this(builder, id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public BagBuilder(T builder, String id, String name, String type) {
|
||||
this.builder = builder;
|
||||
this.id = id;
|
||||
|
@ -23,72 +29,120 @@ public class BagBuilder<T extends ParameterBagContainerBuilder<T>> {
|
|||
this.parameters = new ArrayList<>();
|
||||
}
|
||||
|
||||
public StringParamBuilder<T> string(String id) {
|
||||
return string(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public StringParamBuilder<T> string(String id, String name) {
|
||||
StringParamBuilder<T> builder = new StringParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public TextParamBuilder<T> text(String id) {
|
||||
return text(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public TextParamBuilder<T> text(String id, String name) {
|
||||
TextParamBuilder<T> builder = new TextParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public BooleanParamBuilder<T> booleanB(String id) {
|
||||
return booleanB(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public BooleanParamBuilder<T> booleanB(String id, String name) {
|
||||
BooleanParamBuilder<T> builder = new BooleanParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerParamBuilder<T> integer(String id) {
|
||||
return integer(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerParamBuilder<T> integer(String id, String name) {
|
||||
IntegerParamBuilder<T> builder = new IntegerParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public LongParamBuilder<T> longB(String id) {
|
||||
return longB(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public LongParamBuilder<T> longB(String id, String name) {
|
||||
LongParamBuilder<T> builder = new LongParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatParamBuilder<T> floatB(String id) {
|
||||
return floatB(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatParamBuilder<T> floatB(String id, String name) {
|
||||
FloatParamBuilder<T> builder = new FloatParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public DateParamBuilder<T> date(String id) {
|
||||
return date(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public DateParamBuilder<T> date(String id, String name) {
|
||||
DateParamBuilder<T> builder = new DateParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public DurationParamBuilder<T> duration(String id) {
|
||||
return duration(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public DurationParamBuilder<T> duration(String id, String name) {
|
||||
DurationParamBuilder<T> builder = new DurationParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public StringListParamBuilder<T> stringList(String id) {
|
||||
return stringList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public StringListParamBuilder<T> stringList(String id, String name) {
|
||||
StringListParamBuilder<T> builder = new StringListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerListParamBuilder<T> integerList(String id) {
|
||||
return integerList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerListParamBuilder<T> integerList(String id, String name) {
|
||||
IntegerListParamBuilder<T> builder = new IntegerListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public LongListParamBuilder<T> longList(String id) {
|
||||
return longList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public LongListParamBuilder<T> longList(String id, String name) {
|
||||
LongListParamBuilder<T> builder = new LongListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatListParamBuilder<T> floatList(String id) {
|
||||
return floatList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatListParamBuilder<T> floatList(String id, String name) {
|
||||
FloatListParamBuilder<T> builder = new FloatListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
|
|
|
@ -5,15 +5,25 @@ import li.strolch.model.State;
|
|||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.iso8601.ISO8601;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.*;
|
||||
|
||||
public class OrderBuilder extends RootElementBuilder<OrderBuilder> {
|
||||
|
||||
private final StrolchElementBuilder builder;
|
||||
|
||||
public OrderBuilder(String id, String type) {
|
||||
this(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public OrderBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
}
|
||||
|
||||
public OrderBuilder(StrolchElementBuilder builder, String id, String type) {
|
||||
this(builder, id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public OrderBuilder(StrolchElementBuilder builder, String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = builder;
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
package li.strolch.model.builder;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamId;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.model.ParameterBag;
|
||||
import li.strolch.model.ParameterBagContainer;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.model.parameter.StringListParameter;
|
||||
import li.strolch.model.parameter.StringParameter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamId;
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public abstract class ParameterBagContainerBuilder<T extends ParameterBagContainerBuilder<T>> {
|
||||
|
||||
private final String id;
|
||||
|
@ -24,6 +25,10 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
private final Map<String, String[]> singleRelations;
|
||||
private final Map<String, String[]> multiRelations;
|
||||
|
||||
public ParameterBagContainerBuilder(String id, String type) {
|
||||
this(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ParameterBagContainerBuilder(String id, String name, String type) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
|
@ -58,9 +63,12 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return bag(BAG_RELATIONS, TYPE_RELATIONS, TYPE_RELATIONS);
|
||||
}
|
||||
|
||||
public BagBuilder<T> bag(String id, String type) {
|
||||
return bag(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public BagBuilder<T> bag(String id, String name, String type) {
|
||||
@SuppressWarnings("unchecked")
|
||||
BagBuilder<T> bagBuilder = new BagBuilder<>((T) this, id, name, type);
|
||||
@SuppressWarnings("unchecked") BagBuilder<T> bagBuilder = new BagBuilder<>((T) this, id, name, type);
|
||||
if (this.parametersBags.put(id, bagBuilder) != null)
|
||||
throw new IllegalArgumentException("Bag builder for " + id + " already exists!");
|
||||
return bagBuilder;
|
||||
|
@ -70,11 +78,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return resourceRelation(buildParamId(type), type, type);
|
||||
}
|
||||
|
||||
public T resourceRelation(String paramId, String type) {
|
||||
return resourceRelation(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T resourceRelation(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.singleRelations.put(paramId, new String[] { paramName, type, Tags.RESOURCE });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.singleRelations.put(paramId, new String[]{paramName, type, Tags.RESOURCE});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -82,11 +93,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return resourceRelations(buildParamId(type), type + "s", type);
|
||||
}
|
||||
|
||||
public T resourceRelations(String paramId, String type) {
|
||||
return resourceRelations(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T resourceRelations(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.multiRelations.put(paramId, new String[] { paramName, type, Tags.RESOURCE });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.multiRelations.put(paramId, new String[]{paramName, type, Tags.RESOURCE});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -94,11 +108,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return orderRelation(buildParamId(type), type, type);
|
||||
}
|
||||
|
||||
public T orderRelation(String paramId, String type) {
|
||||
return orderRelation(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T orderRelation(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.singleRelations.put(paramId, new String[] { paramName, type, Tags.ORDER });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.singleRelations.put(paramId, new String[]{paramName, type, Tags.ORDER});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -106,11 +123,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return orderRelations(buildParamId(type), type + "s", type);
|
||||
}
|
||||
|
||||
public T orderRelations(String paramId, String type) {
|
||||
return orderRelations(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T orderRelations(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.multiRelations.put(paramId, new String[] { paramName, type, Tags.ORDER });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.multiRelations.put(paramId, new String[]{paramName, type, Tags.ORDER});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -118,11 +138,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return activityRelation(buildParamId(type), type, type);
|
||||
}
|
||||
|
||||
public T activityRelation(String paramId, String type) {
|
||||
return activityRelation(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T activityRelation(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.singleRelations.put(paramId, new String[] { paramName, type, Tags.ACTIVITY });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.singleRelations.put(paramId, new String[]{paramName, type, Tags.ACTIVITY});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -130,11 +153,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return activityRelations(buildParamId(type), type + "s", type);
|
||||
}
|
||||
|
||||
public T activityRelations(String paramId, String type) {
|
||||
return activityRelations(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T activityRelations(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.multiRelations.put(paramId, new String[] { paramName, type, Tags.ACTIVITY });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.multiRelations.put(paramId, new String[]{paramName, type, Tags.ACTIVITY});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,18 +3,23 @@ package li.strolch.model.builder;
|
|||
import li.strolch.model.ParameterBagContainer;
|
||||
import li.strolch.model.PolicyContainer;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public class PolicyContainerBuilder<T extends ParameterBagContainerBuilder<T>> extends ParameterBagContainerBuilder<T> {
|
||||
|
||||
private PoliciesBuilder<T> policies;
|
||||
|
||||
public PolicyContainerBuilder(String id, String type) {
|
||||
super(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public PolicyContainerBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
}
|
||||
|
||||
public PoliciesBuilder<T> policies() {
|
||||
if (this.policies == null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
this.policies = new PoliciesBuilder<>(t);
|
||||
}
|
||||
return policies;
|
||||
|
|
|
@ -1,65 +1,104 @@
|
|||
package li.strolch.model.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.builder.states.*;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
import static li.strolch.utils.helper.StringHelper.getUniqueId;
|
||||
|
||||
public class ResourceBuilder extends RootElementBuilder<ResourceBuilder> {
|
||||
|
||||
private final StrolchElementBuilder builder;
|
||||
private final List<TimedStateBuilder<?>> timedStates;
|
||||
|
||||
public ResourceBuilder(String name, String type) {
|
||||
this(getUniqueId(), name, type);
|
||||
}
|
||||
|
||||
public ResourceBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
this.timedStates = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ResourceBuilder(StrolchElementBuilder builder, String name, String type) {
|
||||
this(builder, getUniqueId(), name, type);
|
||||
}
|
||||
|
||||
public ResourceBuilder(StrolchElementBuilder builder, String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = builder;
|
||||
this.timedStates = new ArrayList<>();
|
||||
}
|
||||
|
||||
public BooleanStateBuilder booleanState(String id) {
|
||||
return booleanState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public BooleanStateBuilder booleanState(String id, String name) {
|
||||
BooleanStateBuilder builder = new BooleanStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatStateBuilder floatState(String id) {
|
||||
return floatState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatStateBuilder floatState(String id, String name) {
|
||||
FloatStateBuilder builder = new FloatStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerStateBuilder integerState(String id) {
|
||||
return integerState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerStateBuilder integerState(String id, String name) {
|
||||
IntegerStateBuilder builder = new IntegerStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public LongStateBuilder longState(String id) {
|
||||
return longState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public LongStateBuilder longState(String id, String name) {
|
||||
LongStateBuilder builder = new LongStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatListStateBuilder floatListState(String id) {
|
||||
return floatListState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatListStateBuilder floatListState(String id, String name) {
|
||||
FloatListStateBuilder builder = new FloatListStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerListStateBuilder integerListState(String id) {
|
||||
return integerListState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerListStateBuilder integerListState(String id, String name) {
|
||||
IntegerListStateBuilder builder = new IntegerListStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public StringSetStateBuilder stringSetState(String id) {
|
||||
return stringSetState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public StringSetStateBuilder stringSetState(String id, String name) {
|
||||
StringSetStateBuilder builder = new StringSetStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
package li.strolch.model.builder;
|
||||
|
||||
import li.strolch.model.PolicyContainer;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public abstract class RootElementBuilder<T extends ParameterBagContainerBuilder<T>> extends PolicyContainerBuilder<T> {
|
||||
|
||||
public RootElementBuilder(String id, String type) {
|
||||
super(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public RootElementBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
}
|
||||
|
|
|
@ -27,19 +27,19 @@ public class StrolchElementBuilder {
|
|||
this.activityBuilders = new HashMap<>();
|
||||
}
|
||||
|
||||
public ResourceBuilder resource(String name, String type) {
|
||||
public ResourceBuilder resourceTemplate(String name, String type) {
|
||||
ResourceBuilder builder = new ResourceBuilder(this, type, name, TEMPLATE);
|
||||
this.resourceBuilders.put(type, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public OrderBuilder order(String name, String type) {
|
||||
public OrderBuilder orderTemplate(String name, String type) {
|
||||
OrderBuilder builder = new OrderBuilder(this, type, name, TEMPLATE);
|
||||
this.orderBuilders.put(type, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public ActivityBuilder activity(String name, String type, TimeOrdering timeOrdering) {
|
||||
public ActivityBuilder activityTemplate(String name, String type, TimeOrdering timeOrdering) {
|
||||
ActivityBuilder builder = new ActivityBuilder(this, type, name, TEMPLATE, timeOrdering);
|
||||
this.activityBuilders.put(type, builder);
|
||||
return builder;
|
||||
|
@ -62,7 +62,7 @@ public class StrolchElementBuilder {
|
|||
|
||||
public List<StrolchRootElement> buildTemplates() {
|
||||
return concat(concat(this.resourceBuilders.values().stream(), //
|
||||
this.orderBuilders.values().stream()), //
|
||||
this.orderBuilders.values().stream()), //
|
||||
this.activityBuilders.values().stream()) //
|
||||
.map(RootElementBuilder::build).collect(toList());
|
||||
}
|
||||
|
@ -71,24 +71,24 @@ public class StrolchElementBuilder {
|
|||
ResourceBuilder builder = this.resourceBuilders.get(type);
|
||||
if (builder == null)
|
||||
throw new IllegalArgumentException("No resource template defined for type " + type);
|
||||
return updateFields(type, newName, builder.build());
|
||||
return setInitialFields(type, newName, builder.build());
|
||||
}
|
||||
|
||||
public Order newOrder(String type, String newName) {
|
||||
OrderBuilder builder = this.orderBuilders.get(type);
|
||||
if (builder == null)
|
||||
throw new IllegalArgumentException("No resource template defined for type " + type);
|
||||
return updateFields(type, newName, builder.build());
|
||||
return setInitialFields(type, newName, builder.build());
|
||||
}
|
||||
|
||||
public Activity newActivity(String type, String newName) {
|
||||
ActivityBuilder builder = this.activityBuilders.get(type);
|
||||
if (builder == null)
|
||||
throw new IllegalArgumentException("No resource template defined for type " + type);
|
||||
return updateFields(type, newName, builder.build());
|
||||
return setInitialFields(type, newName, builder.build());
|
||||
}
|
||||
|
||||
private <T extends StrolchRootElement> T updateFields(String type, String newName, T element) {
|
||||
private <T extends StrolchRootElement> T setInitialFields(String type, String newName, T element) {
|
||||
element.setId(StringHelper.getUniqueId());
|
||||
element.setName(newName);
|
||||
element.setType(type);
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
package li.strolch.model.xml;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static li.strolch.model.StrolchModelConstants.DEFAULT_ENCODING;
|
||||
import static li.strolch.model.StrolchModelConstants.DEFAULT_XML_VERSION;
|
||||
import javanet.staxutils.IndentingXMLStreamWriter;
|
||||
import li.strolch.model.Order;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.model.activity.Activity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import javax.xml.stream.FactoryConfigurationError;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
|
@ -19,14 +22,10 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javanet.staxutils.IndentingXMLStreamWriter;
|
||||
import li.strolch.model.Order;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.model.activity.Activity;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static li.strolch.model.StrolchModelConstants.DEFAULT_ENCODING;
|
||||
import static li.strolch.model.StrolchModelConstants.DEFAULT_XML_VERSION;
|
||||
import static li.strolch.utils.helper.XmlHelper.getSaxParser;
|
||||
|
||||
public class StrolchXmlHelper {
|
||||
|
||||
|
@ -69,8 +68,7 @@ public class StrolchXmlHelper {
|
|||
private static SimpleStrolchElementListener parse(String xml) {
|
||||
try {
|
||||
SimpleStrolchElementListener elementListener = new SimpleStrolchElementListener();
|
||||
SAXParser sp = SAXParserFactory.newInstance().newSAXParser();
|
||||
sp.parse(new ByteArrayInputStream(xml.getBytes(UTF_8)), new XmlModelSaxReader(elementListener));
|
||||
getSaxParser().parse(new ByteArrayInputStream(xml.getBytes(UTF_8)), new XmlModelSaxReader(elementListener));
|
||||
return elementListener;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to parse XML", e);
|
||||
|
|
|
@ -15,19 +15,19 @@
|
|||
*/
|
||||
package li.strolch.model.xml;
|
||||
|
||||
import static li.strolch.utils.helper.StringHelper.*;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.File;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import li.strolch.exception.StrolchException;
|
||||
import li.strolch.model.Tags;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static li.strolch.utils.helper.StringHelper.formatNanoDuration;
|
||||
import static li.strolch.utils.helper.StringHelper.isEmpty;
|
||||
import static li.strolch.utils.helper.XmlHelper.getSaxParser;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -61,7 +61,8 @@ public class XmlModelSaxFileReader extends XmlModelSaxReader {
|
|||
|
||||
File includeFile = new File(this.modelFile.getParentFile(), includeFileS);
|
||||
if (!includeFile.exists() || !includeFile.canRead()) {
|
||||
String msg = "The IncludeFile does not exist, or is not readable. Source model: {0} with IncludeFile: {1}";
|
||||
String msg
|
||||
= "The IncludeFile does not exist, or is not readable. Source model: {0} with IncludeFile: {1}";
|
||||
msg = MessageFormat.format(msg, this.modelFile.getAbsolutePath(), includeFileS);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
@ -78,10 +79,7 @@ public class XmlModelSaxFileReader extends XmlModelSaxReader {
|
|||
long startNanos = System.nanoTime();
|
||||
this.statistics.startTime = LocalDateTime.now();
|
||||
|
||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
SAXParser sp = spf.newSAXParser();
|
||||
|
||||
sp.parse(this.modelFile, this);
|
||||
getSaxParser().parse(this.modelFile, this);
|
||||
|
||||
long endNanos = System.nanoTime();
|
||||
this.statistics.durationNanos = endNanos - startNanos;
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
*/
|
||||
package li.strolch.model.xml;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.DEFAULT_ENCODING;
|
||||
import li.strolch.exception.StrolchException;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
@ -27,12 +30,8 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.text.MessageFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import li.strolch.exception.StrolchException;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import static li.strolch.model.StrolchModelConstants.DEFAULT_ENCODING;
|
||||
import static li.strolch.utils.helper.XmlHelper.getSaxParser;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
|
@ -85,10 +84,7 @@ public class XmlModelSaxStreamReader extends XmlModelSaxReader {
|
|||
long startNanos = System.nanoTime();
|
||||
this.statistics.startTime = LocalDateTime.now();
|
||||
|
||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
SAXParser sp = spf.newSAXParser();
|
||||
|
||||
sp.parse(this.source, this);
|
||||
getSaxParser().parse(this.source, this);
|
||||
|
||||
long endNanos = System.nanoTime();
|
||||
this.statistics.durationNanos = endNanos - startNanos;
|
||||
|
|
|
@ -33,7 +33,7 @@ public class StrolchElementBuilderTest {
|
|||
*/
|
||||
|
||||
// person
|
||||
.resource("Person Template", "Person") //
|
||||
.resourceTemplate("Person Template", "Person") //
|
||||
.defaultBag() //
|
||||
.date("birthdate", "Birthdate").value(ZonedDateTime.now()).end() //
|
||||
.string("case", "Case").interpretation("Case").uom("Simple").end() //
|
||||
|
@ -47,7 +47,7 @@ public class StrolchElementBuilderTest {
|
|||
.endResource() //
|
||||
|
||||
// cars
|
||||
.resource("Car Template", "Car") //
|
||||
.resourceTemplate("Car Template", "Car") //
|
||||
.defaultBag() //
|
||||
.string("color", "Color").value("white").end() //
|
||||
.endBag() //
|
||||
|
@ -56,7 +56,7 @@ public class StrolchElementBuilderTest {
|
|||
.endResource() //
|
||||
|
||||
// machines
|
||||
.resource("Machine Template", "Machine") //
|
||||
.resourceTemplate("Machine Template", "Machine") //
|
||||
.defaultBag() //
|
||||
.string("color", "Color").end() //
|
||||
.endBag() //
|
||||
|
@ -68,7 +68,7 @@ public class StrolchElementBuilderTest {
|
|||
*/
|
||||
|
||||
// orders
|
||||
.order("Order Template", "Order") //
|
||||
.orderTemplate("Order Template", "Order") //
|
||||
.defaultBag() //
|
||||
.string("description", "Description").end() //
|
||||
.endBag() //
|
||||
|
@ -83,7 +83,7 @@ public class StrolchElementBuilderTest {
|
|||
*/
|
||||
|
||||
// ToStock
|
||||
.activity("ToStock Template", "ToStock", TimeOrdering.SERIES) //
|
||||
.activityTemplate("ToStock Template", "ToStock", TimeOrdering.SERIES) //
|
||||
.defaultBag() //
|
||||
.string("description", "Description").end() //
|
||||
.endBag() //
|
||||
|
@ -211,7 +211,7 @@ public class StrolchElementBuilderTest {
|
|||
|
||||
Resource car1 = new StrolchElementBuilder() //
|
||||
|
||||
.resource("Car Template", "Car") //
|
||||
.resourceTemplate("Car Template", "Car") //
|
||||
.defaultBag() //
|
||||
.string("color", "Color").value("white").end() //
|
||||
.endBag() //
|
||||
|
|
|
@ -1,19 +1,5 @@
|
|||
package li.strolch.model.activity;
|
||||
|
||||
import static li.strolch.model.ModelGenerator.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import li.strolch.model.timevalue.IValueChange;
|
||||
import li.strolch.model.timevalue.impl.IntegerValue;
|
||||
import li.strolch.model.timevalue.impl.ValueChange;
|
||||
|
@ -25,6 +11,19 @@ import org.slf4j.LoggerFactory;
|
|||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import static li.strolch.model.ModelGenerator.*;
|
||||
import static li.strolch.utils.helper.XmlHelper.getDocumentBuilder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class ActionTest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ActionTest.class);
|
||||
|
@ -76,8 +75,7 @@ public class ActionTest {
|
|||
// @Test
|
||||
public void showToDOM() throws ParserConfigurationException, TransformerException {
|
||||
|
||||
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
Document document = db.newDocument();
|
||||
Document document = getDocumentBuilder().newDocument();
|
||||
Element dom = new StrolchElementToDomVisitor().toDom(this.action);
|
||||
document.appendChild(dom);
|
||||
|
||||
|
|
|
@ -15,10 +15,16 @@
|
|||
*/
|
||||
package li.strolch.model.activity;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import li.strolch.exception.StrolchException;
|
||||
import li.strolch.model.ModelGenerator;
|
||||
import li.strolch.model.State;
|
||||
import li.strolch.model.xml.StrolchElementToDomVisitor;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
|
@ -31,15 +37,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import li.strolch.exception.StrolchException;
|
||||
import li.strolch.model.ModelGenerator;
|
||||
import li.strolch.model.State;
|
||||
import li.strolch.model.xml.StrolchElementToDomVisitor;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import static li.strolch.utils.helper.XmlHelper.getDocumentBuilder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ActivityTest {
|
||||
|
||||
|
@ -342,9 +341,7 @@ public class ActivityTest {
|
|||
// @Test
|
||||
public void showToDOM() throws ParserConfigurationException, TransformerException {
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document document = db.newDocument();
|
||||
Document document = getDocumentBuilder().newDocument();
|
||||
Element dom = new StrolchElementToDomVisitor().toDom(this.activity);
|
||||
document.appendChild(dom);
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -15,15 +15,6 @@
|
|||
*/
|
||||
package li.strolch.persistence.postgresql;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import li.strolch.model.activity.Activity;
|
||||
|
@ -35,18 +26,32 @@ import li.strolch.persistence.api.StrolchPersistenceException;
|
|||
import li.strolch.persistence.api.TransactionResult;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
import static li.strolch.utils.helper.XmlHelper.getSaxParser;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements ActivityDao {
|
||||
|
||||
public static final String ACTIVITIES = "activities";
|
||||
|
||||
private static final String insertAsXmlSqlS = "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, asxml) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?)";
|
||||
private static final String insertAsJsonSqlS = "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, asjson) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?)";
|
||||
private static final String insertAsXmlSqlS
|
||||
= "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, asxml) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?)";
|
||||
private static final String insertAsJsonSqlS
|
||||
= "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, asjson) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?)";
|
||||
|
||||
private static final String updateAsXmlSqlS = "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, asxml = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsJsonSqlS = "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, asjson = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsXmlSqlS
|
||||
= "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, asxml = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsJsonSqlS
|
||||
= "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, asjson = ? where type = ? and id = ? and version = ?";
|
||||
|
||||
private static final String updateLatestSqlS = "update {0} SET latest = false WHERE type = ? and id = ? AND version = ?";
|
||||
private static final String updateLatestSqlS
|
||||
= "update {0} SET latest = false WHERE type = ? and id = ? AND version = ?";
|
||||
|
||||
public PostgreSqlActivityDao(DataType dataType, Connection connection, TransactionResult txResult,
|
||||
boolean versioningEnabled) {
|
||||
|
@ -62,8 +67,7 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
|
|||
protected Activity parseFromXml(String id, String type, SQLXML sqlxml) {
|
||||
SimpleStrolchElementListener listener = new SimpleStrolchElementListener();
|
||||
try (InputStream binaryStream = sqlxml.getBinaryStream()) {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.parse(binaryStream, new XmlModelSaxReader(listener));
|
||||
getSaxParser().parse(binaryStream, new XmlModelSaxReader(listener));
|
||||
} catch (SQLException | IOException | SAXException | ParserConfigurationException e) {
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to extract Activity from sqlxml value for {0} / {1}", id, type), e);
|
||||
|
@ -126,9 +130,9 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
|
|||
}
|
||||
|
||||
} catch (SQLException | SAXException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to insert Activity {0} due to {1}", activity.getLocator(), e.getLocalizedMessage()),
|
||||
e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to insert Activity {0} due to {1}", activity.getLocator(),
|
||||
e.getLocalizedMessage()), e);
|
||||
}
|
||||
|
||||
if (activity.getVersion().isFirstVersion()) {
|
||||
|
@ -146,15 +150,16 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
|
|||
|
||||
int modCount = preparedStatement.executeUpdate();
|
||||
if (modCount != 1) {
|
||||
String msg = "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
String msg
|
||||
= "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
msg = MessageFormat.format(msg, activity.getId(), activity.getVersion().getPreviousVersion(), modCount);
|
||||
throw new StrolchPersistenceException(msg);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to update previous version of Activity {0} due to {1}", activity.getVersion(),
|
||||
e.getLocalizedMessage()), e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to update previous version of Activity {0} due to {1}",
|
||||
activity.getVersion(), e.getLocalizedMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,15 +174,15 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
|
|||
|
||||
// make sure is first version when versioning is not enabled
|
||||
if (!activity.getVersion().isFirstVersion()) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
|
||||
activity.getVersion()));
|
||||
throw new StrolchPersistenceException(MessageFormat.format(
|
||||
"Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
|
||||
activity.getVersion()));
|
||||
}
|
||||
|
||||
// and also not marked as deleted!
|
||||
if (activity.getVersion().isDeleted()) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
|
||||
activity.getVersion()));
|
||||
}
|
||||
|
||||
|
@ -219,9 +224,9 @@ public class PostgreSqlActivityDao extends PostgresqlDao<Activity> implements Ac
|
|||
}
|
||||
|
||||
} catch (SQLException | SAXException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to update Activity {0} due to {1}", activity.getLocator(), e.getLocalizedMessage()),
|
||||
e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to update Activity {0} due to {1}", activity.getLocator(),
|
||||
e.getLocalizedMessage()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,17 +15,6 @@
|
|||
*/
|
||||
package li.strolch.persistence.postgresql;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import li.strolch.model.Order;
|
||||
|
@ -39,31 +28,54 @@ import li.strolch.utils.collections.DateRange;
|
|||
import li.strolch.utils.iso8601.ISO8601;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
import static li.strolch.utils.helper.XmlHelper.getSaxParser;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao {
|
||||
|
||||
public static final String ORDERS = "orders";
|
||||
|
||||
private static final String querySizeDrSqlS = "select count(*) from {0} where latest = true {1}";
|
||||
private static final String querySizeOfTypeDrSqlS = "select count(*) from {0} where type = ANY(?) and latest = true {1}";
|
||||
private static final String querySizeOfTypeDrSqlS
|
||||
= "select count(*) from {0} where type = ANY(?) and latest = true {1}";
|
||||
|
||||
private static final String queryAllDrAsXmlSqlS = "select id, type, asxml from {0} where latest = true {1}";
|
||||
private static final String queryAllDrAsXmlLimitSqlS = "select id, type, asxml from {0} where latest = true {1} order by date {2} limit {3} offset {4}";
|
||||
private static final String queryAllDrAsXmlLimitSqlS
|
||||
= "select id, type, asxml from {0} where latest = true {1} order by date {2} limit {3} offset {4}";
|
||||
private static final String queryAllDrAsJsonSqlS = "select id, type, asjson from {0} where latest = true {1}";
|
||||
private static final String queryAllDrAsJsonLimitSqlS = "select id, type, asjson from {0} where latest = true {1} order by date {2} limit {3} offset {4}";
|
||||
private static final String queryAllDrAsJsonLimitSqlS
|
||||
= "select id, type, asjson from {0} where latest = true {1} order by date {2} limit {3} offset {4}";
|
||||
|
||||
private static final String queryAllByTypeDrAsXmlSqlS = "select id, type, asxml from {0} where type = ANY(?) and latest = true {1}";
|
||||
private static final String queryAllByTypeDrAsXmlLimitSqlS = "select id, type, asxml from {0} where type = ANY(?) and latest = true {1} order by date {2} limit {3,number,#} offset {4,number,#}";
|
||||
private static final String queryAllByTypeDrAsJsonSqlS = "select id, type, asjson from {0} where type = ANY(?) and latest = true {1}";
|
||||
private static final String queryAllByTypeDrAsJsonLimitSqlS = "select id, type, asjson from {0} where type = ANY(?) and latest = true {1} order by date {2} limit {3,number,#} offset {4,number,#}";
|
||||
private static final String queryAllByTypeDrAsXmlSqlS
|
||||
= "select id, type, asxml from {0} where type = ANY(?) and latest = true {1}";
|
||||
private static final String queryAllByTypeDrAsXmlLimitSqlS
|
||||
= "select id, type, asxml from {0} where type = ANY(?) and latest = true {1} order by date {2} limit {3,number,#} offset {4,number,#}";
|
||||
private static final String queryAllByTypeDrAsJsonSqlS
|
||||
= "select id, type, asjson from {0} where type = ANY(?) and latest = true {1}";
|
||||
private static final String queryAllByTypeDrAsJsonLimitSqlS
|
||||
= "select id, type, asjson from {0} where type = ANY(?) and latest = true {1} order by date {2} limit {3,number,#} offset {4,number,#}";
|
||||
|
||||
private static final String insertAsXmlSqlS = "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, date, asxml) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?, ?)";
|
||||
private static final String insertAsJsonSqlS = "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, date, asjson) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?, ?)";
|
||||
private static final String insertAsXmlSqlS
|
||||
= "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, date, asxml) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?, ?)";
|
||||
private static final String insertAsJsonSqlS
|
||||
= "insert into {0} (id, version, created_by, created_at, updated_at, deleted, latest, name, type, state, date, asjson) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?::order_state, ?, ?)";
|
||||
|
||||
private static final String updateAsXmlSqlS = "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, date = ?, asxml = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsJsonSqlS = "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, date = ?, asjson = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsXmlSqlS
|
||||
= "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, date = ?, asxml = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsJsonSqlS
|
||||
= "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, state = ?::order_state, date = ?, asjson = ? where type = ? and id = ? and version = ?";
|
||||
|
||||
private static final String updateLatestSqlS = "update {0} SET latest = false WHERE type = ? and id = ? AND version = ?";
|
||||
private static final String updateLatestSqlS
|
||||
= "update {0} SET latest = false WHERE type = ? and id = ? AND version = ?";
|
||||
|
||||
public PostgreSqlOrderDao(DataType dataType, Connection connection, TransactionResult txResult,
|
||||
boolean versioningEnabled) {
|
||||
|
@ -79,8 +91,7 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
protected Order parseFromXml(String id, String type, SQLXML sqlxml) {
|
||||
SimpleStrolchElementListener listener = new SimpleStrolchElementListener();
|
||||
try (InputStream binaryStream = sqlxml.getBinaryStream()) {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.parse(binaryStream, new XmlModelSaxReader(listener));
|
||||
getSaxParser().parse(binaryStream, new XmlModelSaxReader(listener));
|
||||
} catch (SQLException | IOException | SAXException | ParserConfigurationException e) {
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to extract Order from sqlxml value for {0} / {1}", id, type), e);
|
||||
|
@ -115,10 +126,10 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
// version
|
||||
preparedStatement.setInt(2, order.getVersion().getVersion());
|
||||
preparedStatement.setString(3, order.getVersion().getCreatedBy());
|
||||
preparedStatement
|
||||
.setTimestamp(4, new Timestamp(order.getVersion().getCreated().getTime()), Calendar.getInstance());
|
||||
preparedStatement
|
||||
.setTimestamp(5, new Timestamp(order.getVersion().getUpdated().getTime()), Calendar.getInstance());
|
||||
preparedStatement.setTimestamp(4, new Timestamp(order.getVersion().getCreated().getTime()),
|
||||
Calendar.getInstance());
|
||||
preparedStatement.setTimestamp(5, new Timestamp(order.getVersion().getUpdated().getTime()),
|
||||
Calendar.getInstance());
|
||||
preparedStatement.setBoolean(6, order.getVersion().isDeleted());
|
||||
|
||||
preparedStatement.setBoolean(7, !order.getVersion().isDeleted());
|
||||
|
@ -144,8 +155,9 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
}
|
||||
|
||||
} catch (SQLException | SAXException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to insert Order {0} due to {1}", order.getVersion(), e.getLocalizedMessage()), e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to insert Order {0} due to {1}", order.getVersion(),
|
||||
e.getLocalizedMessage()), e);
|
||||
}
|
||||
|
||||
if (order.getVersion().isFirstVersion()) {
|
||||
|
@ -163,15 +175,16 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
|
||||
int modCount = preparedStatement.executeUpdate();
|
||||
if (modCount != 1) {
|
||||
String msg = "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
String msg
|
||||
= "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
msg = MessageFormat.format(msg, order.getId(), order.getVersion().getPreviousVersion(), modCount);
|
||||
throw new StrolchPersistenceException(msg);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to update previous version of Order {0} due to {1}", order.getVersion(),
|
||||
e.getLocalizedMessage()), e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to update previous version of Order {0} due to {1}",
|
||||
order.getVersion(), e.getLocalizedMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,15 +199,15 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
|
||||
// make sure is first version when versioning is not enabled
|
||||
if (!order.getVersion().isFirstVersion()) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
|
||||
order.getVersion()));
|
||||
throw new StrolchPersistenceException(MessageFormat.format(
|
||||
"Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
|
||||
order.getVersion()));
|
||||
}
|
||||
|
||||
// and also not marked as deleted!
|
||||
if (order.getVersion().isDeleted()) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
|
||||
order.getVersion()));
|
||||
}
|
||||
|
||||
|
@ -205,10 +218,10 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
|
||||
// version
|
||||
preparedStatement.setString(1, order.getVersion().getCreatedBy());
|
||||
preparedStatement
|
||||
.setTimestamp(2, new Timestamp(order.getVersion().getCreated().getTime()), Calendar.getInstance());
|
||||
preparedStatement
|
||||
.setTimestamp(3, new Timestamp(order.getVersion().getUpdated().getTime()), Calendar.getInstance());
|
||||
preparedStatement.setTimestamp(2, new Timestamp(order.getVersion().getCreated().getTime()),
|
||||
Calendar.getInstance());
|
||||
preparedStatement.setTimestamp(3, new Timestamp(order.getVersion().getUpdated().getTime()),
|
||||
Calendar.getInstance());
|
||||
preparedStatement.setBoolean(4, order.getVersion().isDeleted());
|
||||
|
||||
preparedStatement.setBoolean(5, !order.getVersion().isDeleted());
|
||||
|
@ -228,7 +241,8 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
try {
|
||||
int modCount = preparedStatement.executeUpdate();
|
||||
if (modCount != 1) {
|
||||
String msg = "Expected to update 1 element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
String msg
|
||||
= "Expected to update 1 element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
msg = MessageFormat.format(msg, order.getId(), order.getVersion().getVersion(), modCount);
|
||||
throw new StrolchPersistenceException(msg);
|
||||
}
|
||||
|
@ -238,8 +252,9 @@ public class PostgreSqlOrderDao extends PostgresqlDao<Order> implements OrderDao
|
|||
}
|
||||
|
||||
} catch (SQLException | SAXException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to update Order {0} due to {1}", order.getLocator(), e.getLocalizedMessage()), e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to update Order {0} due to {1}", order.getLocator(),
|
||||
e.getLocalizedMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,15 +15,6 @@
|
|||
*/
|
||||
package li.strolch.persistence.postgresql;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import li.strolch.model.Resource;
|
||||
|
@ -35,18 +26,32 @@ import li.strolch.persistence.api.StrolchPersistenceException;
|
|||
import li.strolch.persistence.api.TransactionResult;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.sql.*;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
import static li.strolch.utils.helper.XmlHelper.getSaxParser;
|
||||
|
||||
@SuppressWarnings("nls")
|
||||
public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements ResourceDao {
|
||||
|
||||
public static final String RESOURCES = "resources";
|
||||
|
||||
private static final String insertAsXmlSqlS = "insert into {0} (id, version, created_by, updated_at, created_at, deleted, latest, name, type, asxml) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static final String insertAsJsonSqlS = "insert into {0} (id, version, created_by, updated_at, created_at, deleted, latest, name, type, asjson) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static final String insertAsXmlSqlS
|
||||
= "insert into {0} (id, version, created_by, updated_at, created_at, deleted, latest, name, type, asxml) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
private static final String insertAsJsonSqlS
|
||||
= "insert into {0} (id, version, created_by, updated_at, created_at, deleted, latest, name, type, asjson) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
private static final String updateAsXmlSqlS = "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, asxml = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsJsonSqlS = "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, asjson = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsXmlSqlS
|
||||
= "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, asxml = ? where type = ? and id = ? and version = ?";
|
||||
private static final String updateAsJsonSqlS
|
||||
= "update {0} set created_by = ?, created_at = ?, updated_at = ?, deleted = ?, latest = ?, name = ?, asjson = ? where type = ? and id = ? and version = ?";
|
||||
|
||||
private static final String updateLatestSqlS = "update {0} SET latest = false WHERE type = ? and id = ? AND version = ?";
|
||||
private static final String updateLatestSqlS
|
||||
= "update {0} SET latest = false WHERE type = ? and id = ? AND version = ?";
|
||||
|
||||
protected PostgreSqlResourceDao(DataType dataType, Connection connection, TransactionResult txResult,
|
||||
boolean versioningEnabled) {
|
||||
|
@ -62,8 +67,7 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
|
|||
protected Resource parseFromXml(String id, String type, SQLXML sqlxml) {
|
||||
SimpleStrolchElementListener listener = new SimpleStrolchElementListener();
|
||||
try (InputStream binaryStream = sqlxml.getBinaryStream()) {
|
||||
SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
|
||||
parser.parse(binaryStream, new XmlModelSaxReader(listener));
|
||||
getSaxParser().parse(binaryStream, new XmlModelSaxReader(listener));
|
||||
} catch (SQLException | IOException | SAXException | ParserConfigurationException e) {
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to extract Resource from sqlxml value for {0} / {1}", id, type), e);
|
||||
|
@ -125,9 +129,9 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
|
|||
}
|
||||
|
||||
} catch (SQLException | SAXException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to insert Resource {0} due to {1}", resource.getLocator(), e.getLocalizedMessage()),
|
||||
e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to insert Resource {0} due to {1}", resource.getLocator(),
|
||||
e.getLocalizedMessage()), e);
|
||||
}
|
||||
|
||||
if (resource.getVersion().isFirstVersion()) {
|
||||
|
@ -145,15 +149,16 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
|
|||
|
||||
int modCount = preparedStatement.executeUpdate();
|
||||
if (modCount != 1) {
|
||||
String msg = "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
String msg
|
||||
= "Expected to update 1 previous element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
msg = MessageFormat.format(msg, resource.getId(), resource.getVersion().getPreviousVersion(), modCount);
|
||||
throw new StrolchPersistenceException(msg);
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to insert Resource {0} due to {1}", resource.getLocator(), e.getLocalizedMessage()),
|
||||
e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to insert Resource {0} due to {1}", resource.getLocator(),
|
||||
e.getLocalizedMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,15 +173,15 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
|
|||
|
||||
// make sure is first version when versioning is not enabled
|
||||
if (!resource.getVersion().isFirstVersion()) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
|
||||
resource.getVersion()));
|
||||
throw new StrolchPersistenceException(MessageFormat.format(
|
||||
"Versioning is not enabled, so version must always be 0 to perform an update, but it is {0}",
|
||||
resource.getVersion()));
|
||||
}
|
||||
|
||||
// and also not marked as deleted!
|
||||
if (resource.getVersion().isDeleted()) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Versioning is not enabled, so version can not be marked as deleted for {0}",
|
||||
resource.getVersion()));
|
||||
}
|
||||
|
||||
|
@ -208,7 +213,8 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
|
|||
try {
|
||||
int modCount = preparedStatement.executeUpdate();
|
||||
if (modCount != 1) {
|
||||
String msg = "Expected to update 1 element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
String msg
|
||||
= "Expected to update 1 element with id {0} and version {1} but SQL statement modified {2} elements!";
|
||||
msg = MessageFormat.format(msg, resource.getId(), resource.getVersion().getVersion(), modCount);
|
||||
throw new StrolchPersistenceException(msg);
|
||||
}
|
||||
|
@ -218,9 +224,9 @@ public class PostgreSqlResourceDao extends PostgresqlDao<Resource> implements Re
|
|||
}
|
||||
|
||||
} catch (SQLException | SAXException e) {
|
||||
throw new StrolchPersistenceException(MessageFormat
|
||||
.format("Failed to update Resource {0} due to {1}", resource.getLocator(), e.getLocalizedMessage()),
|
||||
e);
|
||||
throw new StrolchPersistenceException(
|
||||
MessageFormat.format("Failed to update Resource {0} due to {1}", resource.getLocator(),
|
||||
e.getLocalizedMessage()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ public abstract class AbstractDao<T extends StrolchRootElement> implements Strol
|
|||
return this.tx.getMetadataDao().querySize(subTypeRef, file -> true);
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
long size = 0;
|
||||
for (String type : types) {
|
||||
SubTypeRef subTypeRef = getTypeRef(type);
|
||||
size += this.tx.getMetadataDao().querySize(subTypeRef, file -> true);
|
||||
|
|
|
@ -91,7 +91,7 @@ public class XmlOrderDao extends AbstractDao<Order> implements OrderDao {
|
|||
return this.tx.getMetadataDao().querySize(subTypeRef, getDateRangePredicate(dateRange));
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
long size = 0;
|
||||
for (String type : types) {
|
||||
SubTypeRef subTypeRef = getTypeRef(type);
|
||||
size += this.tx.getMetadataDao().querySize(subTypeRef, getDateRangePredicate(dateRange));
|
||||
|
|
154
pom.xml
154
pom.xml
|
@ -5,7 +5,7 @@
|
|||
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
|
||||
<name>strolch</name>
|
||||
<description>Module build for strolch</description>
|
||||
|
@ -74,32 +74,32 @@
|
|||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
|
||||
<buildTimestamp>${maven.build.timestamp}</buildTimestamp>
|
||||
|
||||
<jdk.version>17</jdk.version>
|
||||
<jdk.version>21</jdk.version>
|
||||
<maven.compiler.source>${jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${jdk.version}</maven.compiler.target>
|
||||
|
||||
<!-- compile time dependencies -->
|
||||
<slf4j.version>2.0.5</slf4j.version>
|
||||
<logback.version>1.4.5</logback.version>
|
||||
<logback.version>1.4.14</logback.version>
|
||||
<gson.version>2.10</gson.version>
|
||||
<hikaricp.version>5.0.1</hikaricp.version>
|
||||
<postgresql.version>42.5.1</postgresql.version>
|
||||
<postgresql.version>42.7.2</postgresql.version>
|
||||
<antlr.version>4.9.3</antlr.version>
|
||||
<jakarta-mail.version>2.1.0</jakarta-mail.version>
|
||||
<angus-mail.version>2.0.1</angus-mail.version>
|
||||
<csv.version>1.9.0</csv.version>
|
||||
<cron.version>1.6.2</cron.version>
|
||||
<owasp-encoder-esapi.version>1.2.3</owasp-encoder-esapi.version>
|
||||
|
||||
<jakarta.xml.bind-api.version>4.0.0</jakarta.xml.bind-api.version>
|
||||
<jakarta.xml.bind-api.version>4.0.2</jakarta.xml.bind-api.version>
|
||||
<jakarta.annotation.version>2.1.1</jakarta.annotation.version>
|
||||
<jakarta.activation.version>2.1.0</jakarta.activation.version>
|
||||
<jakarta.xml.bind-api.version>4.0.0</jakarta.xml.bind-api.version>
|
||||
<jakarta.activation.version>2.1.3</jakarta.activation.version>
|
||||
<jakarta.ws.rs-api.version>3.1.0</jakarta.ws.rs-api.version>
|
||||
<jakarta.servlet-api.version>6.0.0</jakarta.servlet-api.version>
|
||||
<jakarta.websocket.version>2.1.0</jakarta.websocket.version>
|
||||
<jersey.version>3.1.2</jersey.version>
|
||||
<jakarta.websocket.version>2.1.1</jakarta.websocket.version>
|
||||
<jersey.version>3.1.5</jersey.version>
|
||||
|
||||
<camel.version>3.19.0</camel.version>
|
||||
<camel.version>3.22.1</camel.version>
|
||||
<hapi.version>2.3</hapi.version>
|
||||
|
||||
<pi4j.version>1.4</pi4j.version>
|
||||
|
@ -123,7 +123,7 @@
|
|||
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
|
||||
<maven-war-plugin.version>3.4.0</maven-war-plugin.version>
|
||||
<maven-javadoc-plugin.version>3.3.1</maven-javadoc-plugin.version>
|
||||
<maven-deploy-plugin.version>3.0.0-M2</maven-deploy-plugin.version>
|
||||
<maven-deploy-plugin.version>3.1.1</maven-deploy-plugin.version>
|
||||
<maven-resources-plugin.version>3.2.0</maven-resources-plugin.version>
|
||||
<maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
|
||||
<maven-assembly-plugin.version>3.3.0</maven-assembly-plugin.version>
|
||||
|
@ -387,6 +387,7 @@
|
|||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<finalName>strolch-${artifactId}</finalName>
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
|
@ -671,21 +672,6 @@
|
|||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>${nexus-staging-maven-plugin.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>oss.sonatype.org</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>deploy-to-sonatype</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>deploy</goal>
|
||||
<goal>release</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
@ -702,12 +688,12 @@
|
|||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>oss.sonatype.org</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
<id>repo.strolch.li</id>
|
||||
<url>https://repo.strolch.li/repository/strolch-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>oss.sonatype.org</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
<id>repo.strolch.li</id>
|
||||
<url>https://repo.strolch.li/repository/strolch-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<site>
|
||||
<id>localhost</id>
|
||||
|
@ -743,7 +729,7 @@
|
|||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>deploy</id>
|
||||
<id>deploy-snapshots</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -761,10 +747,116 @@
|
|||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>repo.strolch.li</serverId>
|
||||
<nexusUrl>https://repo.strolch.li/</nexusUrl>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-deploy</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>deploy</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>deploy-releases</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>repo.strolch.li</serverId>
|
||||
<nexusUrl>https://repo.strolch.li/</nexusUrl>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-deploy</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>deploy</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>deploy-maven-central</id>
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>oss.sonatype.org</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>oss.sonatype.org</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
|
||||
</snapshotRepository>
|
||||
<site>
|
||||
<id>localhost</id>
|
||||
<url>file://${project.basedir}/target</url>
|
||||
</site>
|
||||
</distributionManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>oss.sonatype.org</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>deploy-to-sonatype</id>
|
||||
<phase>deploy</phase>
|
||||
<goals>
|
||||
<goal>deploy</goal>
|
||||
<goal>release</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
|
||||
<reporting>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package li.strolch.privilege.handler;
|
||||
|
||||
import li.strolch.privilege.base.AccessDeniedException;
|
||||
import li.strolch.privilege.base.InvalidCredentialsException;
|
||||
import li.strolch.privilege.model.UserState;
|
||||
import li.strolch.privilege.model.internal.User;
|
||||
import li.strolch.privilege.model.internal.UserHistory;
|
||||
|
@ -9,10 +10,7 @@ import li.strolch.utils.helper.ExceptionHelper;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingEnumeration;
|
||||
import javax.naming.NamingException;
|
||||
import javax.naming.PartialResultException;
|
||||
import javax.naming.*;
|
||||
import javax.naming.directory.*;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Locale;
|
||||
|
@ -20,6 +18,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import static li.strolch.utils.LdapHelper.encodeForLDAP;
|
||||
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
|
||||
|
||||
public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
||||
|
@ -67,31 +66,33 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
|||
|
||||
@Override
|
||||
protected User checkCredentialsAndUserState(String username, char[] password) throws AccessDeniedException {
|
||||
// escape the user provider username
|
||||
String safeUsername = encodeForLDAP(username, true);
|
||||
|
||||
// first see if this is a local user
|
||||
User internalUser = this.persistenceHandler.getUser(username);
|
||||
User internalUser = this.persistenceHandler.getUser(safeUsername);
|
||||
if (internalUser != null && internalUser.getUserState() != UserState.REMOTE)
|
||||
return super.checkCredentialsAndUserState(username, password);
|
||||
return super.checkCredentialsAndUserState(safeUsername, password);
|
||||
|
||||
String userPrincipalName;
|
||||
if (this.domain.isEmpty()) {
|
||||
userPrincipalName = username;
|
||||
userPrincipalName = safeUsername;
|
||||
} else {
|
||||
if (!this.domainPrefix.isEmpty() && username.startsWith(this.domainPrefix)) {
|
||||
logger.warn("Trimming domain from given username, to first search in sAMAccountName");
|
||||
username = username.substring(this.domainPrefix.length());
|
||||
safeUsername = encodeForLDAP(username.substring(this.domainPrefix.length()), true);
|
||||
}
|
||||
userPrincipalName = username + "@" + this.domain;
|
||||
userPrincipalName = safeUsername + "@" + this.domain;
|
||||
}
|
||||
|
||||
logger.info("User {} tries to login on ldap {}", username, this.providerUrl);
|
||||
logger.info("User {} tries to login on ldap {}", safeUsername, this.providerUrl);
|
||||
|
||||
// Create the initial context
|
||||
DirContext ctx = null;
|
||||
try {
|
||||
ctx = new InitialDirContext(buildLdapEnv(password, userPrincipalName));
|
||||
SearchResult searchResult = searchLdap(username, ctx, userPrincipalName);
|
||||
User user = buildUserFromSearchResult(username, searchResult);
|
||||
SearchResult searchResult = searchLdap(safeUsername, ctx, userPrincipalName);
|
||||
User user = buildUserFromSearchResult(safeUsername, searchResult);
|
||||
|
||||
// persist this user
|
||||
if (internalUser == null)
|
||||
|
@ -106,9 +107,12 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
|||
|
||||
} catch (AccessDeniedException e) {
|
||||
throw e;
|
||||
} catch (AuthenticationException e) {
|
||||
logger.error("Could not login with user: " + safeUsername + " on Ldap", e);
|
||||
throw new InvalidCredentialsException("Could not login with user: " + safeUsername + " on Ldap", e);
|
||||
} catch (Exception e) {
|
||||
logger.error("Could not login with user: " + username + " on Ldap", e);
|
||||
throw new AccessDeniedException("Could not login with user: " + username + " on Ldap", e);
|
||||
logger.error("Could not login with user: " + safeUsername + " on Ldap", e);
|
||||
throw new AccessDeniedException("Could not login with user: " + safeUsername + " on Ldap", e);
|
||||
} finally {
|
||||
if (ctx != null) {
|
||||
try {
|
||||
|
@ -136,13 +140,14 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
|||
return env;
|
||||
}
|
||||
|
||||
private SearchResult searchLdap(String username, DirContext ctx, String userPrincipalName) throws NamingException {
|
||||
private SearchResult searchLdap(String safeUsername, DirContext ctx, String userPrincipalName)
|
||||
throws NamingException {
|
||||
SearchControls searchControls = new SearchControls();
|
||||
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
|
||||
// the first search is using sAMAccountName
|
||||
NamingEnumeration<SearchResult> answer = ctx.search(this.searchBase,
|
||||
LDAP_FILTER_TEMPLATE.formatted(SAM_ACCOUNT_NAME, username, this.additionalFilter), searchControls);
|
||||
LDAP_FILTER_TEMPLATE.formatted(SAM_ACCOUNT_NAME, safeUsername, this.additionalFilter), searchControls);
|
||||
|
||||
SearchResult searchResult = null;
|
||||
while (searchResult == null) {
|
||||
|
@ -151,22 +156,26 @@ public abstract class BaseLdapPrivilegeHandler extends DefaultPrivilegeHandler {
|
|||
// and if we don't find anything, then we search with userPrincipalName
|
||||
if (!answer.hasMore()) {
|
||||
|
||||
logger.warn("No LDAP data retrieved using " + SAM_ACCOUNT_NAME + ", trying with " +
|
||||
USER_PRINCIPAL_NAME + "...");
|
||||
logger.warn("No LDAP data retrieved using "
|
||||
+ SAM_ACCOUNT_NAME
|
||||
+ ", trying with "
|
||||
+ USER_PRINCIPAL_NAME
|
||||
+ "...");
|
||||
answer = ctx.search(this.searchBase,
|
||||
LDAP_FILTER_TEMPLATE.formatted(USER_PRINCIPAL_NAME, userPrincipalName,
|
||||
this.additionalFilter), searchControls);
|
||||
|
||||
if (!answer.hasMore())
|
||||
throw new AccessDeniedException("Could not login user: " + username +
|
||||
" on Ldap: no LDAP Data, for either sAMAccountName or userPrincipalName searches. Domain used is " +
|
||||
this.domain);
|
||||
throw new AccessDeniedException("Could not login user: "
|
||||
+ safeUsername
|
||||
+ " on Ldap: no LDAP Data, for either sAMAccountName or userPrincipalName searches. Domain used is "
|
||||
+ this.domain);
|
||||
}
|
||||
|
||||
searchResult = answer.next();
|
||||
if (answer.hasMore())
|
||||
throw new AccessDeniedException(
|
||||
"Could not login with user: " + username + " on Ldap: Multiple LDAP Data");
|
||||
"Could not login with user: " + safeUsername + " on Ldap: Multiple LDAP Data");
|
||||
|
||||
} catch (PartialResultException e) {
|
||||
if (ExceptionHelper.getExceptionMessage(e).contains("Unprocessed Continuation Reference(s)"))
|
||||
|
|
|
@ -25,11 +25,11 @@ import li.strolch.privilege.xml.CertificateStubsSaxWriter;
|
|||
import li.strolch.utils.concurrent.ElementLockingHandler;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.AesCryptoHelper;
|
||||
import li.strolch.utils.helper.AesCryptoHelper.SecretKeys;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -135,7 +135,7 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
/**
|
||||
* Secret key
|
||||
*/
|
||||
protected SecretKey secretKey;
|
||||
protected SecretKeys secretKey;
|
||||
|
||||
/**
|
||||
* flag if session refreshing is allowed
|
||||
|
@ -291,14 +291,6 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
() -> crudHandler.removeRole(certificate, roleName));
|
||||
}
|
||||
|
||||
void invalidSessionsFor(User user) {
|
||||
List<PrivilegeContext> contexts = new ArrayList<>(this.privilegeContextMap.values());
|
||||
for (PrivilegeContext ctx : contexts) {
|
||||
if (ctx.getUserRep().getUsername().equals(user.getUsername()))
|
||||
invalidate(ctx.getCertificate());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initiateChallengeFor(Usage usage, String username) {
|
||||
initiateChallengeFor(usage, username, SOURCE_UNKNOWN);
|
||||
|
@ -354,8 +346,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
false).getCertificate();
|
||||
|
||||
if (!source.equals("unknown") && !source.equals(userChallenge.getSource())) {
|
||||
logger.warn("Challenge request and response source's are different: request: " + userChallenge.getSource() +
|
||||
" to " + source);
|
||||
logger.warn(format("Challenge request and response source''s are different: request: {0} to {1}",
|
||||
userChallenge.getSource(), source));
|
||||
}
|
||||
|
||||
persistSessionsAsync();
|
||||
|
@ -443,9 +435,12 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
* @return a stream of role names
|
||||
*/
|
||||
public static Stream<String> streamAllRolesForUser(PersistenceHandler persistenceHandler, User user) {
|
||||
return Stream.concat(user.getRoles().stream(),
|
||||
user.groups().stream().map(persistenceHandler::getGroup).filter(Objects::nonNull)
|
||||
.flatMap(g -> g.roles().stream()));
|
||||
return Stream.concat(user.getRoles().stream(), user
|
||||
.groups()
|
||||
.stream()
|
||||
.map(persistenceHandler::getGroup)
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(g -> g.roles().stream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -553,34 +548,37 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
// async execution, max. once per second
|
||||
if (this.persistSessionsTask != null)
|
||||
this.persistSessionsTask.cancel(true);
|
||||
this.persistSessionsTask = this.executorService.schedule(() -> {
|
||||
|
||||
// get sessions reference
|
||||
AtomicReference<List<Certificate>> sessions = new AtomicReference<>();
|
||||
this.lockingHandler.lockedExecute("persist-sessions", () -> sessions.set(
|
||||
new ArrayList<>(this.privilegeContextMap.values()).stream().map(PrivilegeContext::getCertificate)
|
||||
.filter(c -> !c.getUserState().isSystem()).collect(toList())));
|
||||
|
||||
// write the sessions
|
||||
try (OutputStream out = Files.newOutputStream(this.persistSessionsPath.toPath());
|
||||
OutputStream outputStream = AesCryptoHelper.wrapEncrypt(this.secretKey, out)) {
|
||||
|
||||
CertificateStubsSaxWriter writer = new CertificateStubsSaxWriter(sessions.get(), outputStream);
|
||||
writer.write();
|
||||
outputStream.flush();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to persist sessions!", e);
|
||||
if (this.persistSessionsPath.exists() && !this.persistSessionsPath.delete()) {
|
||||
logger.error("Failed to delete sessions file after failing to write to it, at " +
|
||||
this.persistSessionsPath.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}, 1, TimeUnit.SECONDS);
|
||||
|
||||
this.persistSessionsTask = this.executorService.schedule(this::internalPersistSessions, 1, TimeUnit.SECONDS);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void internalPersistSessions() {
|
||||
// get sessions reference
|
||||
AtomicReference<List<Certificate>> sessions = new AtomicReference<>();
|
||||
this.lockingHandler.lockedExecute("persist-sessions", () -> sessions.set(
|
||||
new ArrayList<>(this.privilegeContextMap.values())
|
||||
.stream()
|
||||
.map(PrivilegeContext::getCertificate)
|
||||
.filter(c -> !c.getUserState().isSystem())
|
||||
.collect(toList())));
|
||||
|
||||
// write the sessions
|
||||
try (OutputStream out = Files.newOutputStream(this.persistSessionsPath.toPath());
|
||||
OutputStream outputStream = AesCryptoHelper.wrapEncrypt(this.secretKey, out)) {
|
||||
|
||||
CertificateStubsSaxWriter writer = new CertificateStubsSaxWriter(sessions.get(), outputStream);
|
||||
writer.write();
|
||||
outputStream.flush();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to persist sessions!", e);
|
||||
if (this.persistSessionsPath.exists() && !this.persistSessionsPath.delete()) {
|
||||
logger.error("Failed to delete sessions file after failing to write to it, at "
|
||||
+ this.persistSessionsPath.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSessions() {
|
||||
if (!this.persistSessions) {
|
||||
logger.info("Persisting of sessions not enabled, so not loading!.");
|
||||
|
@ -693,8 +691,9 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
PasswordCrypt requestPasswordCrypt;
|
||||
if (userPasswordCrypt.salt() == null) {
|
||||
requestPasswordCrypt = this.encryptionHandler.hashPasswordWithoutSalt(password);
|
||||
} else if (userPasswordCrypt.hashAlgorithm() == null || userPasswordCrypt.hashIterations() == -1 ||
|
||||
userPasswordCrypt.hashKeyLength() == -1) {
|
||||
} else if (userPasswordCrypt.hashAlgorithm() == null
|
||||
|| userPasswordCrypt.hashIterations() == -1
|
||||
|| userPasswordCrypt.hashKeyLength() == -1) {
|
||||
requestPasswordCrypt = this.encryptionHandler.hashPassword(password, userPasswordCrypt.salt());
|
||||
} else {
|
||||
requestPasswordCrypt = this.encryptionHandler.hashPassword(password, userPasswordCrypt.salt(),
|
||||
|
@ -1074,12 +1073,27 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates all the sessions for the given user and persists the sessions async
|
||||
*
|
||||
* @param user the user for which to invalidate the sessions
|
||||
*/
|
||||
void invalidateSessionsFor(User user) {
|
||||
List<PrivilegeContext> contexts = new ArrayList<>(this.privilegeContextMap.values());
|
||||
for (PrivilegeContext ctx : contexts) {
|
||||
if (ctx.getUserRep().getUsername().equals(user.getUsername()))
|
||||
invalidate(ctx.getCertificate());
|
||||
}
|
||||
|
||||
persistSessionsAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces any existing {@link PrivilegeContext} for the given user by updating with the new user object
|
||||
*
|
||||
* @param newUser the new user to update with
|
||||
*/
|
||||
void updateExistingSessionsForUser(User newUser) {
|
||||
void updateExistingSessionsForUser(User newUser, boolean persistSessions) {
|
||||
List<PrivilegeContext> contexts = new ArrayList<>(this.privilegeContextMap.values());
|
||||
for (PrivilegeContext ctx : contexts) {
|
||||
if (!ctx.getUserRep().getUsername().equals(newUser.getUsername()))
|
||||
|
@ -1087,7 +1101,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
replacePrivilegeContextForCert(newUser, ctx.getCertificate());
|
||||
}
|
||||
|
||||
persistSessionsAsync();
|
||||
if (persistSessions)
|
||||
persistSessionsAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -172,8 +172,15 @@ public class PrivilegeCrudHandler {
|
|||
// properties
|
||||
propertySelected = isSelectedByProperty(selPropertyMap, user.getProperties());
|
||||
|
||||
boolean selected = userIdSelected && usernameSelected && firstNameSelected && lastNameSelected &&
|
||||
userStateSelected && localeSelected && groupSelected && roleSelected && propertySelected;
|
||||
boolean selected = userIdSelected
|
||||
&& usernameSelected
|
||||
&& firstNameSelected
|
||||
&& lastNameSelected
|
||||
&& userStateSelected
|
||||
&& localeSelected
|
||||
&& groupSelected
|
||||
&& roleSelected
|
||||
&& propertySelected;
|
||||
|
||||
if (selected)
|
||||
result.add(user.asUserRep());
|
||||
|
@ -376,7 +383,10 @@ public class PrivilegeCrudHandler {
|
|||
|
||||
// delegate to persistence handler
|
||||
toCreate.forEach(this.persistenceHandler::addUser);
|
||||
toUpdate.forEach(this.persistenceHandler::replaceUser);
|
||||
for (User user : toUpdate) {
|
||||
this.persistenceHandler.replaceUser(user);
|
||||
this.privilegeHandler.updateExistingSessionsForUser(user, false);
|
||||
}
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Created " + toCreate.size() + " users");
|
||||
|
@ -530,7 +540,7 @@ public class PrivilegeCrudHandler {
|
|||
|
||||
// delegate to persistence handler
|
||||
this.persistenceHandler.replaceUser(newUser);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
this.privilegeHandler.updateExistingSessionsForUser(newUser, true);
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Replaced user " + newUser.getUsername());
|
||||
|
||||
|
@ -559,9 +569,8 @@ public class PrivilegeCrudHandler {
|
|||
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_REMOVE_USER, new Tuple(null, existingUser)));
|
||||
|
||||
// delegate user removal to persistence handler
|
||||
this.privilegeHandler.invalidSessionsFor(existingUser);
|
||||
this.privilegeHandler.invalidateSessionsFor(existingUser);
|
||||
this.persistenceHandler.removeUser(username);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Removed user " + username);
|
||||
|
||||
|
@ -715,7 +724,7 @@ public class PrivilegeCrudHandler {
|
|||
|
||||
// delegate user replacement to persistence handler
|
||||
this.persistenceHandler.replaceUser(newUser);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
this.privilegeHandler.invalidateSessionsFor(newUser);
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Set state of user " + newUser.getUsername() + " to " + state);
|
||||
|
||||
|
@ -789,11 +798,11 @@ public class PrivilegeCrudHandler {
|
|||
this.persistenceHandler.replaceRole(newRole);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Replaced role " + newRole.getName());
|
||||
|
||||
// update any existing certificates with new role
|
||||
this.privilegeHandler.updateExistingSessionsWithNewRole(newRole);
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Replaced role " + newRole.getName());
|
||||
|
||||
return newRole.asRoleRep();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package li.strolch.privilege.model;
|
||||
|
||||
public class CertificateThreadLocal extends ThreadLocal<Certificate> {
|
||||
|
||||
private static final CertificateThreadLocal instance = new CertificateThreadLocal();
|
||||
|
||||
public static boolean hasCert() {
|
||||
return instance.get() != null;
|
||||
}
|
||||
|
||||
public static Certificate getCert() {
|
||||
Certificate cert = instance.get();
|
||||
if (cert == null)
|
||||
throw new IllegalStateException("No Cert available on thread " + Thread.currentThread().getName());
|
||||
return cert;
|
||||
}
|
||||
|
||||
public static void setCert(Certificate cert) {
|
||||
if (instance.get() != null)
|
||||
throw new IllegalStateException("THIS THREAD HAS ALREADY HAS A CERT!");
|
||||
instance.set(cert);
|
||||
}
|
||||
|
||||
public static void removeCert() {
|
||||
instance.remove();
|
||||
}
|
||||
}
|
|
@ -63,10 +63,10 @@ public class UserRep {
|
|||
* @param groups the set of {@link li.strolch.privilege.model.internal.Group}s assigned to this user
|
||||
* @param roles the set of {@link Role}s assigned to this user
|
||||
* @param locale the user's {@link Locale}
|
||||
* @param propertyMap a {@link Map} containing string value pairs of properties for this user
|
||||
* @param properties a {@link Map} containing string value pairs of properties for this user
|
||||
*/
|
||||
public UserRep(String userId, String username, String firstname, String lastname, UserState userState,
|
||||
Set<String> groups, Set<String> roles, Locale locale, Map<String, String> propertyMap,
|
||||
Set<String> groups, Set<String> roles, Locale locale, Map<String, String> properties,
|
||||
UserHistory history) {
|
||||
this.userId = trimOrEmpty(userId);
|
||||
this.username = trimOrEmpty(username);
|
||||
|
|
|
@ -15,11 +15,6 @@
|
|||
*/
|
||||
package li.strolch.privilege.policy;
|
||||
|
||||
import static li.strolch.privilege.policy.PrivilegePolicyHelper.checkByAllowDenyValues;
|
||||
import static li.strolch.privilege.policy.PrivilegePolicyHelper.preValidate;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import li.strolch.privilege.base.AccessDeniedException;
|
||||
import li.strolch.privilege.base.PrivilegeException;
|
||||
import li.strolch.privilege.handler.PrivilegeHandler;
|
||||
|
@ -31,6 +26,11 @@ import li.strolch.privilege.model.internal.Role;
|
|||
import li.strolch.utils.collections.Tuple;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import static li.strolch.privilege.policy.PrivilegePolicyHelper.checkByAllowDenyValues;
|
||||
import static li.strolch.privilege.policy.PrivilegePolicyHelper.preValidate;
|
||||
|
||||
/**
|
||||
* This {@link PrivilegePolicy} expects a {@link Tuple} as {@link Restrictable#getPrivilegeValue()}. The Tuple must
|
||||
* contain {@link Role} as first and second value. Then the policy decides depending on the user specific privileges
|
||||
|
@ -67,8 +67,8 @@ public class RoleAccessPrivilege implements PrivilegePolicy {
|
|||
|
||||
// RoleAccessPrivilege policy expects the privilege value to be a role
|
||||
if (!(object instanceof Tuple tuple)) {
|
||||
String msg = Restrictable.class.getName() + PrivilegeMessages
|
||||
.getString("Privilege.illegalArgument.nontuple");
|
||||
String msg = Restrictable.class.getName() + PrivilegeMessages.getString(
|
||||
"Privilege.illegalArgument.nontuple");
|
||||
msg = MessageFormat.format(msg, restrictable.getClass().getSimpleName());
|
||||
throw new PrivilegeException(msg);
|
||||
}
|
||||
|
@ -78,32 +78,30 @@ public class RoleAccessPrivilege implements PrivilegePolicy {
|
|||
return true;
|
||||
|
||||
// get role name as privilege value
|
||||
Role oldRole = tuple.getFirst();
|
||||
Role newRole = tuple.getSecond();
|
||||
String oldRole = tuple.getFirst() instanceof Role r ? r.getName() : tuple.getFirst();
|
||||
String newRole = tuple.getSecond() instanceof Role r ? r.getName() : tuple.getSecond();
|
||||
|
||||
switch (privilegeName) {
|
||||
case PrivilegeHandler.PRIVILEGE_GET_ROLE, PrivilegeHandler.PRIVILEGE_ADD_ROLE, PrivilegeHandler.PRIVILEGE_REMOVE_ROLE -> {
|
||||
DBC.INTERIM.assertNull("For " + privilegeName + " first must be null!", oldRole);
|
||||
DBC.INTERIM.assertNotNull("For " + privilegeName + " second must not be null!", newRole);
|
||||
case PrivilegeHandler.PRIVILEGE_GET_ROLE, PrivilegeHandler.PRIVILEGE_ADD_ROLE, PrivilegeHandler.PRIVILEGE_REMOVE_ROLE -> {
|
||||
DBC.INTERIM.assertNull("For " + privilegeName + " first must be null!", oldRole);
|
||||
DBC.INTERIM.assertNotNull("For " + privilegeName + " second must not be null!", newRole);
|
||||
|
||||
String privilegeValue = newRole.getName();
|
||||
return checkByAllowDenyValues(ctx, privilege, restrictable, privilegeValue, assertHasPrivilege);
|
||||
}
|
||||
case PrivilegeHandler.PRIVILEGE_MODIFY_ROLE -> {
|
||||
DBC.INTERIM.assertNotNull("For " + privilegeName + " first must not be null!", oldRole);
|
||||
DBC.INTERIM.assertNotNull("For " + privilegeName + " second must not be null!", newRole);
|
||||
return checkByAllowDenyValues(ctx, privilege, restrictable, newRole, assertHasPrivilege);
|
||||
}
|
||||
case PrivilegeHandler.PRIVILEGE_MODIFY_ROLE -> {
|
||||
DBC.INTERIM.assertNotNull("For " + privilegeName + " first must not be null!", oldRole);
|
||||
DBC.INTERIM.assertNotNull("For " + privilegeName + " second must not be null!", newRole);
|
||||
|
||||
String privilegeValue = newRole.getName();
|
||||
DBC.INTERIM.assertEquals("oldRole and newRole names must be the same", oldRole.getName(), privilegeValue);
|
||||
DBC.INTERIM.assertEquals("oldRole and newRole names must be the same", oldRole, newRole);
|
||||
|
||||
return checkByAllowDenyValues(ctx, privilege, restrictable, privilegeValue, assertHasPrivilege);
|
||||
}
|
||||
default -> {
|
||||
String msg = Restrictable.class.getName() + PrivilegeMessages.getString(
|
||||
"Privilege.roleAccessPrivilege.unknownPrivilege");
|
||||
msg = MessageFormat.format(msg, privilegeName);
|
||||
throw new PrivilegeException(msg);
|
||||
}
|
||||
return checkByAllowDenyValues(ctx, privilege, restrictable, newRole, assertHasPrivilege);
|
||||
}
|
||||
default -> {
|
||||
String msg = Restrictable.class.getName() + PrivilegeMessages.getString(
|
||||
"Privilege.roleAccessPrivilege.unknownPrivilege");
|
||||
msg = MessageFormat.format(msg, privilegeName);
|
||||
throw new PrivilegeException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package li.strolch.privilege.test;
|
|||
import li.strolch.privilege.base.AccessDeniedException;
|
||||
import li.strolch.privilege.base.InvalidCredentialsException;
|
||||
import li.strolch.privilege.base.PrivilegeException;
|
||||
import li.strolch.privilege.base.PrivilegeModelException;
|
||||
import li.strolch.privilege.handler.PrivilegeHandler;
|
||||
import li.strolch.privilege.i18n.PrivilegeMessages;
|
||||
import li.strolch.privilege.model.*;
|
||||
|
@ -38,6 +39,8 @@ import org.slf4j.LoggerFactory;
|
|||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCauseMessage;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
@ -110,26 +113,28 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
|
||||
@Test
|
||||
public void testFailAuthenticationNOk() {
|
||||
InvalidCredentialsException exception = assertThrows(InvalidCredentialsException.class, () -> {
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
|
||||
try {
|
||||
login(ADMIN, ArraysHelper.copyOf(PASS_BAD));
|
||||
} finally {
|
||||
logout();
|
||||
}
|
||||
});
|
||||
MatcherAssert.assertThat(exception.getMessage(), containsString("Password is incorrect for admin"));
|
||||
assertEquals(InvalidCredentialsException.class, getRootCause(exception).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(exception), containsString("Password is incorrect for admin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailAuthenticationPWNull() {
|
||||
PrivilegeException exception = assertThrows(PrivilegeException.class, () -> {
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
|
||||
try {
|
||||
login(ADMIN, null);
|
||||
} finally {
|
||||
logout();
|
||||
}
|
||||
});
|
||||
MatcherAssert.assertThat(exception.getMessage(), containsString("Password is invalid!"));
|
||||
assertEquals(InvalidCredentialsException.class, getRootCause(exception).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(exception), containsString("Password is invalid!"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -219,14 +224,15 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
*/
|
||||
@Test
|
||||
public void testLoginSystemUser() {
|
||||
AccessDeniedException exception = assertThrows(AccessDeniedException.class, () -> {
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
|
||||
try {
|
||||
login(SYSTEM_USER_ADMIN, SYSTEM_USER_ADMIN.toCharArray());
|
||||
} finally {
|
||||
logout();
|
||||
}
|
||||
});
|
||||
MatcherAssert.assertThat(exception.getMessage(),
|
||||
assertEquals(InvalidCredentialsException.class, getRootCause(exception).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(exception),
|
||||
containsString("User system_admin is a system user and may not login!"));
|
||||
}
|
||||
|
||||
|
@ -271,7 +277,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
|
||||
@Test
|
||||
public void shouldFailUpdateInexistantUser() {
|
||||
PrivilegeException exception = assertThrows(PrivilegeException.class, () -> {
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
|
||||
try {
|
||||
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
|
||||
|
||||
|
@ -285,7 +291,8 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
logout();
|
||||
}
|
||||
});
|
||||
MatcherAssert.assertThat(exception.getMessage(), containsString("User bob does not exist"));
|
||||
assertEquals(PrivilegeModelException.class, getRootCause(exception).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(exception), containsString("User bob does not exist"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -359,7 +366,7 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
|
||||
@Test
|
||||
public void shouldDetectPrivilegeConflict1() {
|
||||
PrivilegeException exception = assertThrows(PrivilegeException.class, () -> {
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
|
||||
try {
|
||||
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
|
||||
Certificate certificate = this.ctx.getCertificate();
|
||||
|
@ -372,12 +379,14 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
logout();
|
||||
}
|
||||
});
|
||||
MatcherAssert.assertThat(exception.getMessage(), containsString("User admin has conflicts for privilege "));
|
||||
assertEquals(PrivilegeModelException.class, getRootCause(exception).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(exception),
|
||||
containsString("User admin has conflicts for privilege "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDetectPrivilegeConflict2() {
|
||||
PrivilegeException exception = assertThrows(PrivilegeException.class, () -> {
|
||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> {
|
||||
try {
|
||||
login(ADMIN, ArraysHelper.copyOf(PASS_ADMIN));
|
||||
Certificate certificate = this.ctx.getCertificate();
|
||||
|
@ -389,7 +398,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
logout();
|
||||
}
|
||||
});
|
||||
MatcherAssert.assertThat(exception.getMessage(), containsString("User admin has conflicts for privilege "));
|
||||
assertEquals(PrivilegeModelException.class, getRootCause(exception).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(exception),
|
||||
containsString("User admin has conflicts for privilege "));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -469,8 +480,9 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
try {
|
||||
this.privilegeHandler.setUserState(certificate, TED, UserState.SYSTEM);
|
||||
fail("Should not be able to set user state to SYSTEM");
|
||||
} catch (AccessDeniedException e) {
|
||||
// ok
|
||||
} catch (IllegalStateException e) {
|
||||
if (!(getRootCause(e) instanceof AccessDeniedException))
|
||||
fail("Unexpected root cause " + getRootCause(e));
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
@ -494,15 +506,17 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
try {
|
||||
this.privilegeHandler.setUserLocale(certificate, BOB, Locale.FRENCH);
|
||||
fail("Should not be able to set locale of other user, as missing privilege");
|
||||
} catch (AccessDeniedException e) {
|
||||
// ok
|
||||
} catch (IllegalStateException e) {
|
||||
if (!(getRootCause(e) instanceof AccessDeniedException))
|
||||
fail("Unexpected root cause " + getRootCause(e));
|
||||
}
|
||||
|
||||
try {
|
||||
this.privilegeHandler.setUserState(certificate, BOB, UserState.DISABLED);
|
||||
fail("Should not be able to set state of other user, as missing privilege");
|
||||
} catch (AccessDeniedException e) {
|
||||
// ok
|
||||
} catch (IllegalStateException e) {
|
||||
if (!(getRootCause(e) instanceof AccessDeniedException))
|
||||
fail("Unexpected root cause " + getRootCause(e));
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
@ -526,15 +540,17 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
try {
|
||||
this.privilegeHandler.setUserLocale(certificate, TED, Locale.FRENCH);
|
||||
fail("Should not be able to set locale, as missing privilege");
|
||||
} catch (AccessDeniedException e) {
|
||||
// ok
|
||||
} catch (IllegalStateException e) {
|
||||
if (!(getRootCause(e) instanceof AccessDeniedException))
|
||||
fail("Unexpected root cause " + getRootCause(e));
|
||||
}
|
||||
|
||||
try {
|
||||
this.privilegeHandler.setUserState(certificate, TED, UserState.ENABLED);
|
||||
fail("Should not be able to set state, as missing privilege");
|
||||
} catch (AccessDeniedException e) {
|
||||
// ok
|
||||
} catch (IllegalStateException e) {
|
||||
if (!(getRootCause(e) instanceof AccessDeniedException))
|
||||
fail("Unexpected root cause " + getRootCause(e));
|
||||
}
|
||||
|
||||
} finally {
|
||||
|
@ -659,9 +675,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
// Will fail because user ted has no password
|
||||
login(TED, ArraysHelper.copyOf(PASS_TED));
|
||||
fail("User Ted may not authenticate because the user has no password!");
|
||||
} catch (PrivilegeException e) {
|
||||
} catch (IllegalStateException e) {
|
||||
String msg = "User ted has no password and may not login!";
|
||||
MatcherAssert.assertThat(e.getMessage(), containsString(msg));
|
||||
assertEquals(InvalidCredentialsException.class, getRootCause(e).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(e), containsString(msg));
|
||||
} finally {
|
||||
logout();
|
||||
}
|
||||
|
@ -715,10 +732,11 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
certificate = this.ctx.getCertificate();
|
||||
this.privilegeHandler.addUser(certificate, userRep, null);
|
||||
fail("User bob may not add a user as bob does not have admin rights!");
|
||||
} catch (PrivilegeException e) {
|
||||
} catch (IllegalStateException e) {
|
||||
String msg = MessageFormat.format(PrivilegeMessages.getString("Privilege.noprivilege.user"), BOB,
|
||||
PrivilegeHandler.PRIVILEGE_ADD_USER);
|
||||
assertEquals(msg, e.getMessage());
|
||||
assertEquals(AccessDeniedException.class, getRootCause(e).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(e), containsString(msg));
|
||||
} finally {
|
||||
logout();
|
||||
}
|
||||
|
@ -779,9 +797,10 @@ public class PrivilegeTest extends AbstractPrivilegeTest {
|
|||
// Will fail because user bob is not yet enabled
|
||||
this.privilegeHandler.authenticate(BOB, ArraysHelper.copyOf(PASS_BOB), false);
|
||||
fail("User Bob may not authenticate because the user is not yet enabled!");
|
||||
} catch (PrivilegeException e) {
|
||||
} catch (IllegalStateException e) {
|
||||
String msg = "User bob does not have state ENABLED and can not login!";
|
||||
MatcherAssert.assertThat(e.getMessage(), containsString(msg));
|
||||
assertEquals(AccessDeniedException.class, getRootCause(e).getClass());
|
||||
MatcherAssert.assertThat(getRootCauseMessage(e), containsString(msg));
|
||||
} finally {
|
||||
logout();
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -65,10 +65,10 @@ public class XmlExportModelCommand extends Command {
|
|||
// output
|
||||
private ModelStatistics statistics;
|
||||
|
||||
private int elementsToWrite;
|
||||
private int nrOfResourcesToExport;
|
||||
private int nrOfOrdersToExport;
|
||||
private int nrOfActivitiesToExport;
|
||||
private long elementsToWrite;
|
||||
private long nrOfResourcesToExport;
|
||||
private long nrOfOrdersToExport;
|
||||
private long nrOfActivitiesToExport;
|
||||
|
||||
private long nextLogTime;
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.HashMap;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static java.util.Collections.synchronizedMap;
|
||||
import static li.strolch.execution.EventBasedExecutionHandler.PROP_LOCK_RETRIES;
|
||||
|
||||
|
@ -95,7 +96,9 @@ public class Controller {
|
|||
public ExecutionPolicy refreshExecutionPolicy(StrolchTransaction tx, Action action) {
|
||||
ExecutionPolicy executionPolicy = this.inExecution.computeIfAbsent(action.getLocator(), e -> {
|
||||
Resource resource = tx.readLock(tx.getResourceFor(action, true));
|
||||
return tx.getPolicy(resource, ExecutionPolicy.class);
|
||||
ExecutionPolicy policy = tx.getPolicy(resource, ExecutionPolicy.class);
|
||||
policy.initialize(action);
|
||||
return policy;
|
||||
});
|
||||
|
||||
// always update the TX and controller
|
||||
|
@ -403,6 +406,8 @@ public class Controller {
|
|||
return;
|
||||
|
||||
Action action = this.activity.getElementByLocator(actionLoc);
|
||||
if (action.getState().isExecuted())
|
||||
return;
|
||||
|
||||
// set this action to warning
|
||||
internalToWarning(tx, action);
|
||||
|
@ -425,6 +430,9 @@ public class Controller {
|
|||
return;
|
||||
|
||||
Action action = this.activity.getElementByLocator(actionLoc);
|
||||
if (action.getState().isExecuted())
|
||||
return;
|
||||
|
||||
internalToWarning(tx, action);
|
||||
}
|
||||
|
||||
|
@ -450,8 +458,9 @@ public class Controller {
|
|||
} catch (StrolchLockException e) {
|
||||
tries++;
|
||||
if (tries >= this.lockRetries) {
|
||||
logger.error("Failed to lock " + this.locator + ". Max retries " + tries +
|
||||
" reached, throwing exception!");
|
||||
logger.error(
|
||||
format("Failed to lock {0}. Max retries {1} reached, throwing exception!", this.locator,
|
||||
tries));
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,9 +23,11 @@ public abstract class ActionExecutionCommand extends BasePlanningAndExecutionCom
|
|||
}
|
||||
|
||||
protected ExecutionPolicy getExecutionPolicy(Action action) {
|
||||
if (this.executionPolicy != null)
|
||||
return this.executionPolicy;
|
||||
return tx().getPolicy(action, ExecutionPolicy.class);
|
||||
if (this.executionPolicy == null) {
|
||||
this.executionPolicy = tx().getPolicy(action, ExecutionPolicy.class);
|
||||
this.executionPolicy.initialize(action);
|
||||
}
|
||||
return this.executionPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -97,7 +97,6 @@ public class PlanAndExecuteActivityCommand extends BasePlanningAndExecutionComma
|
|||
executionPolicy = getExecutionPolicy(action);
|
||||
}
|
||||
|
||||
executionPolicy.initialize(action);
|
||||
if (!executionPolicy.isExecutable(action)) {
|
||||
logger.info("Action " + action.getLocator() + " is not yet executable.");
|
||||
return;
|
||||
|
@ -195,16 +194,7 @@ public class PlanAndExecuteActivityCommand extends BasePlanningAndExecutionComma
|
|||
}
|
||||
|
||||
// stop execution if at least one action is not executable from this entire tree
|
||||
boolean anyActionNotExecutable = activity.streamActionsDeep().anyMatch(a -> {
|
||||
if (!a.getState().canSetToExecution())
|
||||
return false;
|
||||
ExecutionPolicy executionPolicy = getExecutionPolicy(a);
|
||||
executionPolicy.initialize(a);
|
||||
boolean executable = executionPolicy.isExecutable(a);
|
||||
if (!executable)
|
||||
logger.info("Action " + a.getLocator() + " is not executable yet!");
|
||||
return !executable;
|
||||
});
|
||||
boolean anyActionNotExecutable = activity.streamActionsDeep().anyMatch(this::isActionExecutable);
|
||||
if (anyActionNotExecutable)
|
||||
return;
|
||||
}
|
||||
|
@ -219,4 +209,13 @@ public class PlanAndExecuteActivityCommand extends BasePlanningAndExecutionComma
|
|||
element.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isActionExecutable(Action action) {
|
||||
if (!action.getState().canSetToExecution())
|
||||
return false;
|
||||
boolean executable = getExecutionPolicy(action).isExecutable(action);
|
||||
if (!executable)
|
||||
logger.info("Action " + action.getLocator() + " is not yet executable!");
|
||||
return !executable;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,13 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
|
|||
return this.controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of activity for which this {@link ExecutionPolicy} is being executed
|
||||
*/
|
||||
public String getActivityType() {
|
||||
return this.actionLoc.get(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the {@link Resource} for this action. Can only be called after {@link #initialize(Action)}
|
||||
* was called.
|
||||
|
@ -243,7 +250,8 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
|
|||
* Performs tasked required when this execution policy is stopped
|
||||
*/
|
||||
protected void handleStopped() {
|
||||
getDelayedExecutionTimer().cancel(this.actionLoc);
|
||||
if (this.actionLoc != null)
|
||||
getDelayedExecutionTimer().cancel(this.actionLoc);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -255,8 +263,12 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
|
|||
*/
|
||||
protected void setActionState(Action action, State state) {
|
||||
if (action.getState().inClosedPhase())
|
||||
throw new IllegalStateException("Action " + action.getLocator() + " has state " + action.getState() +
|
||||
" and can not be changed to " + state);
|
||||
throw new IllegalStateException("Action "
|
||||
+ action.getLocator()
|
||||
+ " has state "
|
||||
+ action.getState()
|
||||
+ " and can not be changed to "
|
||||
+ state);
|
||||
|
||||
action.setState(state);
|
||||
|
||||
|
|
|
@ -10,11 +10,12 @@ import li.strolch.model.timevalue.impl.FloatValue;
|
|||
import li.strolch.model.timevalue.impl.ValueChange;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.privilege.model.PrivilegeContext;
|
||||
import li.strolch.utils.CheckedBiConsumer;
|
||||
import li.strolch.utils.time.PeriodDuration;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
|
@ -39,8 +40,9 @@ public class SimpleExecution extends ExecutionPolicy {
|
|||
|
||||
protected void startWarningTask(PeriodDuration duration, Action action, Supplier<LogMessage> handler) {
|
||||
if (this.warningTask != null) {
|
||||
logger.warn("There is already a warning task registered, for action " + action.getLocator() +
|
||||
". Cancelling and creating a new task...");
|
||||
logger.warn("There is already a warning task registered, for action "
|
||||
+ action.getLocator()
|
||||
+ ". Cancelling and creating a new task...");
|
||||
this.warningTask.cancel(true);
|
||||
this.warningTask = null;
|
||||
}
|
||||
|
@ -150,18 +152,18 @@ public class SimpleExecution extends ExecutionPolicy {
|
|||
return getContainer().getRealm(ctx.getCertificate()).openTx(ctx.getCertificate(), getClass(), readOnly);
|
||||
}
|
||||
|
||||
protected void runWithFreshActionReadonly(BiConsumer<StrolchTransaction, Action> consumer,
|
||||
Supplier<String> failMsgSupplier) {
|
||||
runWithFreshAction(true, consumer, failMsgSupplier);
|
||||
protected void runWithFreshActionReadonly(CheckedBiConsumer<StrolchTransaction, Action> consumer,
|
||||
Consumer<Throwable> failHandler) {
|
||||
runWithFreshAction(true, consumer, failHandler);
|
||||
}
|
||||
|
||||
protected void runWithFreshActionWritable(BiConsumer<StrolchTransaction, Action> consumer,
|
||||
Supplier<String> failMsgSupplier) {
|
||||
runWithFreshAction(false, consumer, failMsgSupplier);
|
||||
protected void runWithFreshActionWritable(CheckedBiConsumer<StrolchTransaction, Action> consumer,
|
||||
Consumer<Throwable> failHandler) {
|
||||
runWithFreshAction(false, consumer, failHandler);
|
||||
}
|
||||
|
||||
private void runWithFreshAction(boolean readOnly, BiConsumer<StrolchTransaction, Action> consumer,
|
||||
Supplier<String> failMsgSupplier) {
|
||||
private void runWithFreshAction(boolean readOnly, CheckedBiConsumer<StrolchTransaction, Action> consumer,
|
||||
Consumer<Throwable> failHandler) {
|
||||
try {
|
||||
runAsAgent(ctx -> {
|
||||
try (StrolchTransaction tx = openLocalTx(ctx, readOnly)) {
|
||||
|
@ -174,7 +176,7 @@ public class SimpleExecution extends ExecutionPolicy {
|
|||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
logger.error(failMsgSupplier.get(), e);
|
||||
failHandler.accept(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
package li.strolch.report;
|
||||
|
||||
import static li.strolch.report.ReportConstants.TYPE_REPORT;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
|
@ -16,9 +9,21 @@ import li.strolch.policy.PolicyHandler;
|
|||
import li.strolch.report.policy.ReportPolicy;
|
||||
import li.strolch.utils.collections.DateRange;
|
||||
import li.strolch.utils.collections.MapOfSets;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static li.strolch.report.ReportConstants.TYPE_REPORT;
|
||||
import static li.strolch.utils.helper.StringHelper.formatMillisecondsDuration;
|
||||
|
||||
public class Report implements AutoCloseable {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Report.class);
|
||||
|
||||
private final ReportPolicy reportPolicy;
|
||||
|
||||
public Report(StrolchTransaction tx, String reportId) {
|
||||
|
@ -26,9 +31,12 @@ public class Report implements AutoCloseable {
|
|||
Resource reportRes = tx.getResourceBy(TYPE_REPORT, reportId, true);
|
||||
PolicyDef reportPolicyDef = reportRes.getPolicyDef(ReportPolicy.class.getSimpleName());
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
PolicyHandler policyHandler = tx.getContainer().getComponent(PolicyHandler.class);
|
||||
this.reportPolicy = policyHandler.getPolicy(reportPolicyDef, tx);
|
||||
this.reportPolicy.initialize(reportId);
|
||||
long took = System.currentTimeMillis() - start;
|
||||
logger.info("Initializing report " + reportId + " took " + formatMillisecondsDuration(took));
|
||||
}
|
||||
|
||||
public ReportPolicy getReportPolicy() {
|
||||
|
@ -83,12 +91,12 @@ public class Report implements AutoCloseable {
|
|||
return this.reportPolicy.doReportWithPage(offset, limit);
|
||||
}
|
||||
|
||||
public MapOfSets<String, StrolchRootElement> generateFilterCriteria(int limit) {
|
||||
public MapOfSets<String, JsonObject> generateFilterCriteria(int limit) {
|
||||
return this.reportPolicy.generateFilterCriteria(limit);
|
||||
}
|
||||
|
||||
public Stream<StrolchRootElement> generateFilterCriteria(String type) {
|
||||
return this.reportPolicy.generateFilterCriteria(type);
|
||||
public Stream<JsonObject> generateFilterCriteria(String type, int limit, String query) {
|
||||
return this.reportPolicy.generateFilterCriteria(type,limit, query);
|
||||
}
|
||||
|
||||
public long getCounter() {
|
||||
|
|
|
@ -1,17 +1,5 @@
|
|||
package li.strolch.report.policy;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.report.ReportConstants.*;
|
||||
import static li.strolch.utils.helper.StringHelper.EMPTY;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.*;
|
||||
import li.strolch.model.parameter.AbstractParameter;
|
||||
|
@ -33,6 +21,20 @@ import li.strolch.utils.collections.TypedTuple;
|
|||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.iso8601.ISO8601;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.report.ReportConstants.*;
|
||||
import static li.strolch.utils.ObjectHelper.*;
|
||||
import static li.strolch.utils.helper.StringHelper.EMPTY;
|
||||
|
||||
/**
|
||||
* A Generic Report defines a report as is described at <a href="https://strolch.li/documentation-reports.html">Strolch
|
||||
* Reports</a>
|
||||
|
@ -75,8 +77,7 @@ public class GenericReport extends ReportPolicy {
|
|||
* Retrieves the {@code Resource} with the given ID, and initializes this instance with the data specified on the
|
||||
* report
|
||||
*
|
||||
* @param reportId
|
||||
* the report to use
|
||||
* @param reportId the report to use
|
||||
*/
|
||||
@Override
|
||||
public void initialize(String reportId) {
|
||||
|
@ -89,14 +90,17 @@ public class GenericReport extends ReportPolicy {
|
|||
|
||||
this.columnsBag = this.reportRes.getParameterBag(BAG_COLUMNS, true);
|
||||
|
||||
this.columnIds = this.columnsBag.getParameters().stream() //
|
||||
.sorted(comparingInt(Parameter::getIndex)) //
|
||||
.map(StrolchElement::getId) //
|
||||
this.columnIds = this.columnsBag
|
||||
.getParameters()
|
||||
.stream()
|
||||
.sorted(comparingInt(Parameter::getIndex))
|
||||
.map(StrolchElement::getId)
|
||||
.collect(toList());
|
||||
|
||||
this.parallel = this.reportRes.getBoolean(PARAM_PARALLEL);
|
||||
this.descending = this.reportRes.getBoolean(PARAM_DESCENDING);
|
||||
this.allowMissingColumns = this.reportRes.getBoolean(PARAM_ALLOW_MISSING_COLUMNS);
|
||||
this.allowMissingColumns = !this.reportRes.hasParameter(PARAM_ALLOW_MISSING_COLUMNS)
|
||||
|| this.reportRes.getBoolean(PARAM_ALLOW_MISSING_COLUMNS);
|
||||
this.filterMissingValuesAsTrue = this.reportRes.getBoolean(PARAM_FILTER_MISSING_VALUES_AS_TRUE);
|
||||
this.dateRangeSelP = this.reportRes.getParameter(BAG_PARAMETERS, PARAM_DATE_RANGE_SEL);
|
||||
|
||||
|
@ -105,9 +109,12 @@ public class GenericReport extends ReportPolicy {
|
|||
StringParameter objectTypeFilterCriteriaP = objectTypeP.getClone();
|
||||
objectTypeFilterCriteriaP.setId(objectType);
|
||||
if (objectTypeFilterCriteriaP.getUom().equals(UOM_NONE))
|
||||
throw new IllegalStateException(
|
||||
"Join UOM " + objectTypeFilterCriteriaP.getUom() + " invalid: " + objectTypeFilterCriteriaP.getId()
|
||||
+ " for " + objectTypeFilterCriteriaP.getLocator());
|
||||
throw new IllegalStateException("Join UOM "
|
||||
+ objectTypeFilterCriteriaP.getUom()
|
||||
+ " invalid: "
|
||||
+ objectTypeFilterCriteriaP.getId()
|
||||
+ " for "
|
||||
+ objectTypeFilterCriteriaP.getLocator());
|
||||
this.filterCriteriaParams.put(objectType, objectTypeFilterCriteriaP);
|
||||
if (this.reportRes.hasParameterBag(BAG_JOINS)) {
|
||||
ParameterBag joinBag = this.reportRes.getParameterBag(BAG_JOINS);
|
||||
|
@ -123,9 +130,12 @@ public class GenericReport extends ReportPolicy {
|
|||
ParameterBag additionalTypeBag = this.reportRes.getParameterBag(BAG_ADDITIONAL_TYPE);
|
||||
StringParameter additionalTypeP = additionalTypeBag.getParameter(PARAM_OBJECT_TYPE, true);
|
||||
if (additionalTypeP.getUom().equals(UOM_NONE))
|
||||
throw new IllegalStateException(
|
||||
"Additional Type UOM " + additionalTypeP.getUom() + " invalid: " + additionalTypeP.getId()
|
||||
+ " for " + additionalTypeP.getLocator());
|
||||
throw new IllegalStateException("Additional Type UOM "
|
||||
+ additionalTypeP.getUom()
|
||||
+ " invalid: "
|
||||
+ additionalTypeP.getId()
|
||||
+ " for "
|
||||
+ additionalTypeP.getLocator());
|
||||
this.filterCriteriaParams.put(additionalTypeP.getValue(), additionalTypeP);
|
||||
}
|
||||
if (this.reportRes.hasParameterBag(BAG_ADDITIONAL_JOINS)) {
|
||||
|
@ -133,9 +143,12 @@ public class GenericReport extends ReportPolicy {
|
|||
joinBag.getParameters().forEach(parameter -> {
|
||||
StringParameter joinP = (StringParameter) parameter;
|
||||
if (joinP.getUom().equals(UOM_NONE))
|
||||
throw new IllegalStateException(
|
||||
"Additional Join UOM " + joinP.getUom() + " invalid: " + joinP.getId() + " for "
|
||||
+ joinP.getLocator());
|
||||
throw new IllegalStateException("Additional Join UOM "
|
||||
+ joinP.getUom()
|
||||
+ " invalid: "
|
||||
+ joinP.getId()
|
||||
+ " for "
|
||||
+ joinP.getLocator());
|
||||
this.filterCriteriaParams.put(parameter.getId(), joinP);
|
||||
});
|
||||
}
|
||||
|
@ -144,7 +157,8 @@ public class GenericReport extends ReportPolicy {
|
|||
if (this.reportRes.hasParameterBag(BAG_ORDERING)) {
|
||||
ParameterBag orderingBag = this.reportRes.getParameterBag(BAG_ORDERING, true);
|
||||
if (orderingBag.hasParameters()) {
|
||||
this.orderingParams = orderingBag.getParameters()
|
||||
this.orderingParams = orderingBag
|
||||
.getParameters()
|
||||
.stream()
|
||||
.map(e -> (StringParameter) e)
|
||||
.collect(toList());
|
||||
|
@ -157,21 +171,21 @@ public class GenericReport extends ReportPolicy {
|
|||
List<ParameterBag> filterBags = this.reportRes.getParameterBagsByType(TYPE_FILTER);
|
||||
for (ParameterBag filterBag : filterBags) {
|
||||
|
||||
if (filterBag.hasParameter(PARAM_FIELD_REF) && (filterBag.hasParameter(PARAM_FIELD_REF1)
|
||||
|| filterBag.hasParameter(PARAM_FIELD_REF2))) {
|
||||
if (filterBag.hasParameter(PARAM_FIELD_REF) && (
|
||||
filterBag.hasParameter(PARAM_FIELD_REF1) || filterBag.hasParameter(PARAM_FIELD_REF2))) {
|
||||
throw new IllegalArgumentException(
|
||||
"Filter " + filterBag.getLocator() + " can not have combination of " + PARAM_FIELD_REF
|
||||
+ " and any of " + PARAM_FIELD_REF1 + ", " + PARAM_FIELD_REF2);
|
||||
} else if ((filterBag.hasParameter(PARAM_FIELD_REF1) && !filterBag.hasParameter(PARAM_FIELD_REF2)) || (
|
||||
!filterBag.hasParameter(PARAM_FIELD_REF1) && filterBag.hasParameter(PARAM_FIELD_REF2))) {
|
||||
format("Filter {0} can not have combination of {1} and any of {2}, {3}", filterBag.getLocator(),
|
||||
PARAM_FIELD_REF, PARAM_FIELD_REF1, PARAM_FIELD_REF2));
|
||||
} else if ((filterBag.hasParameter(PARAM_FIELD_REF1) && !filterBag.hasParameter(PARAM_FIELD_REF2))
|
||||
|| (!filterBag.hasParameter(PARAM_FIELD_REF1) && filterBag.hasParameter(PARAM_FIELD_REF2))) {
|
||||
throw new IllegalArgumentException(
|
||||
"Filter " + filterBag.getLocator() + " must have both " + PARAM_FIELD_REF1 + " and "
|
||||
+ PARAM_FIELD_REF2);
|
||||
} else if (!filterBag.hasParameter(PARAM_FIELD_REF) && (!filterBag.hasParameter(PARAM_FIELD_REF1)
|
||||
|| !filterBag.hasParameter(PARAM_FIELD_REF2))) {
|
||||
format("Filter {0} must have both {1} and {2}", filterBag.getLocator(), PARAM_FIELD_REF1,
|
||||
PARAM_FIELD_REF2));
|
||||
} else if (!filterBag.hasParameter(PARAM_FIELD_REF) && (
|
||||
!filterBag.hasParameter(PARAM_FIELD_REF1) || !filterBag.hasParameter(PARAM_FIELD_REF2))) {
|
||||
throw new IllegalArgumentException(
|
||||
"Filter " + filterBag.getLocator() + " is missing the " + PARAM_FIELD_REF + " or "
|
||||
+ PARAM_FIELD_REF1 + ", " + PARAM_FIELD_REF2 + " combination!");
|
||||
format("Filter {0} is missing the {1} or {2}, {3} combination!", filterBag.getLocator(),
|
||||
PARAM_FIELD_REF, PARAM_FIELD_REF1, PARAM_FIELD_REF2));
|
||||
}
|
||||
|
||||
// prepare filter function policy
|
||||
|
@ -248,8 +262,7 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Sets the given date range
|
||||
*
|
||||
* @param dateRange
|
||||
* the date range to set
|
||||
* @param dateRange the date range to set
|
||||
*
|
||||
* @return this for chaining
|
||||
*/
|
||||
|
@ -281,10 +294,8 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Applies the given filter for the given element type
|
||||
*
|
||||
* @param type
|
||||
* the type of element to filter
|
||||
* @param ids
|
||||
* the IDs of the elements to filter to
|
||||
* @param type the type of element to filter
|
||||
* @param ids the IDs of the elements to filter to
|
||||
*
|
||||
* @return this for chaining
|
||||
*/
|
||||
|
@ -301,10 +312,8 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Applies the given filter for the given element type
|
||||
*
|
||||
* @param type
|
||||
* the type of element to filter
|
||||
* @param ids
|
||||
* the IDs of the elements to filter to
|
||||
* @param type the type of element to filter
|
||||
* @param ids the IDs of the elements to filter to
|
||||
*
|
||||
* @return this for chaining
|
||||
*/
|
||||
|
@ -321,10 +330,8 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Applies the given filter for the given element type
|
||||
*
|
||||
* @param type
|
||||
* the type of element to filter
|
||||
* @param ids
|
||||
* the IDs of the elements to filter to
|
||||
* @param type the type of element to filter
|
||||
* @param ids the IDs of the elements to filter to
|
||||
*
|
||||
* @return this for chaining
|
||||
*/
|
||||
|
@ -361,7 +368,7 @@ public class GenericReport extends ReportPolicy {
|
|||
Stream<Map<String, StrolchRootElement>> stream;
|
||||
|
||||
// query the main objects and return a stream
|
||||
stream = queryRows() //
|
||||
stream = queryRows()
|
||||
|
||||
// transform each element into a map of Type,Value pairs
|
||||
.map(this::evaluateRow);
|
||||
|
@ -384,8 +391,7 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Allows sub classes to extend this stream, i.e. flat map an object to extend the stream where necessary
|
||||
*
|
||||
* @param stream
|
||||
* the stream to extend
|
||||
* @param stream the stream to extend
|
||||
*
|
||||
* @return the stream
|
||||
*/
|
||||
|
@ -423,8 +429,7 @@ public class GenericReport extends ReportPolicy {
|
|||
* Value="Bags/relations/product"/>
|
||||
* </code>
|
||||
*
|
||||
* @param stream
|
||||
* the current stream of rows
|
||||
* @param stream the current stream of rows
|
||||
*
|
||||
* @return the new stream of rows, which iterates over the additionally joined elements, thus creating a cartesian
|
||||
* product stream
|
||||
|
@ -443,8 +448,8 @@ public class GenericReport extends ReportPolicy {
|
|||
String[] locatorParts = joinParamP.getValue().split(Locator.PATH_SEPARATOR);
|
||||
if (locatorParts.length != 3)
|
||||
throw new IllegalStateException(
|
||||
"Parameter reference (" + joinParamP.getValue() + ") is invalid as it does not have 3 parts for "
|
||||
+ joinParamP.getLocator());
|
||||
format("Parameter reference ({0}) is invalid as it does not have 3 parts for {1}",
|
||||
joinParamP.getValue(), joinParamP.getLocator()));
|
||||
String bagKey = locatorParts[1];
|
||||
String paramKey = locatorParts[2];
|
||||
|
||||
|
@ -460,14 +465,14 @@ public class GenericReport extends ReportPolicy {
|
|||
StrolchRootElement joinElement = row.get(joinWithP.getUom());
|
||||
if (joinElement == null)
|
||||
throw new IllegalStateException(
|
||||
"Additional join type " + joinWithP.getUom() + " is not available on row for "
|
||||
+ joinWithP.getLocator());
|
||||
format("Additional join type {0} is not available on row for {1}", joinWithP.getUom(),
|
||||
joinWithP.getLocator()));
|
||||
|
||||
Optional<Parameter<?>> refP = lookupParameter(joinWithP, joinElement, false);
|
||||
if (refP.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"Parameter reference (" + joinWithP.getValue() + ") for " + joinWithP.getLocator()
|
||||
+ " not found on " + joinElement.getLocator());
|
||||
format("Parameter reference ({0}) for {1} not found on {2}", joinWithP.getValue(),
|
||||
joinWithP.getLocator(), joinElement.getLocator()));
|
||||
}
|
||||
|
||||
StringParameter joinP = (StringParameter) refP.get();
|
||||
|
@ -499,14 +504,12 @@ public class GenericReport extends ReportPolicy {
|
|||
protected String formatColumn(Map<String, StrolchRootElement> row, String columnId) {
|
||||
StringParameter columnDefP = this.columnsBag.getParameter(columnId, true);
|
||||
Object value = evaluateColumnValue(columnDefP, row, false);
|
||||
if (value instanceof ZonedDateTime) {
|
||||
return ISO8601.toString((ZonedDateTime) value);
|
||||
} else if (value instanceof Date) {
|
||||
return ISO8601.toString((Date) value);
|
||||
} else if (value instanceof Parameter) {
|
||||
return formatColumn((Parameter<?>) value);
|
||||
} else
|
||||
return value.toString();
|
||||
return switch (value) {
|
||||
case ZonedDateTime zonedDateTime -> ISO8601.toString(zonedDateTime);
|
||||
case Date date -> ISO8601.toString(date);
|
||||
case Parameter<?> parameter -> formatColumn(parameter);
|
||||
default -> value.toString();
|
||||
};
|
||||
}
|
||||
|
||||
protected String formatColumn(Parameter<?> param) {
|
||||
|
@ -532,8 +535,7 @@ public class GenericReport extends ReportPolicy {
|
|||
*
|
||||
* <p>This method can be overridden for further filtering</p>
|
||||
*
|
||||
* @param type
|
||||
* the type of element to filter
|
||||
* @param type the type of element to filter
|
||||
*
|
||||
* @return true if the element is to be kept, false if not
|
||||
*/
|
||||
|
@ -545,70 +547,34 @@ public class GenericReport extends ReportPolicy {
|
|||
* Generates the filter criteria for this report, i.e. it returns a {@link MapOfSets} which defines the type of
|
||||
* elements on which a filter can be set and the {@link Set} of IDs which can be used for filtering.
|
||||
*
|
||||
* @param limit
|
||||
* the max number of values per filter criteria to return
|
||||
* @param limit the max number of values per filter criteria to return
|
||||
*
|
||||
* @return the filter criteria as a map of sets
|
||||
*/
|
||||
@Override
|
||||
public MapOfSets<String, StrolchRootElement> generateFilterCriteria(int limit) {
|
||||
public MapOfSets<String, JsonObject> generateFilterCriteria(int limit) {
|
||||
int maxFacetValues = getMaxFacetValues(limit);
|
||||
|
||||
if (limit <= 0 || limit >= MAX_FACET_VALUE_LIMIT) {
|
||||
logger.warn("Overriding invalid limit " + limit + " with " + MAX_FACET_VALUE_LIMIT);
|
||||
limit = 100;
|
||||
}
|
||||
|
||||
int maxFacetValues;
|
||||
int reportMaxFacetValues = this.reportRes.getInteger(PARAM_MAX_FACET_VALUES);
|
||||
if (reportMaxFacetValues != 0 && reportMaxFacetValues != limit) {
|
||||
logger.warn("Report " + this.reportRes.getId() + " has " + PARAM_MAX_FACET_VALUES + " defined as "
|
||||
+ reportMaxFacetValues + ". Ignoring requested limit " + limit);
|
||||
maxFacetValues = reportMaxFacetValues;
|
||||
} else {
|
||||
maxFacetValues = limit;
|
||||
}
|
||||
|
||||
MapOfSets<String, StrolchRootElement> result = new MapOfSets<>(true);
|
||||
MapOfSets<String, JsonObject> result = new MapOfSets<>(true);
|
||||
|
||||
// we need the list of possible element types, which designate the criteria
|
||||
List<String> criteria = this.filterCriteriaParams.values().stream() //
|
||||
.filter(p -> {
|
||||
if (p.getUom().equals(UOM_NONE))
|
||||
throw new IllegalStateException(
|
||||
"Join UOM " + p.getUom() + " invalid: " + p.getId() + " for " + p.getLocator());
|
||||
if (p.getId().equals(PARAM_OBJECT_TYPE))
|
||||
return filterCriteriaAllowed(p.getUom());
|
||||
return filterCriteriaAllowed(p.getId());
|
||||
}) //
|
||||
.sorted(comparing(StringParameter::getIndex)) //
|
||||
.map(StringParameter::getUom) //
|
||||
.collect(toList());
|
||||
List<String> criteria = this.filterCriteriaParams.values().stream().filter(p -> {
|
||||
if (p.getUom().equals(UOM_NONE))
|
||||
throw new IllegalStateException(
|
||||
format("Join UOM {0} invalid: {1} for {2}", p.getUom(), p.getId(), p.getLocator()));
|
||||
if (p.getId().equals(PARAM_OBJECT_TYPE))
|
||||
return filterCriteriaAllowed(p.getUom());
|
||||
return filterCriteriaAllowed(p.getId());
|
||||
}).sorted(comparing(StringParameter::getIndex)).map(StringParameter::getUom).collect(toList());
|
||||
criteria.addAll(this.directCriteria);
|
||||
|
||||
int maxRowsForFacetGeneration = this.reportRes.getInteger(PARAM_MAX_ROWS_FOR_FACET_GENERATION);
|
||||
|
||||
if (!this.directCriteria.isEmpty()) {
|
||||
criteria.forEach(type -> {
|
||||
if (!this.directCriteria.contains(type))
|
||||
return;
|
||||
StringParameter filterCriteriaP = this.filterCriteriaParams.get(type);
|
||||
Stream<? extends StrolchRootElement> stream = switch (filterCriteriaP.getInterpretation()) {
|
||||
case INTERPRETATION_RESOURCE_REF -> tx().streamResources(filterCriteriaP.getUom());
|
||||
case INTERPRETATION_ORDER_REF -> tx().streamOrders(filterCriteriaP.getUom());
|
||||
case INTERPRETATION_ACTIVITY_REF -> tx().streamActivities(filterCriteriaP.getUom());
|
||||
default -> throw new IllegalArgumentException(
|
||||
"Unhandled filter criteria interpretation " + filterCriteriaP.getInterpretation() + " for "
|
||||
+ filterCriteriaP.getLocator());
|
||||
};
|
||||
|
||||
stream = stream.map(this::mapFilterCriteria).filter(this::filterDirectCriteria);
|
||||
|
||||
if (hasOrdering())
|
||||
stream = stream.sorted(this::sortDirectCriteria);
|
||||
|
||||
if (maxFacetValues > 0)
|
||||
stream = stream.limit(maxFacetValues);
|
||||
|
||||
stream.forEachOrdered(e -> result.addElement(e.getType(), e));
|
||||
if (this.directCriteria.contains(type))
|
||||
prepareStreamForDirectCriteria(type, maxFacetValues).forEachOrdered(
|
||||
e -> result.addElement(e.getType(), mapCriteriaToJson(e)));
|
||||
});
|
||||
|
||||
criteria.removeAll(this.directCriteria);
|
||||
|
@ -625,11 +591,12 @@ public class GenericReport extends ReportPolicy {
|
|||
|
||||
for (String criterion : criteria) {
|
||||
if (row.containsKey(criterion) && result.size(criterion) < maxFacetValues)
|
||||
result.addElement(criterion, row.get(criterion));
|
||||
result.addElement(criterion, mapCriteriaToJson(row.get(criterion)));
|
||||
}
|
||||
|
||||
// stop if we have enough data
|
||||
if (result.stream()
|
||||
if (result
|
||||
.stream()
|
||||
.filter(e -> !this.directCriteria.contains(e.getKey()))
|
||||
.mapToInt(e -> e.getValue().size())
|
||||
.allMatch(v -> v >= maxFacetValues))
|
||||
|
@ -639,13 +606,80 @@ public class GenericReport extends ReportPolicy {
|
|||
return result;
|
||||
}
|
||||
|
||||
protected StrolchRootElement mapFilterCriteria(StrolchRootElement element) {
|
||||
return element;
|
||||
@Override
|
||||
public Stream<JsonObject> generateFilterCriteria(String type, int limit, String query) {
|
||||
int maxFacetValues = getMaxFacetValues(limit);
|
||||
|
||||
Stream<StrolchRootElement> stream;
|
||||
if (this.directCriteria.contains(type)) {
|
||||
stream = prepareStreamForDirectCriteria(type, 0).map(e -> (StrolchRootElement) e);
|
||||
} else {
|
||||
stream = buildStream().filter(row -> row.containsKey(type)).map(row -> row.get(type)).distinct();
|
||||
}
|
||||
|
||||
Stream<JsonObject> resultStream = filterAndMapFilterCriteria(stream, query);
|
||||
if (maxFacetValues != 0)
|
||||
resultStream = resultStream.limit(maxFacetValues);
|
||||
return resultStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<StrolchRootElement> generateFilterCriteria(String type) {
|
||||
return buildStream().filter(row -> row.containsKey(type)).map(row -> row.get(type)).distinct();
|
||||
protected Stream<JsonObject> filterAndMapFilterCriteria(Stream<StrolchRootElement> stream, String query) {
|
||||
Stream<JsonObject> resultStream = stream.map(this::mapCriteriaToJson);
|
||||
String[] queryParts = query == null || query.isEmpty() ? null : query.split(" ");
|
||||
if (queryParts != null)
|
||||
resultStream = resultStream.filter(f -> contains(f.get(Tags.Json.NAME).getAsString(), queryParts, true));
|
||||
return resultStream;
|
||||
}
|
||||
|
||||
protected JsonObject mapCriteriaToJson(StrolchRootElement criterion) {
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty(Tags.Json.ID, criterion.getId());
|
||||
result.addProperty(Tags.Json.NAME, criterion.getName());
|
||||
return result;
|
||||
}
|
||||
|
||||
protected Stream<? extends StrolchRootElement> prepareStreamForDirectCriteria(String type, int maxFacetValues) {
|
||||
StringParameter filterCriteriaP = this.filterCriteriaParams.get(type);
|
||||
if (filterCriteriaP == null) {
|
||||
logger.warn("Filter criteria not found for {}", type);
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
Stream<? extends StrolchRootElement> stream = switch (filterCriteriaP.getInterpretation()) {
|
||||
case INTERPRETATION_RESOURCE_REF -> tx().streamResources(filterCriteriaP.getUom());
|
||||
case INTERPRETATION_ORDER_REF -> tx().streamOrders(filterCriteriaP.getUom());
|
||||
case INTERPRETATION_ACTIVITY_REF -> tx().streamActivities(filterCriteriaP.getUom());
|
||||
default -> throw new IllegalArgumentException(
|
||||
format("Unhandled filter criteria interpretation {0} for {1}", filterCriteriaP.getInterpretation(),
|
||||
filterCriteriaP.getLocator()));
|
||||
};
|
||||
|
||||
stream = stream.filter(this::filterDirectCriteria);
|
||||
|
||||
if (hasOrdering())
|
||||
stream = stream.sorted(this::sortDirectCriteria);
|
||||
|
||||
if (maxFacetValues > 0)
|
||||
stream = stream.limit(maxFacetValues);
|
||||
return stream;
|
||||
}
|
||||
|
||||
private int getMaxFacetValues(int limit) {
|
||||
if (limit <= 0 || limit >= MAX_FACET_VALUE_LIMIT) {
|
||||
logger.warn("Overriding invalid limit {} with " + MAX_FACET_VALUE_LIMIT, limit);
|
||||
limit = 100;
|
||||
}
|
||||
|
||||
int maxFacetValues;
|
||||
int reportMaxFacetValues = this.reportRes.getInteger(PARAM_MAX_FACET_VALUES);
|
||||
if (reportMaxFacetValues != 0 && reportMaxFacetValues != limit) {
|
||||
logger.warn("Report {} has " + PARAM_MAX_FACET_VALUES + " defined as {}. Ignoring requested limit {}",
|
||||
this.reportRes.getId(), reportMaxFacetValues, limit);
|
||||
maxFacetValues = reportMaxFacetValues;
|
||||
} else {
|
||||
maxFacetValues = limit;
|
||||
}
|
||||
return maxFacetValues;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -673,31 +707,9 @@ public class GenericReport extends ReportPolicy {
|
|||
|
||||
int sortVal;
|
||||
if (fieldRefP.getValue().startsWith("$")) {
|
||||
Object columnValue1 = evaluateColumnValue(fieldRefP, Map.of(column1.getType(), column1), false);
|
||||
Object columnValue2 = evaluateColumnValue(fieldRefP, Map.of(column2.getType(), column2), false);
|
||||
|
||||
if (this.descending) {
|
||||
sortVal = ObjectHelper.compare(columnValue2, columnValue1, true);
|
||||
} else {
|
||||
sortVal = ObjectHelper.compare(columnValue1, columnValue2, true);
|
||||
}
|
||||
|
||||
sortVal = compareFieldRef(fieldRefP, column1, column2);
|
||||
} else {
|
||||
Optional<Parameter<?>> param1 = lookupParameter(fieldRefP, column1, false);
|
||||
Optional<Parameter<?>> param2 = lookupParameter(fieldRefP, column2, false);
|
||||
|
||||
if (param1.isEmpty() && param2.isEmpty())
|
||||
continue;
|
||||
|
||||
if (param1.isPresent() && param2.isEmpty())
|
||||
return 1;
|
||||
else if (param1.isEmpty())
|
||||
return -1;
|
||||
|
||||
if (this.descending)
|
||||
sortVal = param2.get().compareTo(param1.get());
|
||||
else
|
||||
sortVal = param1.get().compareTo(param2.get());
|
||||
sortVal = compareParamFieldRefP(fieldRefP, column1, column2);
|
||||
}
|
||||
|
||||
if (sortVal != 0)
|
||||
|
@ -711,10 +723,8 @@ public class GenericReport extends ReportPolicy {
|
|||
* Implements a sorting of the given two rows. This implementation using the ordering as is defined in
|
||||
* {@link ReportConstants#BAG_ORDERING}
|
||||
*
|
||||
* @param row1
|
||||
* the left side
|
||||
* @param row2
|
||||
* the right side
|
||||
* @param row1 the left side
|
||||
* @param row2 the right side
|
||||
*
|
||||
* @return the value {@code -1}, {@code 0} or {@code 1}, depending on the defined ordering
|
||||
*/
|
||||
|
@ -735,31 +745,9 @@ public class GenericReport extends ReportPolicy {
|
|||
|
||||
int sortVal;
|
||||
if (fieldRefP.getValue().startsWith("$")) {
|
||||
Object columnValue1 = evaluateColumnValue(fieldRefP, row1, false);
|
||||
Object columnValue2 = evaluateColumnValue(fieldRefP, row2, false);
|
||||
|
||||
if (this.descending) {
|
||||
sortVal = ObjectHelper.compare(columnValue2, columnValue1, true);
|
||||
} else {
|
||||
sortVal = ObjectHelper.compare(columnValue1, columnValue2, true);
|
||||
}
|
||||
|
||||
sortVal = compareFieldRef(fieldRefP, row1, row2);
|
||||
} else {
|
||||
Optional<Parameter<?>> param1 = lookupParameter(fieldRefP, column1, false);
|
||||
Optional<Parameter<?>> param2 = lookupParameter(fieldRefP, column2, false);
|
||||
|
||||
if (param1.isEmpty() && param2.isEmpty())
|
||||
continue;
|
||||
|
||||
if (param1.isPresent() && param2.isEmpty())
|
||||
return 1;
|
||||
else if (param1.isEmpty())
|
||||
return -1;
|
||||
|
||||
if (this.descending)
|
||||
sortVal = param2.get().compareTo(param1.get());
|
||||
else
|
||||
sortVal = param1.get().compareTo(param2.get());
|
||||
sortVal = compareParamFieldRefP(fieldRefP, column1, column2);
|
||||
}
|
||||
|
||||
if (sortVal != 0)
|
||||
|
@ -769,6 +757,41 @@ public class GenericReport extends ReportPolicy {
|
|||
return 0;
|
||||
}
|
||||
|
||||
private int compareFieldRef(StringParameter fieldRefP, Map<String, StrolchRootElement> row1,
|
||||
Map<String, StrolchRootElement> row2) {
|
||||
Object columnValue1 = evaluateColumnValue(fieldRefP, row1, false);
|
||||
Object columnValue2 = evaluateColumnValue(fieldRefP, row2, false);
|
||||
if (this.descending)
|
||||
return compare(columnValue2, columnValue1, true);
|
||||
return compare(columnValue1, columnValue2, true);
|
||||
}
|
||||
|
||||
private int compareFieldRef(StringParameter fieldRefP, StrolchRootElement column1, StrolchRootElement column2) {
|
||||
Object columnValue1 = evaluateColumnValue(fieldRefP, Map.of(column1.getType(), column1), false);
|
||||
Object columnValue2 = evaluateColumnValue(fieldRefP, Map.of(column2.getType(), column2), false);
|
||||
if (this.descending)
|
||||
return compare(columnValue2, columnValue1, true);
|
||||
return compare(columnValue1, columnValue2, true);
|
||||
}
|
||||
|
||||
private int compareParamFieldRefP(StringParameter fieldRefP, StrolchRootElement column1,
|
||||
StrolchRootElement column2) {
|
||||
Optional<Parameter<?>> param1 = lookupParameter(fieldRefP, column1, false);
|
||||
Optional<Parameter<?>> param2 = lookupParameter(fieldRefP, column2, false);
|
||||
|
||||
if (param1.isEmpty() && param2.isEmpty())
|
||||
return 0;
|
||||
|
||||
if (param1.isPresent() && param2.isEmpty())
|
||||
return 1;
|
||||
else if (param1.isEmpty())
|
||||
return -1;
|
||||
|
||||
if (this.descending)
|
||||
return param2.get().compareTo(param1.get());
|
||||
return param1.get().compareTo(param2.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a filter is defined, i.e. {@link ParameterBag ParameterBags} of type
|
||||
* {@link ReportConstants#TYPE_FILTER}, a date range
|
||||
|
@ -776,8 +799,8 @@ public class GenericReport extends ReportPolicy {
|
|||
* @return true if a filter is defined
|
||||
*/
|
||||
protected boolean hasFilter() {
|
||||
return !this.filtersByPolicy.isEmpty() || this.dateRange != null || (this.filtersById != null
|
||||
&& !this.filtersById.isEmpty());
|
||||
return !this.filtersByPolicy.isEmpty() || this.dateRange != null || (
|
||||
this.filtersById != null && !this.filtersById.isEmpty());
|
||||
}
|
||||
|
||||
protected boolean filterDirectCriteria(StrolchRootElement element) {
|
||||
|
@ -815,8 +838,7 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Returns true if the element is filtered, i.e. is to be kep, false if it should not be kept in the stream
|
||||
*
|
||||
* @param row
|
||||
* the row to check if it is filtered
|
||||
* @param row the row to check if it is filtered
|
||||
*
|
||||
* @return if the element is filtered
|
||||
*/
|
||||
|
@ -864,8 +886,8 @@ public class GenericReport extends ReportPolicy {
|
|||
Optional<Parameter<?>> param = lookupParameter(this.dateRangeSelP, element, false);
|
||||
if (param.isEmpty() || param.get().getValueType() != StrolchValueType.DATE)
|
||||
throw new IllegalStateException(
|
||||
"Date Range selector is invalid, as referenced parameter is not a Date but "
|
||||
+ (param.isPresent() ? param.get().getValueType() : "null"));
|
||||
format("Date Range selector is invalid, as referenced parameter is not a Date but {0}",
|
||||
param.isPresent() ? param.get().getValueType() : "null"));
|
||||
|
||||
date = ((DateParameter) param.get()).getValueZdt();
|
||||
}
|
||||
|
@ -893,13 +915,10 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Evaluates the column value from the given column definition and row
|
||||
*
|
||||
* @param columnDefP
|
||||
* the column definition
|
||||
* @param row
|
||||
* the row
|
||||
* @param allowNull
|
||||
* handles the return value if the lookup fails. If true, then null is returned, else the empty string is
|
||||
* returned
|
||||
* @param columnDefP the column definition
|
||||
* @param row the row
|
||||
* @param allowNull handles the return value if the lookup fails. If true, then null is returned, else the empty
|
||||
* string is returned
|
||||
*
|
||||
* @return the column value
|
||||
*/
|
||||
|
@ -935,8 +954,8 @@ public class GenericReport extends ReportPolicy {
|
|||
else
|
||||
columnValue = parameter;
|
||||
} else {
|
||||
columnValue = lookupParameter(columnDefP, column, allowNull) //
|
||||
.orElseGet(() -> allowNull ? null : new StringParameter(columnDefP.getValue(), columnDef, ""));
|
||||
columnValue = lookupParameter(columnDefP, column, allowNull).orElseGet(
|
||||
() -> allowNull ? null : new StringParameter(columnDefP.getValue(), columnDef, ""));
|
||||
}
|
||||
|
||||
return columnValue;
|
||||
|
@ -945,10 +964,8 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Finds a parameter given the column definition
|
||||
*
|
||||
* @param columnDefP
|
||||
* the column definition
|
||||
* @param column
|
||||
* the element from which the parameter is to be retrieved
|
||||
* @param columnDefP the column definition
|
||||
* @param column the element from which the parameter is to be retrieved
|
||||
*
|
||||
* @return the parameter, or null if it does not exist
|
||||
*/
|
||||
|
@ -959,8 +976,8 @@ public class GenericReport extends ReportPolicy {
|
|||
String[] searchParts = columnDef.split(SEARCH_SEPARATOR);
|
||||
if (searchParts.length != 3)
|
||||
throw new IllegalStateException(
|
||||
"Parameter search reference (" + columnDef + ") is invalid as it does not have 3 parts for "
|
||||
+ columnDefP.getLocator());
|
||||
format("Parameter search reference ({0}) is invalid as it does not have 3 parts for {1}", columnDef,
|
||||
columnDefP.getLocator()));
|
||||
|
||||
String parentParamId = searchParts[1];
|
||||
String paramRef = searchParts[2];
|
||||
|
@ -968,8 +985,8 @@ public class GenericReport extends ReportPolicy {
|
|||
String[] locatorParts = paramRef.split(Locator.PATH_SEPARATOR);
|
||||
if (locatorParts.length != 3)
|
||||
throw new IllegalStateException(
|
||||
"Parameter search reference (" + paramRef + ") is invalid as it does not have 3 parts for "
|
||||
+ columnDefP.getLocator());
|
||||
format("Parameter search reference ({0}) is invalid as it does not have 3 parts for {1}", paramRef,
|
||||
columnDefP.getLocator()));
|
||||
|
||||
String bagKey = locatorParts[1];
|
||||
String paramKey = locatorParts[2];
|
||||
|
@ -981,10 +998,8 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Retrieves the given parameter with the given parameter reference from the given column
|
||||
*
|
||||
* @param paramRefP
|
||||
* the parameter reference
|
||||
* @param element
|
||||
* the element
|
||||
* @param paramRefP the parameter reference
|
||||
* @param element the element
|
||||
*
|
||||
* @return the {@link Optional} with the parameter
|
||||
*/
|
||||
|
@ -995,8 +1010,8 @@ public class GenericReport extends ReportPolicy {
|
|||
String[] locatorParts = paramRef.split(Locator.PATH_SEPARATOR);
|
||||
if (locatorParts.length != 3)
|
||||
throw new IllegalStateException(
|
||||
"Parameter reference (" + paramRef + ") is invalid as it does not have 3 parts for "
|
||||
+ paramRefP.getLocator());
|
||||
format("Parameter reference ({0}) is invalid as it does not have 3 parts for {1}", paramRef,
|
||||
paramRefP.getLocator()));
|
||||
|
||||
String bagKey = locatorParts[1];
|
||||
String paramKey = locatorParts[2];
|
||||
|
@ -1004,8 +1019,8 @@ public class GenericReport extends ReportPolicy {
|
|||
Parameter<?> param = element.getParameter(bagKey, paramKey);
|
||||
if (!overrideAllowMissingColumns && !this.allowMissingColumns && param == null)
|
||||
throw new IllegalStateException(
|
||||
"Parameter reference (" + paramRef + ") for " + paramRefP.getLocator() + " not found on "
|
||||
+ element.getLocator());
|
||||
format("Parameter reference ({0}) for {1} not found on {2}", paramRef, paramRefP.getLocator(),
|
||||
element.getLocator()));
|
||||
|
||||
return Optional.ofNullable(param);
|
||||
}
|
||||
|
@ -1030,12 +1045,17 @@ public class GenericReport extends ReportPolicy {
|
|||
}
|
||||
|
||||
protected boolean hasJoinOnType(String type) {
|
||||
return (this.reportRes.hasParameterBag(BAG_JOINS) //
|
||||
&& this.reportRes.getParameterBag(BAG_JOINS).hasParameter(type)) //
|
||||
|| (this.reportRes.hasParameterBag(BAG_ADDITIONAL_TYPE) //
|
||||
&& this.reportRes.getParameterBag(BAG_ADDITIONAL_TYPE).getString(PARAM_OBJECT_TYPE).equals(type)) //
|
||||
|| (this.reportRes.hasParameterBag(BAG_ADDITIONAL_JOINS) //
|
||||
&& this.reportRes.getParameterBag(BAG_ADDITIONAL_JOINS).hasParameter(type));
|
||||
return (
|
||||
this.reportRes.hasParameterBag(BAG_JOINS) && this.reportRes
|
||||
.getParameterBag(BAG_JOINS)
|
||||
.hasParameter(type)) || (
|
||||
this.reportRes.hasParameterBag(BAG_ADDITIONAL_TYPE) && this.reportRes
|
||||
.getParameterBag(BAG_ADDITIONAL_TYPE)
|
||||
.getString(PARAM_OBJECT_TYPE)
|
||||
.equals(type)) || (
|
||||
this.reportRes.hasParameterBag(BAG_ADDITIONAL_JOINS) && this.reportRes
|
||||
.getParameterBag(BAG_ADDITIONAL_JOINS)
|
||||
.hasParameter(type));
|
||||
}
|
||||
|
||||
protected Stream<? extends StrolchRootElement> getStreamFor(StringParameter objectTypeP) {
|
||||
|
@ -1051,8 +1071,7 @@ public class GenericReport extends ReportPolicy {
|
|||
* Evaluates the row for the given element. The resulting {@link Map} contains the joins on all elements and the
|
||||
* keys are the type of elements and values are the actual elements
|
||||
*
|
||||
* @param element
|
||||
* the element from which the row is evaluated
|
||||
* @param element the element from which the row is evaluated
|
||||
*
|
||||
* @return the {@link Map} of elements denoting the row for the given element
|
||||
*/
|
||||
|
@ -1086,14 +1105,10 @@ public class GenericReport extends ReportPolicy {
|
|||
/**
|
||||
* Finds the join with the given elements
|
||||
*
|
||||
* @param refs
|
||||
* the current row, with any already retrieved joins
|
||||
* @param joinBag
|
||||
* the {@link ReportConstants#BAG_JOINS} {@link ParameterBag}
|
||||
* @param joinP
|
||||
* the join definition
|
||||
* @param optional
|
||||
* a boolean defining if the join my be missing
|
||||
* @param refs the current row, with any already retrieved joins
|
||||
* @param joinBag the {@link ReportConstants#BAG_JOINS} {@link ParameterBag}
|
||||
* @param joinP the join definition
|
||||
* @param optional a boolean defining if the join my be missing
|
||||
*
|
||||
* @return the joined element, or null if it does not exist and {@code optional} is false
|
||||
*/
|
||||
|
@ -1120,27 +1135,7 @@ public class GenericReport extends ReportPolicy {
|
|||
return null;
|
||||
}
|
||||
|
||||
ParameterBag relationsBag = dependency.getParameterBag(BAG_RELATIONS);
|
||||
if (relationsBag == null)
|
||||
throw new IllegalStateException(
|
||||
"Invalid join definition value: " + joinP.getValue() + " on: " + joinP.getLocator() + " as "
|
||||
+ dependency.getLocator() + " has no ParameterBag " + BAG_RELATIONS);
|
||||
|
||||
List<Parameter<?>> relationParams = relationsBag.getParametersByInterpretationAndUom(interpretation, joinType)
|
||||
.stream()
|
||||
.filter(p -> p.getValueType() == StrolchValueType.STRING)
|
||||
.toList();
|
||||
|
||||
if (relationParams.isEmpty())
|
||||
throw new IllegalStateException("Found no relation parameters with UOM " + joinType + " of type "
|
||||
+ StrolchValueType.STRING.getType() + " on dependency " + dependency.getLocator());
|
||||
if (relationParams.size() > 1)
|
||||
throw new IllegalStateException(
|
||||
"Found multiple possible relation parameters for UOM " + joinType + " on dependency "
|
||||
+ dependency.getLocator());
|
||||
|
||||
Parameter<?> relationParam = relationParams.get(0);
|
||||
StringParameter relationP = (StringParameter) relationParam;
|
||||
StringParameter relationP = getJoinRelationParam(dependency, joinP, joinType, interpretation);
|
||||
if (relationP.getValue().isEmpty() && optional)
|
||||
return null;
|
||||
|
||||
|
@ -1152,4 +1147,33 @@ public class GenericReport extends ReportPolicy {
|
|||
refs.put(joinType, joinElem);
|
||||
return joinElem;
|
||||
}
|
||||
|
||||
private static StringParameter getJoinRelationParam(StrolchRootElement dependency, StringParameter joinP,
|
||||
String joinType, String interpretation) {
|
||||
|
||||
ParameterBag relationsBag = dependency.getParameterBag(BAG_RELATIONS);
|
||||
if (relationsBag == null)
|
||||
throw new IllegalStateException(
|
||||
format("Invalid join definition value: {0} on: {1} as {2} has no ParameterBag {3}",
|
||||
joinP.getValue(), joinP.getLocator(), dependency.getLocator(), BAG_RELATIONS));
|
||||
|
||||
List<Parameter<?>> relationParams = relationsBag
|
||||
.getParametersByInterpretationAndUom(interpretation, joinType)
|
||||
.stream()
|
||||
.filter(p -> p.getValueType() == StrolchValueType.STRING)
|
||||
.toList();
|
||||
|
||||
if (relationParams.isEmpty())
|
||||
throw new IllegalStateException(
|
||||
format("Found no relation parameters with UOM {0} of type {1} on dependency {2}", joinType,
|
||||
StrolchValueType.STRING.getType(), dependency.getLocator()));
|
||||
if (relationParams.size() > 1)
|
||||
throw new IllegalStateException("Found multiple possible relation parameters for UOM "
|
||||
+ joinType
|
||||
+ " on dependency "
|
||||
+ dependency.getLocator());
|
||||
|
||||
Parameter<?> relationParam = relationParams.getFirst();
|
||||
return (StringParameter) relationParam;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
package li.strolch.report.policy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
|
@ -14,6 +9,11 @@ import li.strolch.report.ReportElement;
|
|||
import li.strolch.utils.collections.DateRange;
|
||||
import li.strolch.utils.collections.MapOfSets;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class ReportPolicy extends StrolchPolicy {
|
||||
|
||||
public ReportPolicy(StrolchTransaction tx) {
|
||||
|
@ -48,9 +48,9 @@ public abstract class ReportPolicy extends StrolchPolicy {
|
|||
|
||||
public abstract Stream<ReportElement> doReportWithPage(int offset, int limit);
|
||||
|
||||
public abstract MapOfSets<String, StrolchRootElement> generateFilterCriteria(int limit);
|
||||
public abstract MapOfSets<String, JsonObject> generateFilterCriteria(int limit);
|
||||
|
||||
public abstract Stream<StrolchRootElement> generateFilterCriteria(String type);
|
||||
public abstract Stream<JsonObject> generateFilterCriteria(String type, int limit, String query);
|
||||
|
||||
public abstract long getCounter();
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package li.strolch.service.notifications;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.agent.api.StrolchAgent;
|
||||
import li.strolch.model.ParameterBag;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.builder.ResourceBuilder;
|
||||
import li.strolch.persistence.api.Operation;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.privilege.model.PrivilegeContext;
|
||||
import li.strolch.privilege.model.SimpleRestrictable;
|
||||
import li.strolch.runtime.configuration.SupportedLanguage;
|
||||
import li.strolch.service.JsonServiceArgument;
|
||||
import li.strolch.service.api.AbstractService;
|
||||
import li.strolch.service.api.ServiceResult;
|
||||
import li.strolch.utils.collections.Tuple;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.PRIVILEGE_GET_ROLE;
|
||||
import static li.strolch.utils.iso8601.ISO8601.parseToZdt;
|
||||
|
||||
public class CreateNotificationService extends AbstractService<JsonServiceArgument, ServiceResult> {
|
||||
@Override
|
||||
protected ServiceResult getResultInstance() {
|
||||
return new ServiceResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonServiceArgument getArgumentInstance() {
|
||||
return new JsonServiceArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServiceResult internalDoService(JsonServiceArgument arg) throws Exception {
|
||||
DBC.PRE.assertNotNull("JsonElement must be set", arg.jsonElement);
|
||||
DBC.PRE.assertNotNull("JsonElement must be a JsonObject", arg.jsonElement.isJsonObject());
|
||||
|
||||
JsonObject jsonObject = arg.jsonElement.getAsJsonObject();
|
||||
|
||||
try (StrolchTransaction tx = openArgOrUserTx(arg)) {
|
||||
Resource notification = buildNotification(tx, jsonObject, getSupportedLanguages(getAgent()));
|
||||
tx.add(notification);
|
||||
tx.commitOnClose();
|
||||
}
|
||||
|
||||
return ServiceResult.success();
|
||||
}
|
||||
|
||||
protected static Resource buildNotification(StrolchTransaction tx, JsonObject jsonObject,
|
||||
Set<String> supportedLanguages) {
|
||||
Resource notification = newNotification();
|
||||
PrivilegeContext ctx = tx.getPrivilegeContext();
|
||||
|
||||
JsonObject visibilityJ = jsonObject.get(BAG_VISIBILITY).getAsJsonObject();
|
||||
ParameterBag visibility = notification.getParameterBag(BAG_VISIBILITY);
|
||||
|
||||
visibility.setBoolean(PARAM_ENABLED,
|
||||
visibilityJ.has(PARAM_ENABLED) && visibilityJ.get(PARAM_ENABLED).getAsBoolean());
|
||||
visibility.setBoolean(PARAM_FOR_ALL,
|
||||
visibilityJ.has(PARAM_FOR_ALL) && visibilityJ.get(PARAM_FOR_ALL).getAsBoolean());
|
||||
if (visibilityJ.has(PARAM_VISIBLE_FROM))
|
||||
visibility.setDate(PARAM_VISIBLE_FROM, parseToZdt(visibilityJ.get(PARAM_VISIBLE_FROM).getAsString()));
|
||||
if (visibilityJ.has(PARAM_VISIBLE_TO))
|
||||
visibility.setDate(PARAM_VISIBLE_TO, parseToZdt(visibilityJ.get(PARAM_VISIBLE_TO).getAsString()));
|
||||
|
||||
if (visibilityJ.has(PARAM_ROLES)) {
|
||||
String rolesJ = visibilityJ.get(PARAM_ROLES).getAsString();
|
||||
visibility.getStringListP(PARAM_ROLES).setValueFromString(rolesJ);
|
||||
for (String role : visibility.getStringList(PARAM_ROLES)) {
|
||||
ctx.validateAction(new SimpleRestrictable(PRIVILEGE_GET_ROLE, new Tuple(null, role)));
|
||||
}
|
||||
}
|
||||
|
||||
if (visibilityJ.has(PARAM_LOCATIONS)) {
|
||||
String locationsJ = visibilityJ.get(PARAM_LOCATIONS).getAsString();
|
||||
visibility.getStringListP(PARAM_LOCATIONS).setValueFromString(locationsJ);
|
||||
for (String locationId : visibility.getStringList(PARAM_LOCATIONS)) {
|
||||
tx.assertHasPrivilege(Operation.GET, tx.getResourceBy(TYPE_LOCATION, locationId, true));
|
||||
}
|
||||
}
|
||||
|
||||
for (String language : supportedLanguages) {
|
||||
if (!jsonObject.has(language))
|
||||
continue;
|
||||
JsonObject languageJ = jsonObject.get(language).getAsJsonObject();
|
||||
String title = languageJ.get(PARAM_TITLE).getAsString();
|
||||
String text = languageJ.get(PARAM_TEXT).getAsString();
|
||||
|
||||
ParameterBag languageBag = new ParameterBag(language, language, TYPE_TEXT);
|
||||
languageBag.setString(PARAM_TITLE, title);
|
||||
languageBag.setString(PARAM_TEXT, text);
|
||||
notification.addParameterBag(languageBag);
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
||||
public static Set<String> getSupportedLanguages(StrolchAgent agent) {
|
||||
return agent
|
||||
.getStrolchConfiguration()
|
||||
.getRuntimeConfiguration()
|
||||
.getSupportedLanguages()
|
||||
.stream()
|
||||
.map(SupportedLanguage::locale)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static Resource newNotification() {
|
||||
ResourceBuilder notificationBuilder = new ResourceBuilder(TYPE_NOTIFICATION, TYPE_NOTIFICATION)
|
||||
|
||||
.bag(BAG_VISIBILITY, TYPE_VISIBILITY)
|
||||
|
||||
.booleanB(PARAM_ENABLED).end()
|
||||
|
||||
.date(PARAM_VISIBLE_FROM).end()
|
||||
|
||||
.date(PARAM_VISIBLE_TO).end()
|
||||
|
||||
.booleanB(PARAM_FOR_ALL).end()
|
||||
|
||||
.stringList(PARAM_ROLES).end()
|
||||
|
||||
.stringList(PARAM_LOCATIONS).end()
|
||||
|
||||
.endBag();
|
||||
|
||||
return notificationBuilder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package li.strolch.service.notifications;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.service.StringArgument;
|
||||
import li.strolch.service.api.AbstractService;
|
||||
import li.strolch.service.api.ServiceResult;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.TYPE_NOTIFICATION;
|
||||
|
||||
public class RemoveNotificationService extends AbstractService<StringArgument, ServiceResult> {
|
||||
@Override
|
||||
protected ServiceResult getResultInstance() {
|
||||
return new ServiceResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringArgument getArgumentInstance() {
|
||||
return new StringArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServiceResult internalDoService(StringArgument arg) throws Exception {
|
||||
DBC.PRE.assertNotEmpty("value must be set", arg.value);
|
||||
|
||||
try (StrolchTransaction tx = openArgOrUserTx(arg)) {
|
||||
Resource notification = tx.getResourceBy(TYPE_NOTIFICATION, arg.value, true);
|
||||
tx.remove(notification);
|
||||
tx.commitOnClose();
|
||||
}
|
||||
|
||||
return ServiceResult.success();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package li.strolch.service.notifications;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.service.JsonServiceArgument;
|
||||
import li.strolch.service.api.AbstractService;
|
||||
import li.strolch.service.api.ServiceResult;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.service.notifications.CreateNotificationService.buildNotification;
|
||||
import static li.strolch.service.notifications.CreateNotificationService.getSupportedLanguages;
|
||||
|
||||
public class UpdateNotificationService extends AbstractService<JsonServiceArgument, ServiceResult> {
|
||||
@Override
|
||||
protected ServiceResult getResultInstance() {
|
||||
return new ServiceResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonServiceArgument getArgumentInstance() {
|
||||
return new JsonServiceArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServiceResult internalDoService(JsonServiceArgument arg) throws Exception {
|
||||
DBC.PRE.assertNotEmpty("objectId must be set", arg.objectId);
|
||||
DBC.PRE.assertNotNull("JsonElement must be set", arg.jsonElement);
|
||||
DBC.PRE.assertNotNull("JsonElement must be a JsonObject", arg.jsonElement.isJsonObject());
|
||||
|
||||
JsonObject jsonObject = arg.jsonElement.getAsJsonObject();
|
||||
DBC.PRE.assertEquals("arg ID and jsonObject ID must be the same", arg.objectId,
|
||||
jsonObject.get(Tags.Json.ID).getAsString());
|
||||
|
||||
try (StrolchTransaction tx = openArgOrUserTx(arg)) {
|
||||
|
||||
Resource notification = buildNotification(tx, jsonObject, getSupportedLanguages(getAgent()));
|
||||
notification.setId(arg.objectId);
|
||||
|
||||
tx.update(notification);
|
||||
tx.commitOnClose();
|
||||
}
|
||||
|
||||
return ServiceResult.success();
|
||||
}
|
||||
}
|
|
@ -1,18 +1,7 @@
|
|||
package li.strolch.report;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.StrolchElement;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.privilege.model.Certificate;
|
||||
import li.strolch.testbase.runtime.RuntimeMock;
|
||||
|
@ -23,6 +12,16 @@ import org.junit.AfterClass;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class GenericReportTest {
|
||||
|
||||
private static final String RUNTIME_PATH = "target/GenericReportTest/";
|
||||
|
@ -48,49 +47,49 @@ public class GenericReportTest {
|
|||
public void shouldGenerateJsonReport() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
report.doReportAsJson() //
|
||||
.forEach(e -> {
|
||||
|
||||
switch (e.get("slot").getAsString()) {
|
||||
case "Slot 1" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("20.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 2" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("18.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 3" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("11.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 4" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("16.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
default -> fail("Unhandled result element: \n" + e);
|
||||
case "Slot 1" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("20.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 2" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("18.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 3" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("11.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 4" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("16.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
default -> fail("Unhandled result element: \n" + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -100,7 +99,7 @@ public class GenericReportTest {
|
|||
public void shouldFilterReport1() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
report.filter("Product", "product01") //
|
||||
.doReportAsJson() //
|
||||
|
@ -108,12 +107,12 @@ public class GenericReportTest {
|
|||
|
||||
String slotName = e.get("slot").getAsString();
|
||||
switch (slotName) {
|
||||
case "Slot 1":
|
||||
case "Slot 3":
|
||||
break;
|
||||
default:
|
||||
fail("Unexpected slot name " + slotName + ", should have been filtered!");
|
||||
break;
|
||||
case "Slot 1":
|
||||
case "Slot 3":
|
||||
break;
|
||||
default:
|
||||
fail("Unexpected slot name " + slotName + ", should have been filtered!");
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -123,7 +122,7 @@ public class GenericReportTest {
|
|||
public void shouldFilterReport2() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
report.filter("Product", "product01") //
|
||||
.filter("Location", "location02") //
|
||||
|
@ -144,7 +143,7 @@ public class GenericReportTest {
|
|||
Date from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000);
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
@ -158,7 +157,7 @@ public class GenericReportTest {
|
|||
}
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
Date to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
@ -185,7 +184,10 @@ public class GenericReportTest {
|
|||
Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
List<JsonObject> result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
|
||||
List<JsonObject> result = report
|
||||
.filter("Product", "product01")
|
||||
.dateRange(dateRange)
|
||||
.doReportAsJson()
|
||||
.toList();
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
@ -196,7 +198,10 @@ public class GenericReportTest {
|
|||
Date to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
List<JsonObject> result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
|
||||
List<JsonObject> result = report
|
||||
.filter("Product", "product01")
|
||||
.dateRange(dateRange)
|
||||
.doReportAsJson()
|
||||
.toList();
|
||||
assertEquals(2, result.size());
|
||||
}
|
||||
|
@ -207,8 +212,11 @@ public class GenericReportTest {
|
|||
Date to = new Date(LocalDate.of(2017, 3, 2).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
List<JsonObject> result = report.filter("Product", "product01", "product02").dateRange(dateRange)
|
||||
.doReportAsJson().toList();
|
||||
List<JsonObject> result = report
|
||||
.filter("Product", "product01", "product02")
|
||||
.dateRange(dateRange)
|
||||
.doReportAsJson()
|
||||
.toList();
|
||||
assertEquals(4, result.size());
|
||||
}
|
||||
}
|
||||
|
@ -218,21 +226,27 @@ public class GenericReportTest {
|
|||
public void shouldCreateFilterCriteria() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
MapOfSets<String, StrolchRootElement> filterCriteria = report.generateFilterCriteria(1000);
|
||||
MapOfSets<String, JsonObject> filterCriteria = report.generateFilterCriteria(1000);
|
||||
|
||||
assertFalse(filterCriteria.containsSet("Location"));
|
||||
assertFalse(filterCriteria.containsSet("Slot"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Product").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("product01", "product02"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Storage").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Section").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("section001", "section002"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Product")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("product01", "product02"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Storage")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Section")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("section001", "section002"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,9 +254,10 @@ public class GenericReportTest {
|
|||
public void shouldCreateFilterCriteriaFiltered1() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
MapOfSets<String, StrolchRootElement> filterCriteria = report.filter("Product", "product01")
|
||||
MapOfSets<String, JsonObject> filterCriteria = report
|
||||
.filter("Product", "product01")
|
||||
.generateFilterCriteria(1000);
|
||||
|
||||
// assert sequence of filter criteria is correct
|
||||
|
@ -254,15 +269,21 @@ public class GenericReportTest {
|
|||
|
||||
assertFalse(filterCriteria.containsSet("Location"));
|
||||
assertFalse(filterCriteria.containsSet("Slot"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Product").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("product01"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Storage").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Section").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("section001", "section002"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Product")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("product01"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Storage")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Section")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("section001", "section002"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,13 +291,13 @@ public class GenericReportTest {
|
|||
public void shouldCreateFilterCriteriaFiltered2() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
Date from = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
|
||||
Date to = new Date(LocalDate.of(2017, 3, 5).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
MapOfSets<String, StrolchRootElement> filterCriteria = report //
|
||||
MapOfSets<String, JsonObject> filterCriteria = report //
|
||||
.dateRange(dateRange) //
|
||||
.filter("Product", "product01") //
|
||||
.generateFilterCriteria(1000);
|
||||
|
@ -289,26 +310,26 @@ public class GenericReportTest {
|
|||
public void shouldDoAdditionalJoin() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "slotsByOrderUsageReport")) {
|
||||
Report report = new Report(tx, "slotsByOrderUsageReport")) {
|
||||
|
||||
AtomicInteger slotsFound = new AtomicInteger();
|
||||
report.doReportAsJson().forEach(e -> {
|
||||
|
||||
switch (e.get("slot").getAsString()) {
|
||||
case "Slot 1", "Slot 3" -> {
|
||||
assertEquals("Harry", e.get("firstName").getAsString());
|
||||
assertEquals("Barns", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 2", "Slot 4", "Slot 5" -> {
|
||||
assertEquals("Geoffrey", e.get("firstName").getAsString());
|
||||
assertEquals("Bobcat", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 6" -> {
|
||||
assertEquals("", e.get("firstName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 1", "Slot 3" -> {
|
||||
assertEquals("Harry", e.get("firstName").getAsString());
|
||||
assertEquals("Barns", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 2", "Slot 4", "Slot 5" -> {
|
||||
assertEquals("Geoffrey", e.get("firstName").getAsString());
|
||||
assertEquals("Bobcat", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 6" -> {
|
||||
assertEquals("", e.get("firstName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
<code_scheme name="strolch" version="173">
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
</JSCodeStyleSettings>
|
||||
<code_scheme name="strolch copy" version="173">
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
|
||||
<option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />
|
||||
|
@ -26,8 +22,11 @@
|
|||
<option name="THROWS_LIST_WRAP" value="1" />
|
||||
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
|
||||
<option name="THROWS_KEYWORD_WRAP" value="1" />
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="1" />
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="5" />
|
||||
<option name="WRAP_FIRST_METHOD_IN_CALL_CHAIN" value="true" />
|
||||
<option name="PARENTHESES_EXPRESSION_LPAREN_WRAP" value="true" />
|
||||
<option name="BINARY_OPERATION_WRAP" value="5" />
|
||||
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1" />
|
||||
<option name="FOR_STATEMENT_WRAP" value="1" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.1.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -104,8 +104,8 @@ public class I18nMessage {
|
|||
|
||||
} catch (MissingResourceException e) {
|
||||
if (!missingKeysMap.containsSet(baseName + "_" + locale.toLanguageTag())) {
|
||||
logger.error("Failed to find resource bundle " + baseName + " " + locale.toLanguageTag()
|
||||
+ ", returning current bundle " + this.bundle.getLocale().toLanguageTag());
|
||||
logger.error("Failed to find resource bundle {} {}, returning current bundle {}", baseName,
|
||||
locale.toLanguageTag(), this.bundle.getLocale().toLanguageTag());
|
||||
missingKeysMap.addSet(baseName + "_" + locale.toLanguageTag(), emptySet());
|
||||
}
|
||||
return this.bundle;
|
||||
|
@ -123,10 +123,10 @@ public class I18nMessage {
|
|||
if (isEmpty(this.bundleName))
|
||||
return getMessage();
|
||||
if (!missingKeysMap.containsSet(this.bundleName + "_" + locale.toLanguageTag())) {
|
||||
logger.warn("No bundle found for " + this.bundleName + " " + locale + ". Available are: ");
|
||||
logger.warn("No bundle found for {} {}. Available are: ", this.bundleName, locale);
|
||||
getBundleMap().forEach((s, map) -> {
|
||||
logger.info(" " + s);
|
||||
map.forEach((l, resourceBundle) -> logger.info(" " + l + ": " + map.keySet()));
|
||||
logger.info(" {}", s);
|
||||
map.forEach((l, resourceBundle) -> logger.info(" {}: {}", l, map.keySet()));
|
||||
});
|
||||
missingKeysMap.addSet(this.bundleName + "_" + locale.toLanguageTag(), emptySet());
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ public class I18nMessage {
|
|||
String languageTag = bundle.getLocale().toLanguageTag();
|
||||
String bundleKey = baseName + "_" + languageTag;
|
||||
if (!missingKeysMap.containsElement(bundleKey, this.key)) {
|
||||
logger.error("Key " + this.key + " is missing in bundle " + baseName + " for locale " + languageTag);
|
||||
logger.error("Key {} is missing in bundle {} for locale {}", this.key, baseName, languageTag);
|
||||
missingKeysMap.addElement(bundleKey, this.key);
|
||||
}
|
||||
|
||||
|
@ -265,14 +265,14 @@ public class I18nMessage {
|
|||
try {
|
||||
CodeSource src = I18nMessage.class.getProtectionDomain().getCodeSource();
|
||||
if (src == null) {
|
||||
logger.error(
|
||||
"Failed to find CodeSource for ProtectionDomain " + I18nMessage.class.getProtectionDomain());
|
||||
logger.error("Failed to find CodeSource for ProtectionDomain {}",
|
||||
I18nMessage.class.getProtectionDomain());
|
||||
return;
|
||||
}
|
||||
|
||||
File jarLocationF = new File(src.getLocation().toURI());
|
||||
if (!(jarLocationF.exists() && jarLocationF.getParentFile().isDirectory())) {
|
||||
logger.info("Found JAR repository at " + jarLocationF.getParentFile());
|
||||
logger.info("Found JAR repository at {}", jarLocationF.getParentFile());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -292,16 +292,10 @@ public class I18nMessage {
|
|||
JarEntry je = entries.nextElement();
|
||||
|
||||
String entryName = je.getName();
|
||||
|
||||
if (entryName.startsWith("META-INF") //
|
||||
|| entryName.equals("ENV.properties") //
|
||||
|| entryName.equals("agentVersion.properties") //
|
||||
|| entryName.equals("appVersion.properties") //
|
||||
|| entryName.equals("componentVersion.properties") //
|
||||
|| entryName.equals("strolch_db_version.properties"))
|
||||
if (!entryName.endsWith(".properties"))
|
||||
continue;
|
||||
|
||||
if (!entryName.endsWith(".properties"))
|
||||
if (shouldIgnorePropertyFile(entryName))
|
||||
continue;
|
||||
|
||||
TypedTuple<String, Locale> tuple = parsePropertyName(entryName);
|
||||
|
@ -316,23 +310,21 @@ public class I18nMessage {
|
|||
bundleMap.addElement(bundle.getBaseBundleName(), bundle.getLocale(), bundle);
|
||||
|
||||
String propertyName = entryName.replace('/', '.');
|
||||
logger.info(
|
||||
" Loaded bundle " + bundle.getBaseBundleName() + " " + bundle.getLocale() + " from "
|
||||
+ propertyName + " from JAR " + file.getName());
|
||||
logger.info(" Loaded bundle {} {} from {} from JAR {}", bundle.getBaseBundleName(),
|
||||
bundle.getLocale(), propertyName, file.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File classesD = new File(jarD.getParentFile(), "classes");
|
||||
if (classesD.isDirectory()) {
|
||||
File[] propertyFiles = classesD.listFiles(
|
||||
(dir, name) -> name.endsWith(".properties") && !(name.equals("appVersion.properties")
|
||||
|| name.equals("ENV.properties")));
|
||||
File[] propertyFiles = classesD.listFiles((dir, name) -> name.endsWith(".properties") && !(
|
||||
name.equals("appVersion.properties") || name.equals("ENV.properties")));
|
||||
if (propertyFiles != null) {
|
||||
for (File propertyFile : propertyFiles) {
|
||||
|
||||
logger.info(" Found property file " + propertyFile.getName() + " in classes "
|
||||
+ classesD.getAbsolutePath());
|
||||
logger.info(" Found property file {} in classes {}", propertyFile.getName(),
|
||||
classesD.getAbsolutePath());
|
||||
|
||||
TypedTuple<String, Locale> tuple = parsePropertyName(propertyFile.getName());
|
||||
if (tuple == null)
|
||||
|
@ -346,8 +338,8 @@ public class I18nMessage {
|
|||
}
|
||||
|
||||
bundleMap.addElement(bundle.getBaseBundleName(), bundle.getLocale(), bundle);
|
||||
logger.info(" Loaded bundle " + bundle.getBaseBundleName() + " " + bundle.getLocale()
|
||||
+ " from file " + propertyFile.getName());
|
||||
logger.info(" Loaded bundle {} {} from file {}", bundle.getBaseBundleName(),
|
||||
bundle.getLocale(), propertyFile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,17 +370,17 @@ public class I18nMessage {
|
|||
int languageI = Arrays.binarySearch(Locale.getISOLanguages(), language);
|
||||
int countryI = Arrays.binarySearch(Locale.getISOCountries(), country);
|
||||
if (languageI >= 0 && countryI >= 0)
|
||||
locale = new Locale(language, country);
|
||||
locale = Locale.of(language, country);
|
||||
else {
|
||||
logger.warn("Ignoring bad bundle locale for " + entryName);
|
||||
logger.warn("Ignoring malformed bad bundle locale for {}", entryName);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
int languageI = Arrays.binarySearch(Locale.getISOLanguages(), localeS);
|
||||
if (languageI >= 0)
|
||||
locale = new Locale(localeS);
|
||||
locale = Locale.forLanguageTag(localeS);
|
||||
else {
|
||||
logger.warn("Ignoring bad bundle locale for " + entryName);
|
||||
logger.warn("Ignoring bad bundle locale for {}", entryName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -401,6 +393,7 @@ public class I18nMessage {
|
|||
}
|
||||
|
||||
private static class CustomControl extends ResourceBundle.Control {
|
||||
|
||||
private final InputStream stream;
|
||||
|
||||
public CustomControl(InputStream stream) {
|
||||
|
@ -414,65 +407,76 @@ public class I18nMessage {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean shouldIgnorePropertyFile(String name) {
|
||||
return name.startsWith("META-INF")
|
||||
|| name.equals("ENV.properties")
|
||||
|| name.equals("agentVersion.properties")
|
||||
|| name.equals("appVersion.properties")
|
||||
|| name.equals("componentVersion.properties")
|
||||
|| name.contains("_db_version");
|
||||
}
|
||||
|
||||
private static boolean shouldIgnoreFile(File file) {
|
||||
return file.getName().contains("aopalliance") //
|
||||
|| file.getName().contains("activation") //
|
||||
|| file.getName().contains("antlr") //
|
||||
|| file.getName().contains("assertj-core") //
|
||||
|| file.getName().startsWith("com.sun") //
|
||||
|| file.getName().startsWith("commonj.") //
|
||||
|| file.getName().startsWith("commons-") //
|
||||
|| file.getName().startsWith("jackson-") //
|
||||
|| file.getName().startsWith("hapi-") //
|
||||
|| file.getName().startsWith("jaxb-") //
|
||||
|| file.getName().startsWith("org.hl7.") //
|
||||
|| file.getName().startsWith("listenablefuture-") //
|
||||
|| file.getName().startsWith("j2objc-annotations") //
|
||||
|| file.getName().startsWith("failureaccess-") //
|
||||
|| file.getName().startsWith("error_prone_") //
|
||||
|| file.getName().startsWith("guava-") //
|
||||
|| file.getName().startsWith("org.eclipse") //
|
||||
|| file.getName().startsWith("javax") //
|
||||
|| file.getName().startsWith("jaxws") //
|
||||
|| file.getName().startsWith("jaxrs") //
|
||||
|| file.getName().startsWith("jaxb") //
|
||||
|| file.getName().contains("jsr305") //
|
||||
|| file.getName().contains("c3p0") //
|
||||
|| file.getName().contains("camel") //
|
||||
|| file.getName().contains("checker-qual") //
|
||||
|| file.getName().contains("cron") //
|
||||
|| file.getName().contains("FastInfoset") //
|
||||
|| file.getName().contains("gmbal") //
|
||||
|| file.getName().contains("grizzly") //
|
||||
|| file.getName().contains("gson") //
|
||||
|| file.getName().contains("ha-api") //
|
||||
|| file.getName().contains("HikariCP") //
|
||||
|| file.getName().contains("hk2") //
|
||||
|| file.getName().contains("icu4j") //
|
||||
|| file.getName().contains("jakarta") //
|
||||
|| file.getName().contains("javassist") //
|
||||
|| file.getName().contains("jersey") //
|
||||
|| file.getName().contains("joda-time") //
|
||||
|| file.getName().contains("logback") //
|
||||
|| file.getName().contains("management-api") //
|
||||
|| file.getName().contains("mchange-commons-java") //
|
||||
|| file.getName().contains("mimepull") //
|
||||
|| file.getName().contains("org.abego.treelayout") //
|
||||
|| file.getName().contains("osgi") //
|
||||
|| file.getName().contains("pfl-basic") //
|
||||
|| file.getName().contains("pfl-tf") //
|
||||
|| file.getName().contains("policy-2.7.10") //
|
||||
|| file.getName().contains("postgresql") //
|
||||
|| file.getName().contains("quartz") //
|
||||
|| file.getName().contains("saaj-impl") //
|
||||
|| file.getName().contains("sax") //
|
||||
|| file.getName().contains("slf4j") //
|
||||
|| file.getName().contains("ST4") //
|
||||
|| file.getName().contains("stax-ex") //
|
||||
|| file.getName().contains("stax2-api") //
|
||||
|| file.getName().contains("streambuffer") //
|
||||
|| file.getName().contains("tyrus") //
|
||||
|| file.getName().contains("validation-api") //
|
||||
|| file.getName().contains("yasson");
|
||||
String name = file.getName();
|
||||
return name.contains("aopalliance")
|
||||
|| name.contains("activation")
|
||||
|| name.contains("antlr")
|
||||
|| name.contains("assertj-core")
|
||||
|| name.startsWith("com.sun")
|
||||
|| name.startsWith("commonj.")
|
||||
|| name.startsWith("commons-")
|
||||
|| name.startsWith("jackson-")
|
||||
|| name.startsWith("hapi-")
|
||||
|| name.startsWith("jaxb-")
|
||||
|| name.startsWith("org.hl7.")
|
||||
|| name.startsWith("org.glassfish.")
|
||||
|| name.startsWith("listenablefuture-")
|
||||
|| name.startsWith("j2objc-annotations")
|
||||
|| name.startsWith("failureaccess-")
|
||||
|| name.startsWith("error_prone_")
|
||||
|| name.startsWith("guava-")
|
||||
|| name.startsWith("org.eclipse")
|
||||
|| name.startsWith("javax")
|
||||
|| name.startsWith("jaxws")
|
||||
|| name.startsWith("jaxrs")
|
||||
|| name.startsWith("jaxb")
|
||||
|| name.contains("jsr305")
|
||||
|| name.contains("c3p0")
|
||||
|| name.contains("camel")
|
||||
|| name.contains("checker-qual")
|
||||
|| name.contains("cron")
|
||||
|| name.contains("FastInfoset")
|
||||
|| name.contains("gmbal")
|
||||
|| name.contains("grizzly")
|
||||
|| name.contains("gson")
|
||||
|| name.contains("ha-api")
|
||||
|| name.contains("HikariCP")
|
||||
|| name.contains("hk2")
|
||||
|| name.contains("icu4j")
|
||||
|| name.contains("jakarta")
|
||||
|| name.contains("javassist")
|
||||
|| name.contains("jersey")
|
||||
|| name.contains("joda-time")
|
||||
|| name.contains("logback")
|
||||
|| name.contains("management-api")
|
||||
|| name.contains("mchange-commons-java")
|
||||
|| name.contains("mimepull")
|
||||
|| name.contains("org.abego.treelayout")
|
||||
|| name.contains("osgi")
|
||||
|| name.contains("pfl-basic")
|
||||
|| name.contains("pfl-tf")
|
||||
|| name.contains("policy-2.7.10")
|
||||
|| name.contains("postgresql")
|
||||
|| name.contains("quartz")
|
||||
|| name.contains("saaj-impl")
|
||||
|| name.contains("sax")
|
||||
|| name.contains("slf4j")
|
||||
|| name.contains("ST4")
|
||||
|| name.contains("stax-ex")
|
||||
|| name.contains("stax2-api")
|
||||
|| name.contains("streambuffer")
|
||||
|| name.contains("tyrus")
|
||||
|| name.contains("validation-api")
|
||||
|| name.contains("yasson");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package li.strolch.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Copied from <a
|
||||
* href="https://github.com/ESAPI/esapi-java-legacy/blob/develop/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java#L298">OWASP
|
||||
* ESAPI DefaultEncoder</a>
|
||||
*/
|
||||
public class LdapHelper {
|
||||
|
||||
public static String encodeForLDAP(String input, boolean encodeWildcards) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
// should be replaced with LDAP codec
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// According to Microsoft docs [1,2], the forward slash ('/') MUST be escaped.
|
||||
// According to RFC 4515 Section 3 [3], the forward slash (and other characters) MAY be escaped.
|
||||
// Since Microsoft is a MUST, escape forward slash for all implementations. Also see discussion at [4].
|
||||
// Characters above 0x7F are converted to UTF-8 and then hex encoded in the default case.
|
||||
// [1] https://docs.microsoft.com/en-us/windows/win32/adsi/search-filter-syntax
|
||||
// [2] https://social.technet.microsoft.com/wiki/contents/articles/5312.active-directory-characters-to-escape.aspx
|
||||
// [3] https://tools.ietf.org/search/rfc4515#section-3
|
||||
// [4] https://lists.openldap.org/hyperkitty/list/openldap-technical@openldap.org/thread/3QPDDLO356ONSJM3JUKD7NMPOOIKIQ5T/
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
char c = input.charAt(i);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
sb.append("\\5c");
|
||||
break;
|
||||
case '/':
|
||||
sb.append("\\2f");
|
||||
break;
|
||||
case '*':
|
||||
if (encodeWildcards) {
|
||||
sb.append("\\2a");
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
break;
|
||||
case '(':
|
||||
sb.append("\\28");
|
||||
break;
|
||||
case ')':
|
||||
sb.append("\\29");
|
||||
break;
|
||||
case '\0':
|
||||
sb.append("\\00");
|
||||
break;
|
||||
default:
|
||||
if (c >= 0x80) {
|
||||
final byte[] u = String.valueOf(c).getBytes(StandardCharsets.UTF_8);
|
||||
for (byte b : u) {
|
||||
sb.append(String.format("\\%02x", b));
|
||||
}
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -94,8 +94,7 @@ public class ObjectHelper {
|
|||
return ((String) left).compareToIgnoreCase((String) right);
|
||||
|
||||
if (left instanceof Comparable) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Comparable<Object> comparable = (Comparable<Object>) left;
|
||||
@SuppressWarnings("unchecked") Comparable<Object> comparable = (Comparable<Object>) left;
|
||||
|
||||
return comparable.compareTo(right);
|
||||
}
|
||||
|
@ -105,6 +104,10 @@ public class ObjectHelper {
|
|||
}
|
||||
|
||||
public static boolean contains(Object left, Object right, boolean ignoreCase) {
|
||||
return contains(left, right, ignoreCase, true);
|
||||
}
|
||||
|
||||
public static boolean contains(Object left, Object right, boolean ignoreCase, boolean matchAll) {
|
||||
if (left == null && right == null)
|
||||
return true;
|
||||
if (left == null)
|
||||
|
@ -117,7 +120,7 @@ public class ObjectHelper {
|
|||
if (right instanceof Collection<?> rightCollection) {
|
||||
for (Object l : leftCollection) {
|
||||
for (Object r : rightCollection) {
|
||||
if (contains(l, r, ignoreCase))
|
||||
if (contains(l, r, ignoreCase, matchAll))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -128,7 +131,7 @@ public class ObjectHelper {
|
|||
if (right instanceof String[] rightArr) {
|
||||
for (Object l : leftCollection) {
|
||||
for (Object r : rightArr) {
|
||||
if (contains(l, r, ignoreCase))
|
||||
if (contains(l, r, ignoreCase, matchAll))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +140,7 @@ public class ObjectHelper {
|
|||
}
|
||||
|
||||
for (Object l : leftCollection) {
|
||||
if (contains(l, right, ignoreCase))
|
||||
if (contains(l, right, ignoreCase, matchAll))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -151,18 +154,22 @@ public class ObjectHelper {
|
|||
if (ignoreCase) {
|
||||
leftString = leftString.toLowerCase();
|
||||
for (String s : rightArr) {
|
||||
if (!leftString.contains(s.toLowerCase()))
|
||||
if (!matchAll && leftString.contains(s.toLowerCase()))
|
||||
return true;
|
||||
else if (matchAll && !leftString.contains(s.toLowerCase()))
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
for (String s : rightArr) {
|
||||
if (!leftString.contains(s))
|
||||
if (!matchAll && leftString.contains(s))
|
||||
return true;
|
||||
else if (matchAll && !leftString.contains(s))
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
|
||||
return matchAll;
|
||||
}
|
||||
|
||||
if (right.getClass().isEnum())
|
||||
|
@ -225,7 +232,7 @@ public class ObjectHelper {
|
|||
|
||||
return false;
|
||||
|
||||
} else if (left instanceof String[] leftArr) {
|
||||
} else if (left instanceof Object[] leftArr) {
|
||||
for (Object r : collectionRight) {
|
||||
for (Object l : leftArr) {
|
||||
if (equals(r, l, ignoreCase))
|
||||
|
@ -245,10 +252,35 @@ public class ObjectHelper {
|
|||
}
|
||||
}
|
||||
|
||||
if (right instanceof Object[] arr) {
|
||||
for (Object o : arr) {
|
||||
if (equals(left, o, ignoreCase))
|
||||
return true;
|
||||
if (right instanceof Object[] arrayRight) {
|
||||
|
||||
if (left instanceof Collection<?> collectionLeft) {
|
||||
|
||||
for (Object o : arrayRight) {
|
||||
for (Object l : collectionLeft) {
|
||||
if (equals(l, o, ignoreCase))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} else if (left instanceof Object[] leftArr) {
|
||||
|
||||
for (Object o : arrayRight) {
|
||||
for (Object l : leftArr) {
|
||||
if (equals(l, o, ignoreCase))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
for (Object o : arrayRight) {
|
||||
if (equals(left, o, ignoreCase))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
package li.strolch.utils.collections;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Simple wrapper for two elements
|
||||
*
|
||||
* @param <T>
|
||||
* first object
|
||||
* @param <U>
|
||||
* second object
|
||||
* @param <T> first object
|
||||
* @param <U> second object
|
||||
*
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -66,4 +66,19 @@ public class TypedTuple<T, U> {
|
|||
public boolean hasBoth() {
|
||||
return this.hasFirst() & this.hasSecond();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o)
|
||||
return true;
|
||||
if (o == null || getClass() != o.getClass())
|
||||
return false;
|
||||
TypedTuple<?, ?> that = (TypedTuple<?, ?>) o;
|
||||
return Objects.equals(first, that.first) && Objects.equals(second, that.second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(first, second);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
package li.strolch.utils.concurrent;
|
||||
|
||||
import li.strolch.utils.CheckedRunnable;
|
||||
import li.strolch.utils.CheckedSupplier;
|
||||
import li.strolch.utils.collections.TypedTuple;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -9,13 +16,6 @@ import java.util.concurrent.ScheduledFuture;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import li.strolch.utils.CheckedRunnable;
|
||||
import li.strolch.utils.CheckedSupplier;
|
||||
import li.strolch.utils.collections.TypedTuple;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ElementLockingHandler<T> {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ElementLockingHandler.class);
|
||||
private final TimeUnit tryLockTimeUnit;
|
||||
|
@ -42,10 +42,8 @@ public class ElementLockingHandler<T> {
|
|||
* Note that only {@link #lock(Object)} and {@link #unlock(Object)} are called, if the element was locked
|
||||
* previously, then the lock counter is only reduced to the value prior to the call
|
||||
*
|
||||
* @param element
|
||||
* the element to lock
|
||||
* @param action
|
||||
* the action to perform
|
||||
* @param element the element to lock
|
||||
* @param action the action to perform
|
||||
*/
|
||||
public void lockedExecute(T element, CheckedRunnable action) {
|
||||
lock(element);
|
||||
|
@ -68,10 +66,8 @@ public class ElementLockingHandler<T> {
|
|||
* Note that only {@link #lock(Object)} and {@link #unlock(Object)} are called, if the element was locked
|
||||
* previously, then the lock counter is only reduced to the value prior to the call
|
||||
*
|
||||
* @param element
|
||||
* the element to lock
|
||||
* @param action
|
||||
* the action to perform
|
||||
* @param element the element to lock
|
||||
* @param action the action to perform
|
||||
*
|
||||
* @return the result of the action
|
||||
*/
|
||||
|
@ -79,9 +75,6 @@ public class ElementLockingHandler<T> {
|
|||
lock(element);
|
||||
try {
|
||||
return action.get();
|
||||
} catch (RuntimeException e) {
|
||||
logger.error("Failed to execute action " + action + " for locked element " + element, e);
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to execute action " + action + " for locked element " + element, e);
|
||||
} finally {
|
||||
|
@ -93,11 +86,9 @@ public class ElementLockingHandler<T> {
|
|||
* Locks the given element by creating a {@link ReentrantLock} on it. If the lock is already held by the calling
|
||||
* thread, then the lock count is increased
|
||||
*
|
||||
* @param element
|
||||
* the element for which a {@link ReentrantLock} is to be created and/or locked
|
||||
* @param element the element for which a {@link ReentrantLock} is to be created and/or locked
|
||||
*
|
||||
* @throws ElementLockingException
|
||||
* if the lock could not be acquired
|
||||
* @throws ElementLockingException if the lock could not be acquired
|
||||
*/
|
||||
public void lock(T element) throws ElementLockingException {
|
||||
DBC.PRE.assertNotNull("element may not be null!", element);
|
||||
|
@ -115,11 +106,9 @@ public class ElementLockingHandler<T> {
|
|||
* If the lock must be completely released, then use {@link #releaseLock(T)}
|
||||
* </p>
|
||||
*
|
||||
* @param element
|
||||
* the element for which the current/last {@link ReentrantLock} is to be unlocked
|
||||
* @param element the element for which the current/last {@link ReentrantLock} is to be unlocked
|
||||
*
|
||||
* @throws ElementLockingException
|
||||
* if unlocking failed
|
||||
* @throws ElementLockingException if unlocking failed
|
||||
*/
|
||||
public void unlock(T element) throws ElementLockingException {
|
||||
DBC.PRE.assertNotNull("element may not be null!", element);
|
||||
|
@ -136,11 +125,9 @@ public class ElementLockingHandler<T> {
|
|||
* Releases the lock on the given element, by unlocking all locks, i.e. after this method is called, no lock will be
|
||||
* held anymore by the current thread
|
||||
*
|
||||
* @param element
|
||||
* the element for which the {@link ReentrantLock} is to be released
|
||||
* @param element the element for which the {@link ReentrantLock} is to be released
|
||||
*
|
||||
* @throws ElementLockingException
|
||||
* if the lock could not be released
|
||||
* @throws ElementLockingException if the lock could not be released
|
||||
*/
|
||||
public void releaseLock(T element) throws ElementLockingException {
|
||||
DBC.PRE.assertNotNull("element may not be null!", element);
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package li.strolch.utils.helper;
|
||||
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
@ -9,112 +13,90 @@ import java.io.OutputStream;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Inspired by <a
|
||||
* href="https://github.com/tozny/java-aes-crypto/blob/master/aes-crypto/src/main/java/com/tozny/crypto/android/AesCbcWithIntegrity.java">tozny</a>
|
||||
*/
|
||||
public class AesCryptoHelper {
|
||||
|
||||
private static final String CIPHER = "AES/CBC/PKCS5Padding";
|
||||
private static final String CIPHER_TRANSFORMATION = "AES/CBC/PKCS5Padding";
|
||||
private static final String CIPHER = "AES";
|
||||
private static final int AES_KEY_LENGTH_BITS = 128;
|
||||
private static final int IV_LENGTH_BYTES = 16;
|
||||
private static final int PBE_ITERATION_COUNT = 10000;
|
||||
private static final String PBE_ALGORITHM = "PBKDF2WithHmacSHA256";
|
||||
|
||||
private static final String HMAC_ALGORITHM = "HmacSHA256";
|
||||
private static final int HMAC_KEY_LENGTH_BITS = 256;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AesCryptoHelper.class);
|
||||
|
||||
public static OutputStream wrapEncrypt(char[] password, byte[] salt, OutputStream outputStream) {
|
||||
|
||||
try {
|
||||
|
||||
// set up key
|
||||
SecretKey secret = buildSecret(password, salt);
|
||||
|
||||
SecretKeys secret = buildSecret(password, salt);
|
||||
return wrapEncrypt(secret, outputStream);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static OutputStream wrapEncrypt(SecretKey secret, OutputStream outputStream) {
|
||||
|
||||
public static OutputStream wrapEncrypt(SecretKeys secretKeys, OutputStream outputStream) {
|
||||
try {
|
||||
|
||||
// set up cipher
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secret);
|
||||
byte[] iv = generateIv();
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeys.getConfidentialityKey(), new IvParameterSpec(iv));
|
||||
|
||||
// set up the initialization vector
|
||||
AlgorithmParameters params = cipher.getParameters();
|
||||
byte[] initVector = params.getParameterSpec(IvParameterSpec.class).getIV();
|
||||
DBC.INTERIM.assertEquals("IV must be 16 bytes long!", 16, initVector.length);
|
||||
/*
|
||||
* Now we get back the IV that will actually be used. Some runtimes
|
||||
* versions do funny stuff w/ the IV, so this is to work around bugs:
|
||||
*/
|
||||
iv = cipher.getIV();
|
||||
DBC.INTERIM.assertEquals("IV must be 16 bytes long!", 16, iv.length);
|
||||
|
||||
// write the initialization vector, but not through the cipher output stream!
|
||||
outputStream.write(initVector);
|
||||
outputStream.write(iv);
|
||||
outputStream.flush();
|
||||
|
||||
return new CipherOutputStream(outputStream, cipher);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream wrapDecrypt(char[] password, byte[] salt, InputStream inputStream) {
|
||||
|
||||
try {
|
||||
|
||||
// set up key
|
||||
SecretKey secret = buildSecret(password, salt);
|
||||
|
||||
SecretKeys secret = buildSecret(password, salt);
|
||||
return wrapDecrypt(secret, inputStream);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream wrapDecrypt(SecretKey secret, InputStream inputStream) {
|
||||
|
||||
public static InputStream wrapDecrypt(SecretKeys secretKeys, InputStream inputStream) {
|
||||
try {
|
||||
|
||||
// read the initialization vector from the normal input stream
|
||||
byte[] initVector = new byte[16];
|
||||
if (inputStream.read(initVector) == -1)
|
||||
if (inputStream.read(initVector) != 16)
|
||||
throw new IllegalStateException("Failed to read init vector!");
|
||||
|
||||
// init cipher
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(initVector));
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeys.getConfidentialityKey(), new IvParameterSpec(initVector));
|
||||
|
||||
return new CipherInputStream(inputStream, cipher);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void encrypt(char[] password, byte[] salt, String clearTextFileS, String encryptedFileS) {
|
||||
|
||||
try (InputStream inFile = Files.newInputStream(Paths.get(clearTextFileS));
|
||||
OutputStream outFile = Files.newOutputStream(Paths.get(encryptedFileS))) {
|
||||
|
||||
OutputStream outFile = Files.newOutputStream(Paths.get(encryptedFileS))) {
|
||||
encrypt(password, salt, inFile, outFile);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to encrypt file " + clearTextFileS + " to " + encryptedFileS, e);
|
||||
}
|
||||
|
||||
logger.info("Encrypted file " + clearTextFileS + " to " + encryptedFileS);
|
||||
}
|
||||
|
||||
public static void encrypt(SecretKey secret, String clearTextFileS, String encryptedFileS) {
|
||||
|
||||
try (InputStream inFile = Files.newInputStream(Paths.get(clearTextFileS));
|
||||
OutputStream outFile = Files.newOutputStream(Paths.get(encryptedFileS))) {
|
||||
|
||||
encrypt(secret, inFile, outFile);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to encrypt file " + clearTextFileS + " to " + encryptedFileS, e);
|
||||
}
|
||||
|
@ -123,32 +105,29 @@ public class AesCryptoHelper {
|
|||
}
|
||||
|
||||
public static void encrypt(char[] password, byte[] salt, InputStream inFile, OutputStream outFile) {
|
||||
|
||||
try {
|
||||
|
||||
// set up key
|
||||
SecretKey secret = buildSecret(password, salt);
|
||||
|
||||
SecretKeys secret = buildSecret(password, salt);
|
||||
encrypt(secret, inFile, outFile);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException("Failed to encrypt input stream to output stream!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void encrypt(SecretKey secret, InputStream inFile, OutputStream outFile) {
|
||||
|
||||
public static void encrypt(SecretKeys secretKeys, InputStream inFile, OutputStream outFile) {
|
||||
try {
|
||||
|
||||
// set up cipher
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secret);
|
||||
byte[] iv = generateIv();
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeys.getConfidentialityKey(), new IvParameterSpec(iv));
|
||||
|
||||
// set up the initialization vector
|
||||
AlgorithmParameters params = cipher.getParameters();
|
||||
byte[] initVector = params.getParameterSpec(IvParameterSpec.class).getIV();
|
||||
DBC.INTERIM.assertEquals("IV must be 16 bytes long!", 16, initVector.length);
|
||||
outFile.write(initVector);
|
||||
/*
|
||||
* Now we get back the IV that will actually be used. Some runtimes
|
||||
* versions do funny stuff w/ the IV, so this is to work around bugs:
|
||||
*/
|
||||
iv = cipher.getIV();
|
||||
DBC.INTERIM.assertEquals("IV must be 16 bytes long!", 16, iv.length);
|
||||
|
||||
outFile.write(iv);
|
||||
|
||||
byte[] input = new byte[64];
|
||||
int bytesRead;
|
||||
|
@ -159,24 +138,20 @@ public class AesCryptoHelper {
|
|||
outFile.write(output);
|
||||
}
|
||||
|
||||
byte[] output = cipher.doFinal();
|
||||
if (output != null)
|
||||
outFile.write(output);
|
||||
byte[] byteCipherText = cipher.doFinal();
|
||||
if (byteCipherText != null)
|
||||
outFile.write(byteCipherText);
|
||||
|
||||
outFile.flush();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
throw new RuntimeException("Failed to encrypt input stream to output stream!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void decrypt(char[] password, byte[] salt, String encryptedFileS, String decryptedFileS) {
|
||||
|
||||
try (InputStream fis = Files.newInputStream(Paths.get(encryptedFileS));
|
||||
OutputStream fos = Files.newOutputStream(Paths.get(decryptedFileS))) {
|
||||
|
||||
OutputStream fos = Files.newOutputStream(Paths.get(decryptedFileS))) {
|
||||
decrypt(password, salt, fis, fos);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to decrypt file " + decryptedFileS + " to " + decryptedFileS, e);
|
||||
}
|
||||
|
@ -185,46 +160,26 @@ public class AesCryptoHelper {
|
|||
|
||||
}
|
||||
|
||||
public static void decrypt(SecretKey secret, String encryptedFileS, String decryptedFileS) {
|
||||
|
||||
try (InputStream fis = Files.newInputStream(Paths.get(encryptedFileS));
|
||||
OutputStream fos = Files.newOutputStream(Paths.get(decryptedFileS))) {
|
||||
|
||||
decrypt(secret, fis, fos);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to decrypt file " + decryptedFileS + " to " + decryptedFileS, e);
|
||||
}
|
||||
|
||||
logger.info("Decrypted file " + encryptedFileS + " to " + decryptedFileS);
|
||||
}
|
||||
|
||||
public static void decrypt(char[] password, byte[] salt, InputStream fis, OutputStream fos) {
|
||||
|
||||
try {
|
||||
|
||||
// set up key
|
||||
SecretKey secret = buildSecret(password, salt);
|
||||
|
||||
SecretKeys secret = buildSecret(password, salt);
|
||||
decrypt(secret, fis, fos);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void decrypt(SecretKey secret, InputStream fis, OutputStream fos) {
|
||||
|
||||
public static void decrypt(SecretKeys secretKeys, InputStream fis, OutputStream fos) {
|
||||
try {
|
||||
|
||||
// read the initialization vector
|
||||
byte[] initVector = new byte[16];
|
||||
if (fis.read(initVector) == -1)
|
||||
byte[] iv = new byte[16];
|
||||
if (fis.read(iv) == -1)
|
||||
throw new IllegalStateException("Failed to read init vector!");
|
||||
|
||||
// init cipher
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(initVector));
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeys.getConfidentialityKey(), new IvParameterSpec(iv));
|
||||
|
||||
byte[] in = new byte[64];
|
||||
int read;
|
||||
|
@ -239,7 +194,6 @@ public class AesCryptoHelper {
|
|||
fos.write(output);
|
||||
|
||||
fos.flush();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -254,7 +208,7 @@ public class AesCryptoHelper {
|
|||
try {
|
||||
|
||||
// set up key
|
||||
SecretKey secret = buildSecret(password, salt);
|
||||
SecretKeys secret = buildSecret(password, salt);
|
||||
|
||||
return encrypt(secret, clearTextBytes);
|
||||
|
||||
|
@ -263,25 +217,20 @@ public class AesCryptoHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static byte[] encrypt(SecretKey secret, byte[] clearTextBytes) {
|
||||
|
||||
public static byte[] encrypt(SecretKeys secretKeys, byte[] clearTextBytes) {
|
||||
try {
|
||||
|
||||
// set up cipher
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secret);
|
||||
|
||||
// set up the initialization vector
|
||||
AlgorithmParameters params = cipher.getParameters();
|
||||
byte[] initVector = params.getParameterSpec(IvParameterSpec.class).getIV();
|
||||
DBC.INTERIM.assertEquals("IV must be 16 bytes long!", 16, initVector.length);
|
||||
byte[] iv = generateIv();
|
||||
Cipher aesCipherForEncryption = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
aesCipherForEncryption.init(Cipher.ENCRYPT_MODE, secretKeys.getConfidentialityKey(),
|
||||
new IvParameterSpec(iv));
|
||||
|
||||
// encrypt
|
||||
byte[] encryptedBytes = cipher.doFinal(clearTextBytes);
|
||||
byte[] encryptedBytes = aesCipherForEncryption.doFinal(clearTextBytes);
|
||||
|
||||
// create result bytes
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(initVector.length + encryptedBytes.length);
|
||||
byteBuffer.put(initVector);
|
||||
ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + encryptedBytes.length);
|
||||
byteBuffer.put(iv);
|
||||
byteBuffer.put(encryptedBytes);
|
||||
|
||||
return byteBuffer.array();
|
||||
|
@ -292,47 +241,154 @@ public class AesCryptoHelper {
|
|||
}
|
||||
|
||||
public static byte[] decrypt(char[] password, byte[] salt, byte[] encryptedBytes) {
|
||||
|
||||
try {
|
||||
|
||||
// set up key
|
||||
SecretKey secret = buildSecret(password, salt);
|
||||
|
||||
SecretKeys secret = buildSecret(password, salt);
|
||||
return decrypt(secret, encryptedBytes);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decrypt(SecretKey secret, byte[] encryptedBytes) {
|
||||
|
||||
public static byte[] decrypt(SecretKeys secretKeys, byte[] encryptedBytes) {
|
||||
try {
|
||||
|
||||
// read initialization vector
|
||||
byte[] initVector = new byte[16];
|
||||
System.arraycopy(encryptedBytes, 0, initVector, 0, 16);
|
||||
|
||||
// init cipher
|
||||
Cipher cipher = Cipher.getInstance(CIPHER);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(initVector));
|
||||
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKeys.getConfidentialityKey(), new IvParameterSpec(initVector));
|
||||
|
||||
return cipher.doFinal(encryptedBytes, 16, encryptedBytes.length - 16);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static SecretKey buildSecret(char[] password, byte[] salt) {
|
||||
public static SecretKeys buildSecret(char[] password, byte[] salt) {
|
||||
try {
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||
KeySpec keySpec = new PBEKeySpec(password, salt, 65536, 256);
|
||||
SecretKey secretKey = factory.generateSecret(keySpec);
|
||||
// Get enough random bytes for both the AES key and the HMAC key:
|
||||
KeySpec keySpec = new PBEKeySpec(password, salt, PBE_ITERATION_COUNT,
|
||||
AES_KEY_LENGTH_BITS + HMAC_KEY_LENGTH_BITS);
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(PBE_ALGORITHM);
|
||||
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
|
||||
|
||||
return new SecretKeySpec(secretKey.getEncoded(), "AES");
|
||||
// Split the random bytes into two parts:
|
||||
byte[] confidentialityKeyBytes = copyOfRange(keyBytes, 0, AES_KEY_LENGTH_BITS / 8);
|
||||
byte[] integrityKeyBytes = copyOfRange(keyBytes, AES_KEY_LENGTH_BITS / 8,
|
||||
AES_KEY_LENGTH_BITS / 8 + HMAC_KEY_LENGTH_BITS / 8);
|
||||
|
||||
// Generate the AES key
|
||||
SecretKey confidentialityKey = new SecretKeySpec(confidentialityKeyBytes, CIPHER);
|
||||
|
||||
// Generate the HMAC key
|
||||
SecretKey integrityKey = new SecretKeySpec(integrityKeyBytes, HMAC_ALGORITHM);
|
||||
|
||||
return new SecretKeys(confidentialityKey, integrityKey);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the elements from the start to the end
|
||||
*
|
||||
* @param from the source
|
||||
* @param start the start index to copy
|
||||
* @param end the end index to finish
|
||||
*
|
||||
* @return the new buffer
|
||||
*/
|
||||
private static byte[] copyOfRange(byte[] from, int start, int end) {
|
||||
int length = end - start;
|
||||
byte[] result = new byte[length];
|
||||
System.arraycopy(from, start, result, 0, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a random Initialization Vector (IV) of IV_LENGTH_BYTES.
|
||||
*
|
||||
* @return The byte array of this IV
|
||||
*/
|
||||
public static byte[] generateIv() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] b = new byte[IV_LENGTH_BYTES];
|
||||
random.nextBytes(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder class that has both the secret AES key for encryption (confidentiality) and the secret HMAC key for
|
||||
* integrity.
|
||||
*/
|
||||
|
||||
public static class SecretKeys {
|
||||
private SecretKey confidentialityKey;
|
||||
private SecretKey integrityKey;
|
||||
|
||||
/**
|
||||
* Construct the secret keys container.
|
||||
*
|
||||
* @param confidentialityKeyIn The AES key
|
||||
* @param integrityKeyIn the HMAC key
|
||||
*/
|
||||
public SecretKeys(SecretKey confidentialityKeyIn, SecretKey integrityKeyIn) {
|
||||
setConfidentialityKey(confidentialityKeyIn);
|
||||
setIntegrityKey(integrityKeyIn);
|
||||
}
|
||||
|
||||
public SecretKey getConfidentialityKey() {
|
||||
return confidentialityKey;
|
||||
}
|
||||
|
||||
public void setConfidentialityKey(SecretKey confidentialityKey) {
|
||||
this.confidentialityKey = confidentialityKey;
|
||||
}
|
||||
|
||||
public SecretKey getIntegrityKey() {
|
||||
return integrityKey;
|
||||
}
|
||||
|
||||
public void setIntegrityKey(SecretKey integrityKey) {
|
||||
this.integrityKey = integrityKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the two keys as a string
|
||||
*
|
||||
* @return base64(confidentialityKey):base64(integrityKey)
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return Base64.getEncoder().encodeToString(getConfidentialityKey().getEncoded()) + ":" + Base64
|
||||
.getEncoder()
|
||||
.encodeToString(getIntegrityKey().getEncoded());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + confidentialityKey.hashCode();
|
||||
result = prime * result + integrityKey.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
SecretKeys other = (SecretKeys) obj;
|
||||
if (!integrityKey.equals(other.integrityKey))
|
||||
return false;
|
||||
if (!confidentialityKey.equals(other.confidentialityKey))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,10 +16,11 @@
|
|||
package li.strolch.utils.helper;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import static li.strolch.utils.helper.XmlHelper.getDocumentBuilder;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -27,8 +28,7 @@ public class DomUtil {
|
|||
|
||||
public static DocumentBuilder createDocumentBuilder() {
|
||||
try {
|
||||
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
|
||||
return dbfac.newDocumentBuilder();
|
||||
return getDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
String msg = "No Xml Parser could be loaded: {0}";
|
||||
msg = MessageFormat.format(msg, e.getMessage());
|
||||
|
|
|
@ -40,7 +40,8 @@ public class ExceptionHelper {
|
|||
|
||||
public static String getCallerMethod(int depth) {
|
||||
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) //
|
||||
.walk(frames -> frames.map((StackWalker.StackFrame sf) -> sf.getClassName() + "." + sf.getMethodName())
|
||||
.walk(frames -> frames
|
||||
.map((StackWalker.StackFrame sf) -> sf.getClassName() + "." + sf.getMethodName())
|
||||
.skip(depth)
|
||||
.findFirst()).orElse("UnknownClass.unknownMethod!");
|
||||
}
|
||||
|
@ -51,6 +52,17 @@ public class ExceptionHelper {
|
|||
.orElse("UnknownClass.unknownMethod!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message of the root cause of the given exception
|
||||
*
|
||||
* @param e the exception for which to get the root cause
|
||||
*
|
||||
* @return the message of the root cause of the given exception
|
||||
*/
|
||||
public static String getRootCauseExceptionMessage(Throwable e) {
|
||||
return getExceptionMessage(getRootCause(e), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns a message for the given {@link Throwable}
|
||||
|
@ -61,8 +73,7 @@ public class ExceptionHelper {
|
|||
* in such a case
|
||||
* </p>
|
||||
*
|
||||
* @param t
|
||||
* the {@link Throwable}
|
||||
* @param t the {@link Throwable}
|
||||
*
|
||||
* @return the exception as string
|
||||
*/
|
||||
|
@ -80,11 +91,9 @@ public class ExceptionHelper {
|
|||
* in such a case
|
||||
* </p>
|
||||
*
|
||||
* @param t
|
||||
* the {@link Throwable}
|
||||
* @param withClassName
|
||||
* if true, then exception class name is prepended to the exception message, if the exception message is null,
|
||||
* then this param is ignored
|
||||
* @param t the {@link Throwable}
|
||||
* @param withClassName if true, then exception class name is prepended to the exception message, if the exception
|
||||
* message is null, then this param is ignored
|
||||
*
|
||||
* @return the exception as string
|
||||
*/
|
||||
|
@ -104,8 +113,7 @@ public class ExceptionHelper {
|
|||
* in such a case
|
||||
* </p>
|
||||
*
|
||||
* @param t
|
||||
* the {@link Throwable}
|
||||
* @param t the {@link Throwable}
|
||||
*
|
||||
* @return the exception as string
|
||||
*/
|
||||
|
@ -123,15 +131,15 @@ public class ExceptionHelper {
|
|||
* in such a case
|
||||
* </p>
|
||||
*
|
||||
* @param t
|
||||
* the {@link Throwable}
|
||||
* @param withClassName
|
||||
* if true, then exception class name is prepended to the exception message, if the exception message is null,
|
||||
* then this param is ignored
|
||||
* @param t the {@link Throwable}
|
||||
* @param withClassName if true, then exception class name is prepended to the exception message, if the exception
|
||||
* message is null, then this param is ignored
|
||||
*
|
||||
* @return the exception as string
|
||||
*/
|
||||
public static String getExceptionMessageWithCauses(Throwable t, boolean withClassName) {
|
||||
if (t == null)
|
||||
return "(null)";
|
||||
if (t.getCause() == null)
|
||||
return getExceptionMessage(t, withClassName);
|
||||
|
||||
|
@ -142,13 +150,12 @@ public class ExceptionHelper {
|
|||
/**
|
||||
* Formats the given {@link Throwable}'s stack trace to a string
|
||||
*
|
||||
* @param t
|
||||
* the throwable for which the stack trace is to be formatted to string
|
||||
* @param t the throwable for which the stack trace is to be formatted to string
|
||||
*
|
||||
* @return a string representation of the given {@link Throwable}'s stack trace
|
||||
*/
|
||||
public static String formatException(Throwable t) {
|
||||
String ls = System.getProperty(PROP_LINE_SEPARATOR);
|
||||
String ls = System.lineSeparator();
|
||||
if (!ls.equals(UNIX_LINE_SEP))
|
||||
System.setProperty(PROP_LINE_SEPARATOR, UNIX_LINE_SEP);
|
||||
try {
|
||||
|
@ -165,8 +172,7 @@ public class ExceptionHelper {
|
|||
/**
|
||||
* Formats the given {@link Throwable}'s message including causes to a string
|
||||
*
|
||||
* @param t
|
||||
* the throwable for which the messages are to be formatted to a string
|
||||
* @param t the throwable for which the messages are to be formatted to a string
|
||||
*
|
||||
* @return a string representation of the given {@link Throwable}'s messages including causes
|
||||
*/
|
||||
|
@ -177,11 +183,9 @@ public class ExceptionHelper {
|
|||
/**
|
||||
* Formats the given {@link Throwable}'s message including causes to a string
|
||||
*
|
||||
* @param t
|
||||
* the throwable for which the messages are to be formatted to a string
|
||||
* @param withClassName
|
||||
* if true, then exception class name is prepended to the exception message, if the exception message is null, *
|
||||
* then this param is ignored
|
||||
* @param t the throwable for which the messages are to be formatted to a string
|
||||
* @param withClassName if true, then exception class name is prepended to the exception message, if the exception
|
||||
* message is null, * then this param is ignored
|
||||
*
|
||||
* @return a string representation of the given {@link Throwable}'s messages including causes
|
||||
*/
|
||||
|
@ -196,8 +200,7 @@ public class ExceptionHelper {
|
|||
/**
|
||||
* Returns the root cause for the given {@link Throwable}
|
||||
*
|
||||
* @param throwable
|
||||
* the {@link Throwable} for which to get the root cause
|
||||
* @param throwable the {@link Throwable} for which to get the root cause
|
||||
*
|
||||
* @return the root cause of the given {@link Throwable}
|
||||
*/
|
||||
|
@ -213,8 +216,7 @@ public class ExceptionHelper {
|
|||
/**
|
||||
* Returns {@link #getExceptionMessage(Throwable, boolean)} for the root cause of the given {@link Throwable}
|
||||
*
|
||||
* @param throwable
|
||||
* the throwable for which to get the message of the root cause
|
||||
* @param throwable the throwable for which to get the message of the root cause
|
||||
*
|
||||
* @return {@link #getExceptionMessage(Throwable, boolean)} for the root cause of the given {@link Throwable}
|
||||
*/
|
||||
|
@ -222,11 +224,23 @@ public class ExceptionHelper {
|
|||
return getExceptionMessage(getRootCause(throwable), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #getExceptionMessage(Throwable, boolean)} for the root cause of the given {@link Throwable}
|
||||
*
|
||||
* @param throwable the throwable for which to get the message of the root cause
|
||||
* @param withClassName if true, then exception class name is prepended to the exception message, if the exception
|
||||
* message is null, then this param is ignored
|
||||
*
|
||||
* @return {@link #getExceptionMessage(Throwable, boolean)} for the root cause of the given {@link Throwable}
|
||||
*/
|
||||
public static String getRootCauseMessage(Throwable throwable, boolean withClassName) {
|
||||
return getExceptionMessage(getRootCause(throwable), withClassName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks the causes for the given {@link Throwable} and sees if the given cause exists
|
||||
*
|
||||
* @param throwable
|
||||
* the {@link Throwable} for which to find the given cause type
|
||||
* @param throwable the {@link Throwable} for which to find the given cause type
|
||||
*
|
||||
* @return true if the cause was found, false if not
|
||||
*/
|
||||
|
|
|
@ -15,10 +15,9 @@
|
|||
*/
|
||||
package li.strolch.utils.helper;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
import static li.strolch.utils.helper.StringHelper.isEmpty;
|
||||
import static li.strolch.utils.helper.StringHelper.normalizeLength;
|
||||
import static li.strolch.utils.helper.TempFileOptions.*;
|
||||
import li.strolch.utils.DataUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
|
@ -30,9 +29,10 @@ import java.time.LocalTime;
|
|||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Set;
|
||||
|
||||
import li.strolch.utils.DataUnit;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static li.strolch.utils.helper.StringHelper.isEmpty;
|
||||
import static li.strolch.utils.helper.StringHelper.normalizeLength;
|
||||
import static li.strolch.utils.helper.TempFileOptions.*;
|
||||
|
||||
/**
|
||||
* Helper class for dealing with files
|
||||
|
@ -82,6 +82,7 @@ public class FileHelper {
|
|||
* <li>{@link TempFileOptions#SEPARATE_DATE_SEGMENTS}</li>
|
||||
* <li>{@link TempFileOptions#SEPARATE_HOURS}</li>
|
||||
* <li>{@link TempFileOptions#WITH_HOURS}</li>
|
||||
* <li>{@link TempFileOptions#MILLIS_FIRST}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param tempPath the directory to which to write the file
|
||||
|
@ -92,8 +93,14 @@ public class FileHelper {
|
|||
* @return the temporary file
|
||||
*/
|
||||
public static File getTempFile(File tempPath, String prefix, String suffix, Set<TempFileOptions> options) {
|
||||
return getTempFile(tempPath, prefix, suffix, options, LocalDateTime.now(), System.currentTimeMillis());
|
||||
}
|
||||
|
||||
static File getTempFile(File tempPath, String prefix, String suffix, Set<TempFileOptions> options,
|
||||
LocalDateTime dateTime, long timestamp) {
|
||||
prefix = StringHelper.trimOrEmpty(prefix);
|
||||
suffix = StringHelper.trimOrEmpty(suffix);
|
||||
|
||||
LocalDateTime dateTime = LocalDateTime.now();
|
||||
LocalDate localDate = dateTime.toLocalDate();
|
||||
|
||||
boolean separateDateSegments = options.contains(SEPARATE_DATE_SEGMENTS);
|
||||
|
@ -113,21 +120,22 @@ public class FileHelper {
|
|||
throw new IllegalStateException("Failed to create path " + path.getAbsolutePath());
|
||||
|
||||
boolean appendMillis = options.contains(APPEND_MILLIS);
|
||||
if (appendMillis)
|
||||
prefix = (isEmpty(prefix) ? "" : prefix + "_");
|
||||
else
|
||||
prefix = (isEmpty(prefix) ? "" : prefix);
|
||||
boolean millisFirst = appendMillis && options.contains(MILLIS_FIRST);
|
||||
if (appendMillis) {
|
||||
if (isEmpty(prefix)) {
|
||||
prefix = String.valueOf(timestamp);
|
||||
} else {
|
||||
if (millisFirst)
|
||||
prefix = timestamp + "_" + prefix;
|
||||
else
|
||||
prefix = prefix + "_" + timestamp;
|
||||
}
|
||||
|
||||
suffix = (isEmpty(suffix) ? "" : suffix);
|
||||
if (!suffix.startsWith(".") && appendMillis)
|
||||
suffix = "_" + suffix;
|
||||
|
||||
String fileName;
|
||||
if (appendMillis)
|
||||
fileName = prefix + System.currentTimeMillis() + suffix;
|
||||
else
|
||||
fileName = prefix + suffix;
|
||||
if (!suffix.startsWith("."))
|
||||
prefix = prefix + "_";
|
||||
}
|
||||
|
||||
String fileName = prefix + suffix;
|
||||
return new File(path, fileName);
|
||||
}
|
||||
|
||||
|
@ -279,12 +287,12 @@ public class FileHelper {
|
|||
outBuffer.flush();
|
||||
|
||||
if (checksum) {
|
||||
String fromFileMD5 = StringHelper.toHexString(FileHelper.hashFileMd5(fromFile));
|
||||
String toFileMD5 = StringHelper.toHexString(FileHelper.hashFileMd5(toFile));
|
||||
if (!fromFileMD5.equals(toFileMD5)) {
|
||||
String fromFileSha256 = StringHelper.toHexString(FileHelper.hashFileSha256(fromFile));
|
||||
String toFileSha256 = StringHelper.toHexString(FileHelper.hashFileSha256(toFile));
|
||||
if (!fromFileSha256.equals(toFileSha256)) {
|
||||
FileHelper.logger.error(
|
||||
MessageFormat.format("Copying failed, as MD5 sums are not equal: {0} / {1}", fromFileMD5,
|
||||
toFileMD5));
|
||||
MessageFormat.format("Copying failed, as MD5 sums are not equal: {0} / {1}", fromFileSha256,
|
||||
toFileSha256));
|
||||
if (!toFile.delete())
|
||||
throw new IllegalStateException("Failed to delete file " + toFile);
|
||||
|
||||
|
@ -362,18 +370,6 @@ public class FileHelper {
|
|||
return DataUnit.Bytes.humanizeBytesValue(fileSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the MD5 hash of the given file, returning the hash as a byte array. Use
|
||||
* {@link StringHelper#toHexString(byte[])} to create a HEX string of the bytes
|
||||
*
|
||||
* @param file the file to hash
|
||||
*
|
||||
* @return the hash as a byte array
|
||||
*/
|
||||
public static byte[] hashFileMd5(File file) {
|
||||
return FileHelper.hashFile(file, "MD5");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the SHA256 hash of the given file, returning the hash as a byte array. Use
|
||||
* {@link StringHelper#toHexString(byte[])} to create a HEX string of the bytes
|
||||
|
|
|
@ -10,5 +10,6 @@ public enum TempFileOptions {
|
|||
SEPARATE_DATE_SEGMENTS,
|
||||
WITH_HOURS,
|
||||
SEPARATE_HOURS,
|
||||
APPEND_MILLIS
|
||||
APPEND_MILLIS,
|
||||
MILLIS_FIRST
|
||||
}
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
package li.strolch.utils.helper;
|
||||
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import javax.xml.crypto.dsig.*;
|
||||
import javax.xml.crypto.dsig.dom.DOMSignContext;
|
||||
import javax.xml.crypto.dsig.dom.DOMValidateContext;
|
||||
|
@ -22,13 +30,7 @@ import java.security.PublicKey;
|
|||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import static li.strolch.utils.helper.XmlHelper.getDocumentBuilderFactory;
|
||||
|
||||
public class XmlDomSigner {
|
||||
|
||||
|
@ -230,7 +232,7 @@ public class XmlDomSigner {
|
|||
|
||||
Document doc;
|
||||
try {
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilderFactory dbf = getDocumentBuilderFactory();
|
||||
dbf.setNamespaceAware(true);
|
||||
doc = dbf.newDocumentBuilder().parse(in);
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -28,6 +28,8 @@ import org.w3c.dom.Document;
|
|||
import org.w3c.dom.Element;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXNotRecognizedException;
|
||||
import org.xml.sax.SAXNotSupportedException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.xml.parsers.*;
|
||||
|
@ -94,14 +96,9 @@ public class XmlHelper {
|
|||
* @param xmlFileInputStream the XML {@link InputStream} which is to be parsed
|
||||
*/
|
||||
public static void parseDocument(InputStream xmlFileInputStream, DefaultHandler xmlHandler) {
|
||||
|
||||
try {
|
||||
|
||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
|
||||
SAXParser sp = spf.newSAXParser();
|
||||
SAXParser sp = getSaxParser();
|
||||
sp.parse(xmlFileInputStream, xmlHandler);
|
||||
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new XmlException("Failed to initialize a SAX Parser: " + e.getMessage(), e);
|
||||
} catch (SAXException e) {
|
||||
|
@ -111,6 +108,29 @@ public class XmlHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static SAXParser getSaxParser() throws SAXException, ParserConfigurationException {
|
||||
SAXParserFactory factory = getSaxParserFactory();
|
||||
return factory.newSAXParser();
|
||||
}
|
||||
|
||||
public static SAXParserFactory getSaxParserFactory()
|
||||
throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException {
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
return factory;
|
||||
}
|
||||
|
||||
public static DocumentBuilder getDocumentBuilder() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory dbf = getDocumentBuilderFactory();
|
||||
return dbf.newDocumentBuilder();
|
||||
}
|
||||
|
||||
public static DocumentBuilderFactory getDocumentBuilderFactory() throws ParserConfigurationException {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an XML file on the file system and returns the resulting {@link Document} object
|
||||
*
|
||||
|
@ -130,7 +150,7 @@ public class XmlHelper {
|
|||
|
||||
try {
|
||||
|
||||
SAXParserFactory spf = SAXParserFactory.newInstance();
|
||||
SAXParserFactory spf = getSaxParserFactory();
|
||||
if (nsAware)
|
||||
spf.setNamespaceAware(true);
|
||||
|
||||
|
@ -306,12 +326,7 @@ public class XmlHelper {
|
|||
*/
|
||||
public static Document createDocument() throws RuntimeException {
|
||||
try {
|
||||
|
||||
DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
|
||||
|
||||
return docBuilder.newDocument();
|
||||
|
||||
return getDocumentBuilder().newDocument();
|
||||
} catch (DOMException | ParserConfigurationException e) {
|
||||
throw new XmlException("Failed to create Document: " + e.getLocalizedMessage(), e);
|
||||
}
|
||||
|
@ -333,18 +348,12 @@ public class XmlHelper {
|
|||
public static void marshall(File dstFile, Object object) throws Exception {
|
||||
|
||||
try (FileOutputStream out = new FileOutputStream(dstFile)) {
|
||||
|
||||
JAXBContext jc = JAXBContext.newInstance(object.getClass());
|
||||
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document document = db.newDocument();
|
||||
|
||||
Document document = getDocumentBuilder().newDocument();
|
||||
Marshaller marshaller = jc.createMarshaller();
|
||||
marshaller.marshal(object, document);
|
||||
|
||||
writeDocument(document, out);
|
||||
|
||||
out.flush();
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ public class SynchronizedMapOfListsTest {
|
|||
Future<Boolean> task5 = this.executorService.submit(iterateTask);
|
||||
|
||||
run.set(true);
|
||||
Thread.sleep(20L);
|
||||
Thread.sleep(100L);
|
||||
run.set(false);
|
||||
|
||||
Boolean result0 = task0.get();
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue