Compare commits

...

102 Commits

Author SHA1 Message Date
Robert von Burg 3054d5fca5
[Minor] Added ExceptionHelper.getRootCauseMessage() 2024-05-03 10:08:10 +02:00
Robert von Burg 19825b37a8
[Major] Refactored facet generation for reports
simplified code, move facet value generation into policy, added extension point for formatting criteria to Json, so the facet value's name can be changed in subclasses.
2024-05-02 12:43:21 +02:00
Robert von Burg c65c6624fb
[Fix] Ignoring more bundles in I18nMessage 2024-04-30 12:50:54 +02:00
Robert von Burg 4d7b3faf3f
[Fix] Fixed RestfulStrolchComponent not starting 2024-04-23 15:30:59 +02:00
Robert von Burg dc93800fd1
[Project] Set new version 2.3.0-SNAPSHOT 2024-04-23 14:46:57 +02:00
Robert von Burg 4b78b30600
[Minor] Updated restful logging 2024-04-23 14:29:03 +02:00
Robert von Burg 3edcec4d08
[Minor] Cleaned up logging in BasicAuth and ServletRequestHelper 2024-04-23 11:37:09 +02:00
Robert von Burg cf4ae20d0b
[Minor] Fixed HTTP return code for BasicAuth errors 2024-04-23 10:33:12 +02:00
Robert von Burg 68bd3dd6d6
[New] Added ServletRequestHelper.logRequest() 2024-04-23 10:32:52 +02:00
Robert von Burg c4ac21ec98
[New] Added ExceptionHelper.getRootCauseExceptionMessage() 2024-04-23 10:32:43 +02:00
Robert von Burg a45475f783
[Fix] BasicAuth should check root cause of exception 2024-04-23 09:47:31 +02:00
Robert von Burg baa7a0af4e
[New] Added enabled flag to notifications 2024-04-23 08:19:52 +02:00
Robert von Burg 1080096549
[New] Added LogRequestFilter to log requests prior to filter mapping 2024-04-22 15:01:05 +02:00
Robert von Burg 393659ff19
[Fix] Fixed Strolch exception i18n hierarchy 2024-03-25 10:18:43 +01:00
Robert von Burg a7258f0d06
[Major] Refactored basic auth and getRemoteIp() helper 2024-03-21 14:23:49 +01:00
Robert von Burg 2aca456729
[New] Added CertificateThreadLocal 2024-03-21 14:23:04 +01:00
Robert von Burg f4f9e2c798
[Project] Removed duplicate version declaration 2024-03-21 12:06:48 +01:00
Robert von Burg f051008d68
[Project] Updated dependencies to JAX-WS 2024-03-18 11:11:51 +01:00
Robert von Burg 1e6ac0c042
[Minor] Code cleanup 2024-03-14 16:06:56 +01:00
Robert von Burg a103864eb8
[Project] Updated to camel 3.22.1 2024-03-11 08:31:06 +01:00
Robert von Burg 125dfe9b03
[Fix] Notification only visible to all if forAll
otherwise role, etc. must be set and match
2024-03-07 12:26:51 +01:00
Robert von Burg 2914889172
[Minor] Code cleanup 2024-03-05 15:33:34 +01:00
Robert von Burg d3c261b750
[Fix] Fixed action name in NotificationResource 2024-03-05 15:33:02 +01:00
Robert von Burg de8d35480d
[Minor] Code cleanup 2024-03-05 13:31:15 +01:00
Robert von Burg 1158acfd90
[Fix] Use existing TX when writing audits for invalidating sessions 2024-03-05 13:30:46 +01:00
Robert von Burg faf05126b4
[Fix] Fixed CME in tx.streamCached*() methods 2024-03-05 11:12:12 +01:00
Robert von Burg d00b00d234
[Fix] Fixed updating sessions on changes of user or roles 2024-03-05 10:16:32 +01:00
Robert von Burg 76d38e9af0
[Minor] Made methods public 2024-03-04 17:21:55 +01:00
Robert von Burg f850367d2b
[Fix] Trying to fix broken test 2024-03-04 16:28:44 +01:00
Robert von Burg a4119ef1da
[New] Completed implementation of notifications 2024-03-04 16:10:38 +01:00
Robert von Burg abe089f95c
[Minor] Allow RoleAccessPrivilege to use String parameter 2024-03-04 16:10:14 +01:00
Robert von Burg 15b2788b9a
[WIP] Further implementing notifications 2024-03-01 16:22:11 +01:00
Robert von Burg f02b541848
[Minor] Sorting of supported languages 2024-03-01 16:21:52 +01:00
Robert von Burg 5526f20220
[New] Added StrolchAgent.getRuntimeConfiguration() 2024-03-01 09:58:19 +01:00
Robert von Burg a9c393f02a
[Fix] Implemented to JSON for notifications 2024-02-29 17:05:45 +01:00
Robert von Burg 22359e51d8
[New] Added CRUD services for notifications 2024-02-27 16:44:53 +01:00
Robert von Burg 7b9f2f867f
[New] Extended builder pattern 2024-02-27 14:46:41 +01:00
Robert von Burg a9067bf161
[Major] Refactored SimpleExecution.runWith* actions to take a Consumer<Throwable> in the event of an exception 2024-02-27 12:01:42 +01:00
dependabot[bot] e91a96b549
Bump org.postgresql:postgresql from 42.5.1 to 42.7.2 (#31)
Bumps [org.postgresql:postgresql](https://github.com/pgjdbc/pgjdbc) from 42.5.1 to 42.7.2.
- [Release notes](https://github.com/pgjdbc/pgjdbc/releases)
- [Changelog](https://github.com/pgjdbc/pgjdbc/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pgjdbc/pgjdbc/commits)

---
updated-dependencies:
- dependency-name: org.postgresql:postgresql
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-22 10:28:07 +01:00
Robert von Burg 5c2bcc1620
[New] Added NotificationResource 2024-02-15 17:24:35 +01:00
Robert von Burg f3adc63af2
[New] Added new FileHelper.getTempFile() option MILLIS_FIRST, code cleanup 2024-02-13 09:49:39 +01:00
Robert von Burg a07bd83249
[WIP] Implementing system notifications 2024-02-12 16:43:57 +01:00
Robert von Burg c46c60c8fe
[Project] Changed final name to include strolch- prefix 2024-02-12 12:48:38 +01:00
Robert von Burg a2c720ce48
[New] Allow override of methods in DefaultStrolchSessionHandler 2024-02-09 11:27:40 +01:00
Robert von Burg c66446391a
[Fix] Fixed NPE in toString() 2024-02-06 15:22:43 +01:00
Robert von Burg 69d1f77364
[Fix] Searches should search on items in cache as well
But then ignore the item from the ElementMap stream
2024-02-06 08:00:44 +01:00
Robert von Burg e33950b7a1
[New] Better cache management in TX
- Added items to cache when modified
- added streaming of cached elements
2024-02-06 07:59:28 +01:00
Robert von Burg 56098c96fa
[Minor] Changed the toString()-methods to include the Locator 2024-02-05 14:01:13 +01:00
Robert von Burg 63670a77b6
[Fix] Fixed ExecutionPolicy not initialized before use 2024-01-19 10:22:41 +01:00
Robert von Burg 9c15d0d1d2
[Fix] Don't set Action to WARNING if already Executed 2024-01-18 14:46:34 +01:00
Robert von Burg 77cd9264d0
[Minor] Code cleanup 2024-01-15 14:37:12 +01:00
Robert von Burg bf01876f7c
[Minor] Don't log exception for session validation 2024-01-11 16:24:31 +01:00
Robert von Burg 373235f5e2
[Fix] Renamed varargs methd ExpressionBuilder.isInArray 2024-01-11 14:21:41 +01:00
Robert von Burg 2eeda52f86
[New] ObjectHelper.isIn() extended to allow left and right side to be an array/collection 2024-01-11 13:35:32 +01:00
Robert von Burg ce22f180af
[New] Added new SearchExpressions.paramOnBagType()
This allows to search to have a search, where multiple bags of the same type having a parameter with a given ID, yet different values is searched using the .isIn().

For example:

    paramOnBagType("Owner", "name").isIn("Felix", "Jill")

This allows for one less where clause with lambdas.
2024-01-11 12:33:51 +01:00
Robert von Burg 8b20e4392d
[New] ObjectHelper.isIn() extended to allow left and right side to be an array/collection 2024-01-11 12:32:03 +01:00
Robert von Burg 83f575de41
[New] Added new SearchExpression extract(Function<T, Object>) 2024-01-08 15:40:50 +01:00
Robert von Burg 23054a5bad
[Project] Updated maven-deploy-plugin version to 3.1.1 2024-01-04 15:54:41 +01:00
Robert von Burg d0008f9951
[Project] Removed qodana badge 2024-01-04 11:22:07 +01:00
Robert von Burg 1e2965d588
[Project] Added deployment to repo.strolch.li 2024-01-04 11:09:11 +01:00
Robert von Burg 7a8edc7cc6
[Project] Updated SECURITY.md 2024-01-04 09:40:33 +01:00
Robert von Burg 22e2e5cc68
[Project] Added deployment to repo.strolch.li 2024-01-02 20:32:43 +01:00
Robert von Burg f6a8746069
[Project] Added deployment to repo.strolch.li 2024-01-02 20:28:33 +01:00
Robert von Burg fd5f344764
[Project] Added deployment to repo.strolch.li 2024-01-02 14:47:13 +01:00
Robert von Burg ec8965eee2
[Project] Added deployment to repo.strolch.li 2024-01-01 13:37:16 +01:00
Robert von Burg c17b23b72f
[Fix] Fixed LoggingLoader.reloadLogging() 2023-12-17 20:46:47 +01:00
Robert von Burg 5064249e72
[Project] Updated logback to 1.4.14 2023-12-13 21:08:55 +01:00
Robert von Burg 269bd83ded
[New] search with contains() extended for matchAny or matchAll (default) 2023-12-11 15:00:28 +01:00
Robert von Burg 529c1d0e3b
[Minor] Fixed NPE 2023-12-08 09:31:22 +01:00
Robert von Burg 974c3955ee
[Fix] Fixed NPE in ExecutionPolicy.handleStopped() 2023-12-06 12:24:25 +01:00
Robert von Burg 0d674cf947
[Fix] Fixed ObjectHelper.contains() for left=string,right=array 2023-12-01 10:58:36 +01:00
Robert von Burg ecb4db2377
[Fix] Fixed NPE in GenericReport 2023-11-30 15:42:27 +01:00
Robert von Burg 48968972be
[Fix] BaseLdapPrivilegeHandler throws InvalidCredentialsException on AuthenticationException 2023-11-30 15:17:36 +01:00
Robert von Burg 4e0e0ab22a
[Fix] Refactored AuthenticationResource exception handling 2023-11-30 15:12:55 +01:00
Robert von Burg 92f87cbc77
[Fix] Refactored AuthenticationResource exception handling 2023-11-30 15:05:50 +01:00
Robert von Burg 3c2aac3b9e
[Fix] encode user provider input for LDAP query
Remove OWASP ESAPI as it has too many dependencies
2023-11-30 14:42:48 +01:00
Robert von Burg 004c6775c0
[Minor] Handle lossy conversion of long to int 2023-11-30 13:39:11 +01:00
Robert von Burg 4b0b031b2b
[Fix] Updated AesCryptoHelper
Inspired by https://github.com/tozny/java-aes-crypto/blob/master/aes-crypto/src/main/java/com/tozny/crypto/android/AesCbcWithIntegrity.java
2023-11-30 13:32:01 +01:00
Robert von Burg c5402ba4fb
[Fix] encode user provider input for LDAP query 2023-11-30 13:30:36 +01:00
Robert von Burg 30086f36cc
[Fix] Added missing default branch 2023-11-30 13:12:35 +01:00
Robert von Burg 792221e190
[Fix] Replaced MD5 with SHA256 2023-11-30 13:11:44 +01:00
Robert von Burg 91a2f5651c
[Fix] Updated AesCryptoHelper
Inspired by https://github.com/tozny/java-aes-crypto/blob/master/aes-crypto/src/main/java/com/tozny/crypto/android/AesCbcWithIntegrity.java
2023-11-30 13:10:01 +01:00
Robert von Burg 579cd0b0e5
[Fix] Always use SAXParserFactory feature disallow-doctype-decl=true 2023-11-30 12:16:28 +01:00
Robert von Burg 2ab8d95c0b
[Minor] Handle lossy conversion of long to int 2023-11-30 11:11:50 +01:00
Robert von Burg ed16e969f2
[Project] Downgrade to logback 1.4.8, as 1.4.12 has incompatible API change 2023-11-30 10:49:45 +01:00
Robert von Burg dc0836773c
[Minor] Handle lossy conversion of long to int 2023-11-30 10:36:18 +01:00
dependabot[bot] 3fbb0acc4c
Bump ch.qos.logback:logback-classic from 1.4.5 to 1.4.12 (#30)
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.4.5 to 1.4.12.
- [Commits](https://github.com/qos-ch/logback/compare/v_1.4.5...v_1.4.12)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-30 10:11:09 +01:00
Robert von Burg 74ba243b2f
[Project] Updated codeql to use Java 21 2023-11-30 10:10:22 +01:00
Robert von Burg ec72200d15
[Minor] Added logging of time to init report 2023-11-28 15:42:45 +01:00
Robert von Burg 7867f206a1
[New] Added TypedTuple.equals and .hashCode() 2023-11-28 15:42:22 +01:00
Robert von Burg 4bee5990ac
[Fix] Fixed locator not updated on new IActivityElement parent 2023-11-27 15:48:21 +01:00
Robert von Burg bd03144b6a
[Project] Updated to JDK 21 2023-11-27 08:21:58 +01:00
Robert von Burg 4c8dd9b0d1
[Project] Set new version 2.2.0-SNAPSHOT 2023-11-27 07:53:36 +01:00
Robert von Burg 4f419f02e5
[Fix] Fixed broken tests 2023-11-24 15:17:48 +01:00
Robert von Burg d2627d13e7
[New] Added ExecutionPolicy.getActivityType() 2023-11-24 12:52:11 +01:00
Robert von Burg d6cfc6711a
[Minor] Better Exception handling in ElementLockingHandler 2023-11-24 07:57:05 +01:00
Robert von Burg 6ee6326390
[Project] Updated intellij formatter.xml 2023-11-23 14:44:33 +01:00
Robert von Burg 589f251aa2
[Fix] Fixed bad inspector privilege check 2023-11-21 12:11:08 +01:00
Robert von Burg 68a13d0e77
[Major] Renamed REST classes to have Resource suffix 2023-11-21 10:20:07 +01:00
Robert von Burg 52ae1574f4
[Fix] Fixed not copying user properties to UserRep 2023-11-21 10:19:43 +01:00
Robert von Burg 1bd986d755
[Minor] GenericReport fixes
- allowMissingColumns is now default enabled
- make sure direct criteria is always available on the filter criteria
2023-11-09 14:05:36 +01:00
Robert von Burg f105147ec1
[Project] Updated IntelliJ formatter for chopping down long chains 2023-11-02 09:55:08 +01:00
136 changed files with 3583 additions and 1951 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"/&gt;
* </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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &lt;eitch@eitchnet.ch&gt;
*/
@ -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);
}
}

View File

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

View File

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

View File

@ -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 &lt;eitch@eitchnet.ch&gt;
*/
@ -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());

View File

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

View File

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

View File

@ -10,5 +10,6 @@ public enum TempFileOptions {
SEPARATE_DATE_SEGMENTS,
WITH_HOURS,
SEPARATE_HOURS,
APPEND_MILLIS
APPEND_MILLIS,
MILLIS_FIRST
}

View File

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

View File

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

View File

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