Compare commits
48 Commits
Author | SHA1 | Date |
---|---|---|
Robert von Burg | 3054d5fca5 | |
Robert von Burg | 19825b37a8 | |
Robert von Burg | c65c6624fb | |
Robert von Burg | 4d7b3faf3f | |
Robert von Burg | dc93800fd1 | |
Robert von Burg | 4b78b30600 | |
Robert von Burg | 3edcec4d08 | |
Robert von Burg | cf4ae20d0b | |
Robert von Burg | 68bd3dd6d6 | |
Robert von Burg | c4ac21ec98 | |
Robert von Burg | a45475f783 | |
Robert von Burg | baa7a0af4e | |
Robert von Burg | 1080096549 | |
Robert von Burg | 393659ff19 | |
Robert von Burg | a7258f0d06 | |
Robert von Burg | 2aca456729 | |
Robert von Burg | f4f9e2c798 | |
Robert von Burg | f051008d68 | |
Robert von Burg | 1e6ac0c042 | |
Robert von Burg | a103864eb8 | |
Robert von Burg | 125dfe9b03 | |
Robert von Burg | 2914889172 | |
Robert von Burg | d3c261b750 | |
Robert von Burg | de8d35480d | |
Robert von Burg | 1158acfd90 | |
Robert von Burg | faf05126b4 | |
Robert von Burg | d00b00d234 | |
Robert von Burg | 76d38e9af0 | |
Robert von Burg | f850367d2b | |
Robert von Burg | a4119ef1da | |
Robert von Burg | abe089f95c | |
Robert von Burg | 15b2788b9a | |
Robert von Burg | f02b541848 | |
Robert von Burg | 5526f20220 | |
Robert von Burg | a9c393f02a | |
Robert von Burg | 22359e51d8 | |
Robert von Burg | 7b9f2f867f | |
Robert von Burg | a9067bf161 | |
dependabot[bot] | e91a96b549 | |
Robert von Burg | 5c2bcc1620 | |
Robert von Burg | f3adc63af2 | |
Robert von Burg | a07bd83249 | |
Robert von Burg | c46c60c8fe | |
Robert von Burg | a2c720ce48 | |
Robert von Burg | c66446391a | |
Robert von Burg | 69d1f77364 | |
Robert von Burg | e33950b7a1 | |
Robert von Burg | 56098c96fa |
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -81,20 +81,14 @@ public class StrolchAgent {
|
|||
this.appVersion = appVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link StrolchConfiguration}
|
||||
*
|
||||
* @return the {@link StrolchConfiguration}
|
||||
*/
|
||||
public StrolchConfiguration getStrolchConfiguration() {
|
||||
return this.strolchConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the container
|
||||
*
|
||||
* @return the container
|
||||
*/
|
||||
public RuntimeConfiguration getRuntimeConfiguration() {
|
||||
return this.strolchConfiguration.getRuntimeConfiguration();
|
||||
}
|
||||
|
||||
public ComponentContainer getContainer() {
|
||||
return this.container;
|
||||
}
|
||||
|
@ -411,8 +405,9 @@ public class StrolchAgent {
|
|||
|
||||
public JsonObject getSystemState(long updateInterval, TimeUnit updateIntervalUnit) {
|
||||
|
||||
if (this.systemState == null ||
|
||||
System.currentTimeMillis() - this.systemStateUpdateTime > updateIntervalUnit.toMillis(updateInterval)) {
|
||||
if (this.systemState == null
|
||||
|| System.currentTimeMillis() - this.systemStateUpdateTime > updateIntervalUnit.toMillis(
|
||||
updateInterval)) {
|
||||
this.systemState = new JsonObject();
|
||||
|
||||
JsonObject osJ = new JsonObject();
|
||||
|
@ -474,8 +469,8 @@ public class StrolchAgent {
|
|||
File configPathF = runtimeConfig.getConfigPath();
|
||||
File dataPathF = runtimeConfig.getDataPath();
|
||||
File tempPathF = runtimeConfig.getTempPath();
|
||||
StrolchConfiguration newConfig = parseConfiguration(runtimeConfig.getEnvironment(), configPathF,
|
||||
dataPathF, tempPathF);
|
||||
StrolchConfiguration newConfig = parseConfiguration(runtimeConfig.getEnvironment(), configPathF, dataPathF,
|
||||
tempPathF);
|
||||
|
||||
for (String name : this.container.getComponentNames()) {
|
||||
ComponentConfiguration newComponentConfig = newConfig.getComponentConfiguration(name);
|
||||
|
|
|
@ -31,6 +31,8 @@ import li.strolch.runtime.configuration.ComponentConfiguration;
|
|||
import li.strolch.runtime.privilege.PrivilegeHandler;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.runtime.StrolchConstants.*;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -70,9 +72,9 @@ public class DefaultRealmHandler extends StrolchComponent implements RealmHandle
|
|||
@Override
|
||||
public void setup(ComponentConfiguration configuration) {
|
||||
this.realms = new HashMap<>(1);
|
||||
String[] realms = configuration.getStringArray(PROP_REALMS, StrolchConstants.DEFAULT_REALM);
|
||||
String[] realms = configuration.getStringArray(PROP_REALMS, DEFAULT_REALM);
|
||||
for (String realmName : realms) {
|
||||
String dataStoreModeKey = StrolchConstants.makeRealmKey(realmName, PREFIX_DATA_STORE_MODE);
|
||||
String dataStoreModeKey = makeRealmKey(realmName, PREFIX_DATA_STORE_MODE);
|
||||
String realmMode = configuration.getString(dataStoreModeKey, null);
|
||||
InternalStrolchRealm realm = buildRealm(realmName, realmMode);
|
||||
this.realms.put(realmName, realm);
|
||||
|
|
|
@ -30,6 +30,8 @@ import li.strolch.runtime.configuration.StrolchConfigurationException;
|
|||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
|
||||
import static li.strolch.agent.impl.DefaultRealmHandler.*;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
|
@ -87,7 +89,7 @@ public class TransientRealm extends InternalStrolchRealm {
|
|||
public void initialize(ComponentContainer container, ComponentConfiguration configuration) {
|
||||
super.initialize(container, configuration);
|
||||
|
||||
String key = StrolchConstants.makeRealmKey(getRealm(), DefaultRealmHandler.PREFIX_DATA_STORE_FILE);
|
||||
String key = StrolchConstants.makeRealmKey(getRealm(), PREFIX_DATA_STORE_FILE);
|
||||
if (!configuration.hasProperty(key)) {
|
||||
String msg = "There is no data store file for realm {0}. Set a property with key {1}";
|
||||
msg = MessageFormat.format(msg, getRealm(), key);
|
||||
|
|
|
@ -38,11 +38,11 @@ import li.strolch.utils.objectfilter.ObjectFilterStatistics;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static li.strolch.agent.api.StrolchAgent.getUniqueId;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.model.Tags.*;
|
||||
|
@ -515,7 +515,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (locator.getSize() < 3) {
|
||||
String msg
|
||||
= "The locator is invalid as it does not have at least three path elements (e.g. Resource/MyType/@id): {0}";
|
||||
msg = MessageFormat.format(msg, locator.toString());
|
||||
msg = format(msg, locator.toString());
|
||||
throw new StrolchModelException(msg);
|
||||
}
|
||||
|
||||
|
@ -535,7 +535,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (allowNull)
|
||||
return null;
|
||||
String msg = "No top level object could be found with locator {0}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator));
|
||||
throw new StrolchModelException(format(msg, locator));
|
||||
}
|
||||
|
||||
if (elements.size() == 3)
|
||||
|
@ -551,7 +551,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (allowNull)
|
||||
return null;
|
||||
String msg = "Could not find ParameterBag for locator {0} on element {1}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, rootElement.getLocator()));
|
||||
throw new StrolchModelException(format(msg, locator, rootElement.getLocator()));
|
||||
}
|
||||
|
||||
if (elements.size() == 5)
|
||||
|
@ -563,7 +563,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
if (allowNull)
|
||||
return null;
|
||||
String msg = "Could not find Parameter for locator {0} on element {1}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, bag.getLocator()));
|
||||
throw new StrolchModelException(format(msg, locator, bag.getLocator()));
|
||||
}
|
||||
return (T) parameter;
|
||||
|
||||
|
@ -571,7 +571,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (elements.size() != 5) {
|
||||
String msg = "Missing state Id on locator {0}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator));
|
||||
throw new StrolchModelException(format(msg, locator));
|
||||
}
|
||||
|
||||
Resource resource = rootElement.asResource();
|
||||
|
@ -589,7 +589,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (!(element instanceof Activity)) {
|
||||
String msg = "Invalid locator {0} with part {1} as not an Activity but deeper element specified";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, next));
|
||||
throw new StrolchModelException(format(msg, locator, next));
|
||||
}
|
||||
|
||||
element = ((Activity) element).getElement(next);
|
||||
|
@ -602,7 +602,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
return null;
|
||||
|
||||
String msg = "Invalid locator {0} with part {1}";
|
||||
throw new StrolchModelException(MessageFormat.format(msg, locator, stateOrBagOrActivity));
|
||||
throw new StrolchModelException(format(msg, locator, stateOrBagOrActivity));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -641,8 +641,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
break;
|
||||
if (!parents.add(parent))
|
||||
throw new IllegalStateException(
|
||||
"circular dependencies from " + element.getLocator() + " to " + parent.getLocator() +
|
||||
" on relations parameter " + parentParamKey);
|
||||
format("circular dependencies from {0} to {1} on relations parameter {2}", element.getLocator(),
|
||||
parent.getLocator(), parentParamKey));
|
||||
|
||||
t = parent.getParameter(bagKey, paramKey);
|
||||
}
|
||||
|
@ -787,7 +787,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (assertExists && refP.isEmpty()) {
|
||||
String msg = "The Order with type \"{0}\" and id \"{1}\" does not exist for param \"{2}\"";
|
||||
throw new StrolchException(MessageFormat.format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
throw new StrolchException(format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
}
|
||||
|
||||
if (refP.isEmpty())
|
||||
|
@ -901,7 +901,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (assertExists && refP.isEmpty()) {
|
||||
String msg = "The Resource with type \"{0}\" and id \"{1}\" does not exist for param \"{2}\"";
|
||||
throw new StrolchException(MessageFormat.format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
throw new StrolchException(format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
}
|
||||
|
||||
if (refP.isEmpty())
|
||||
|
@ -1031,7 +1031,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (assertExists && refP.isEmpty()) {
|
||||
String msg = "The Activity with type \"{0}\" and id \"{1}\" does not exist for param \"{2}\"";
|
||||
throw new StrolchException(MessageFormat.format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
throw new StrolchException(format(msg, refP.getUom(), refP.getValue(), refP.getLocator()));
|
||||
}
|
||||
|
||||
if (refP.isEmpty())
|
||||
|
@ -1134,6 +1134,21 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
this.objectFilter.removeObjectCache(locator.get(0), locator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResourceCached(String type, String id) {
|
||||
return this.resourceCache.containsElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOrderCached(String type, String id) {
|
||||
return this.orderCache.containsElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActivityCached(String type, String id) {
|
||||
return this.activityCache.containsElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource getCachedResource(String type, String id) {
|
||||
return this.resourceCache.getElement(type, id);
|
||||
|
@ -1149,6 +1164,66 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
return this.activityCache.getElement(type, id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Resource> streamCachedResources(String... types) {
|
||||
if (types.length == 0)
|
||||
return this.resourceCache.values().stream();
|
||||
if (types.length == 1) {
|
||||
String type = types[0];
|
||||
if (!this.resourceCache.containsMap(type))
|
||||
return Stream.empty();
|
||||
return new ArrayList<>(this.resourceCache.getMap(type).values()).stream();
|
||||
}
|
||||
return this.resourceCache.values().stream().filter(element -> {
|
||||
String resType = element.getType();
|
||||
for (String type : types) {
|
||||
if (resType.equals(type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Order> streamCachedOrders(String... types) {
|
||||
if (types.length == 0)
|
||||
return this.orderCache.values().stream();
|
||||
if (types.length == 1) {
|
||||
String type = types[0];
|
||||
if (!this.orderCache.containsMap(type))
|
||||
return Stream.empty();
|
||||
return new ArrayList<>(this.orderCache.getMap(type).values()).stream();
|
||||
}
|
||||
return this.orderCache.values().stream().filter(element -> {
|
||||
String resType = element.getType();
|
||||
for (String type : types) {
|
||||
if (resType.equals(type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Activity> streamCachedActivities(String... types) {
|
||||
if (types.length == 0)
|
||||
return this.activityCache.values().stream();
|
||||
if (types.length == 1) {
|
||||
String type = types[0];
|
||||
if (!this.activityCache.containsMap(type))
|
||||
return Stream.empty();
|
||||
return new ArrayList<>(this.activityCache.getMap(type).values()).stream();
|
||||
}
|
||||
return this.activityCache.values().stream().filter(element -> {
|
||||
String resType = element.getType();
|
||||
for (String type : types) {
|
||||
if (resType.equals(type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasResource(String type, String id) {
|
||||
boolean inFilter = hasElementInFilter(Tags.RESOURCE, Resource.locatorFor(type, id));
|
||||
|
@ -1253,6 +1328,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
lock(resource);
|
||||
this.objectFilter.add(Tags.RESOURCE, resource.getLocator(), resource);
|
||||
}
|
||||
|
||||
this.resourceCache.addElement(resource.getType(), resource.getId(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1271,6 +1348,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
lock(order);
|
||||
this.objectFilter.add(Tags.ORDER, order.getLocator(), order);
|
||||
}
|
||||
|
||||
this.orderCache.addElement(order.getType(), order.getId(), order);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1289,6 +1368,8 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
lock(activity);
|
||||
this.objectFilter.add(Tags.ACTIVITY, activity.getLocator(), activity);
|
||||
}
|
||||
|
||||
this.activityCache.addElement(activity.getType(), activity.getId(), activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1298,6 +1379,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
resource.assertNotReadonly();
|
||||
lock(resource);
|
||||
this.objectFilter.add(Tags.RESOURCE, resource.getLocator(), resource);
|
||||
this.resourceCache.addElement(resource.getType(), resource.getId(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1307,6 +1389,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
order.assertNotReadonly();
|
||||
lock(order);
|
||||
this.objectFilter.add(Tags.ORDER, order.getLocator(), order);
|
||||
this.orderCache.addElement(order.getType(), order.getId(), order);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1316,6 +1399,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
activity.assertNotReadonly();
|
||||
lock(activity);
|
||||
this.objectFilter.add(Tags.ACTIVITY, activity.getLocator(), activity);
|
||||
this.activityCache.addElement(activity.getType(), activity.getId(), activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1324,6 +1408,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
DBC.PRE.assertNotNull("resource must not be null", resource);
|
||||
resource.assertNotReadonly();
|
||||
this.objectFilter.update(Tags.RESOURCE, resource.getLocator(), resource);
|
||||
this.resourceCache.addElement(resource.getType(), resource.getId(), resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1332,6 +1417,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
DBC.PRE.assertNotNull("order must not be null", order);
|
||||
order.assertNotReadonly();
|
||||
this.objectFilter.update(Tags.ORDER, order.getLocator(), order);
|
||||
this.orderCache.addElement(order.getType(), order.getId(), order);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1340,6 +1426,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
DBC.PRE.assertNotNull("activity must not be null", activity);
|
||||
activity.assertNotReadonly();
|
||||
this.objectFilter.update(Tags.ACTIVITY, activity.getLocator(), activity);
|
||||
this.activityCache.addElement(activity.getType(), activity.getId(), activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1383,7 +1470,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
List<Resource> changedR = this.objectFilter.getRemoved(Resource.class, Tags.RESOURCE);
|
||||
if (changedR.size() == 1) {
|
||||
RemoveResourceCommand cmd = new RemoveResourceCommand(this);
|
||||
cmd.setResource(changedR.get(0));
|
||||
cmd.setResource(changedR.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedR.size() > 1) {
|
||||
RemoveResourcesCommand cmd = new RemoveResourcesCommand(this);
|
||||
|
@ -1395,7 +1482,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedR = this.objectFilter.getUpdated(Resource.class, Tags.RESOURCE);
|
||||
if (changedR.size() == 1) {
|
||||
UpdateResourceCommand cmd = new UpdateResourceCommand(this);
|
||||
cmd.setResource(changedR.get(0));
|
||||
cmd.setResource(changedR.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedR.size() > 1) {
|
||||
UpdateResourcesCommand cmd = new UpdateResourcesCommand(this);
|
||||
|
@ -1407,7 +1494,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedR = this.objectFilter.getAdded(Resource.class, Tags.RESOURCE);
|
||||
if (changedR.size() == 1) {
|
||||
AddResourceCommand cmd = new AddResourceCommand(this);
|
||||
cmd.setResource(changedR.get(0));
|
||||
cmd.setResource(changedR.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedR.size() > 1) {
|
||||
AddResourcesCommand cmd = new AddResourcesCommand(this);
|
||||
|
@ -1422,7 +1509,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
List<Order> changedO = this.objectFilter.getRemoved(Order.class, Tags.ORDER);
|
||||
if (changedO.size() == 1) {
|
||||
RemoveOrderCommand cmd = new RemoveOrderCommand(this);
|
||||
cmd.setOrder(changedO.get(0));
|
||||
cmd.setOrder(changedO.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedO.size() > 1) {
|
||||
RemoveOrdersCommand cmd = new RemoveOrdersCommand(this);
|
||||
|
@ -1434,7 +1521,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedO = this.objectFilter.getUpdated(Order.class, Tags.ORDER);
|
||||
if (changedO.size() == 1) {
|
||||
UpdateOrderCommand cmd = new UpdateOrderCommand(this);
|
||||
cmd.setOrder(changedO.get(0));
|
||||
cmd.setOrder(changedO.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedO.size() > 1) {
|
||||
UpdateOrdersCommand cmd = new UpdateOrdersCommand(this);
|
||||
|
@ -1446,7 +1533,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedO = this.objectFilter.getAdded(Order.class, Tags.ORDER);
|
||||
if (changedO.size() == 1) {
|
||||
AddOrderCommand cmd = new AddOrderCommand(this);
|
||||
cmd.setOrder(changedO.get(0));
|
||||
cmd.setOrder(changedO.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedO.size() > 1) {
|
||||
AddOrdersCommand cmd = new AddOrdersCommand(this);
|
||||
|
@ -1461,7 +1548,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
List<Activity> changedA = this.objectFilter.getRemoved(Activity.class, Tags.ACTIVITY);
|
||||
if (changedA.size() == 1) {
|
||||
RemoveActivityCommand cmd = new RemoveActivityCommand(this);
|
||||
cmd.setActivity(changedA.get(0));
|
||||
cmd.setActivity(changedA.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedA.size() > 1) {
|
||||
RemoveActivitiesCommand cmd = new RemoveActivitiesCommand(this);
|
||||
|
@ -1473,7 +1560,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedA = this.objectFilter.getUpdated(Activity.class, Tags.ACTIVITY);
|
||||
if (changedA.size() == 1) {
|
||||
UpdateActivityCommand cmd = new UpdateActivityCommand(this);
|
||||
cmd.setActivity(changedA.get(0));
|
||||
cmd.setActivity(changedA.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedA.size() > 1) {
|
||||
UpdateActivitiesCommand cmd = new UpdateActivitiesCommand(this);
|
||||
|
@ -1485,7 +1572,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
changedA = this.objectFilter.getAdded(Activity.class, Tags.ACTIVITY);
|
||||
if (changedA.size() == 1) {
|
||||
AddActivityCommand cmd = new AddActivityCommand(this);
|
||||
cmd.setActivity(changedA.get(0));
|
||||
cmd.setActivity(changedA.getFirst());
|
||||
add(cmd);
|
||||
} else if (changedA.size() > 1) {
|
||||
AddActivitiesCommand cmd = new AddActivitiesCommand(this);
|
||||
|
@ -1531,7 +1618,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
this.closeStrategy = TransactionCloseStrategy.ROLLBACK;
|
||||
|
||||
String msg = "Strolch Transaction for realm {0} failed due to {1}";
|
||||
msg = MessageFormat.format(msg, getRealmName(), getExceptionMessage(e));
|
||||
msg = format(msg, getRealmName(), getExceptionMessage(e));
|
||||
throw new StrolchTransactionException(msg, e);
|
||||
}
|
||||
}
|
||||
|
@ -1595,7 +1682,7 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
@Override
|
||||
public void autoCloseableRollback() {
|
||||
long start = System.nanoTime();
|
||||
logger.warn(MessageFormat.format("Rolling back TX for realm {0}...", getRealmName()));
|
||||
logger.warn(format("Rolling back TX for realm {0}...", getRealmName()));
|
||||
try {
|
||||
this.txResult.setState(TransactionState.ROLLING_BACK);
|
||||
undoCommands();
|
||||
|
@ -1808,12 +1895,13 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
OperationsLog operationsLog = container.getComponent(OperationsLog.class);
|
||||
operationsLog.addMessage(new LogMessage(this.realm.getRealm(), this.certificate.getUsername(),
|
||||
Locator.valueOf(AGENT, "tx", this.action, getUniqueId()), LogSeverity.Exception,
|
||||
LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"),
|
||||
"agent.tx.failed").withException(e).value("reason", e));
|
||||
LogMessageState.Information, ResourceBundle.getBundle("strolch-agent"), "agent.tx.failed")
|
||||
.withException(e)
|
||||
.value("reason", e));
|
||||
}
|
||||
|
||||
String msg = "Strolch Transaction for realm {0} failed due to {1}\n{2}";
|
||||
msg = MessageFormat.format(msg, getRealmName(), getExceptionMessage(e), sb.toString());
|
||||
msg = format(msg, getRealmName(), getExceptionMessage(e), sb.toString());
|
||||
StrolchTransactionException ex = new StrolchTransactionException(msg, e);
|
||||
|
||||
if (throwEx)
|
||||
|
@ -1922,10 +2010,10 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
|
||||
if (this.auditTrail != null && !isSuppressAuditsForAudits()) {
|
||||
if (this.realm.isAuditTrailEnabledForRead())
|
||||
auditsForAudits(audits, AccessType.READ, Tags.AUDIT, this.auditTrail.getRead());
|
||||
auditsForAudits(audits, AccessType.CREATE, Tags.AUDIT, this.auditTrail.getCreated());
|
||||
auditsForAudits(audits, AccessType.UPDATE, Tags.AUDIT, this.auditTrail.getUpdated());
|
||||
auditsForAudits(audits, AccessType.DELETE, Tags.AUDIT, this.auditTrail.getDeleted());
|
||||
auditsForAudits(audits, AccessType.READ, this.auditTrail.getRead());
|
||||
auditsForAudits(audits, AccessType.CREATE, this.auditTrail.getCreated());
|
||||
auditsForAudits(audits, AccessType.UPDATE, this.auditTrail.getUpdated());
|
||||
auditsForAudits(audits, AccessType.DELETE, this.auditTrail.getDeleted());
|
||||
}
|
||||
|
||||
if (!audits.isEmpty())
|
||||
|
@ -1940,9 +2028,9 @@ public abstract class AbstractTransaction implements StrolchTransaction {
|
|||
}
|
||||
}
|
||||
|
||||
private void auditsForAudits(List<Audit> audits, AccessType accessType, String elementType, Set<Audit> elements) {
|
||||
private void auditsForAudits(List<Audit> audits, AccessType accessType, Set<Audit> elements) {
|
||||
for (Audit element : elements) {
|
||||
audits.add(auditFrom(accessType, elementType, StringHelper.DASH, element.getId().toString()));
|
||||
audits.add(auditFrom(accessType, Tags.AUDIT, StringHelper.DASH, element.getId().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1571,6 +1571,21 @@ public interface StrolchTransaction extends AutoCloseable {
|
|||
*/
|
||||
void removeFromCache(Locator locator);
|
||||
|
||||
/**
|
||||
* Returns true if the given resource is currently cached
|
||||
*/
|
||||
boolean isResourceCached(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns true if the given order is currently cached
|
||||
*/
|
||||
boolean isOrderCached(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns true if the given activity is currently cached
|
||||
*/
|
||||
boolean isActivityCached(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns the cached resource with the given type and id, or null if not yet fetched
|
||||
*
|
||||
|
@ -1607,6 +1622,21 @@ public interface StrolchTransaction extends AutoCloseable {
|
|||
*/
|
||||
Activity getCachedActivity(String type, String id);
|
||||
|
||||
/**
|
||||
* Returns a stream of resources in the cache
|
||||
*/
|
||||
Stream<Resource> streamCachedResources(String... types);
|
||||
|
||||
/**
|
||||
* Returns a stream of orders in the cache
|
||||
*/
|
||||
Stream<Order> streamCachedOrders(String... types);
|
||||
|
||||
/**
|
||||
* Returns a stream of activities in the cache
|
||||
*/
|
||||
Stream<Activity> streamCachedActivities(String... types);
|
||||
|
||||
/**
|
||||
* Returns true if the @{@link Resource} exists with the given type and ID
|
||||
*
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package li.strolch.policy.notifications;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchModelConstants;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.utils.collections.DateRange;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
|
||||
public class DefaultNotificationsPolicy extends NotificationsPolicy {
|
||||
public DefaultNotificationsPolicy(StrolchTransaction tx) {
|
||||
super(tx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> findUserNotifications() {
|
||||
return tx().streamResources(StrolchModelConstants.TYPE_NOTIFICATION).filter(this::isForUser).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canView(Resource notification) {
|
||||
return isForAll(notification) || isForRole(notification) || isForGroup(notification);
|
||||
}
|
||||
|
||||
protected boolean isForUser(Resource notification) {
|
||||
if (!isActive(notification))
|
||||
return false;
|
||||
return isEnabled(notification) && (
|
||||
isForAll(notification) || isForRole(notification) || isForGroup(notification));
|
||||
}
|
||||
|
||||
protected boolean isActive(Resource notification) {
|
||||
return new DateRange()
|
||||
.from(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_FROM), true)
|
||||
.to(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_TO), true)
|
||||
.contains(ZonedDateTime.now());
|
||||
}
|
||||
|
||||
protected boolean isEnabled(Resource notification) {
|
||||
return notification.getBoolean(BAG_VISIBILITY, PARAM_ENABLED);
|
||||
}
|
||||
|
||||
protected boolean isForAll(Resource notification) {
|
||||
return notification.getBoolean(BAG_VISIBILITY, PARAM_FOR_ALL);
|
||||
}
|
||||
|
||||
protected boolean isForRole(Resource notification) {
|
||||
List<String> roles = notification.getStringList(BAG_VISIBILITY, PARAM_ROLES);
|
||||
return roles.stream().anyMatch(r -> tx().getCertificate().hasRole(r));
|
||||
}
|
||||
|
||||
protected boolean isForGroup(Resource notification) {
|
||||
List<String> groups = notification.getStringList(BAG_VISIBILITY, PARAM_GROUPS);
|
||||
return groups.stream().anyMatch(r -> tx().getCertificate().hasGroup(r));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package li.strolch.policy.notifications;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.policy.PolicyDef;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.policy.StrolchPolicy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.POLICY_DEFAULT;
|
||||
import static li.strolch.model.policy.PolicyDef.getJavaPolicy;
|
||||
import static li.strolch.model.policy.PolicyDef.getKeyPolicy;
|
||||
|
||||
public abstract class NotificationsPolicy extends StrolchPolicy {
|
||||
|
||||
public NotificationsPolicy(StrolchTransaction tx) {
|
||||
super(tx);
|
||||
}
|
||||
|
||||
public abstract List<Resource> findUserNotifications();
|
||||
public abstract boolean canView(Resource notification);
|
||||
|
||||
public static NotificationsPolicy getDefaultPolicy(StrolchTransaction tx) {
|
||||
PolicyDef defaultDef = getKeyPolicy(NotificationsPolicy.class, POLICY_DEFAULT);
|
||||
PolicyDef fallbackDef = getJavaPolicy(NotificationsPolicy.class, DefaultNotificationsPolicy.class);
|
||||
return tx.getPolicy(NotificationsPolicy.class, defaultDef, fallbackDef);
|
||||
}
|
||||
}
|
|
@ -96,5 +96,8 @@ public class StrolchConstants extends StrolchModelConstants {
|
|||
public static final String PRIVILEGE_UPDATE_PREFIX = "Update";
|
||||
public static final String PRIVILEGE_REMOVE_PREFIX = "Remove";
|
||||
public static final String PRIVILEGE_GET_PREFIX = "Get";
|
||||
public static final String PRIVILEGE_GET_NOTIFICATIONS = "GetNotifications";
|
||||
public static final String PRIVILEGE_GET_NOTIFICATION = "GetNotification";
|
||||
public static final String PRIVILEGE_GET_NOTIFICATIONS_ALL = "GetNotificationsAll";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,19 +15,6 @@
|
|||
*/
|
||||
package li.strolch.runtime.privilege;
|
||||
|
||||
import static java.lang.Boolean.parseBoolean;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS_PATH;
|
||||
import static li.strolch.privilege.helper.XmlConstants.PARAM_BASE_PATH;
|
||||
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.agent.api.ComponentContainer;
|
||||
import li.strolch.agent.api.StrolchComponent;
|
||||
import li.strolch.agent.api.StrolchRealm;
|
||||
|
@ -48,6 +35,21 @@ import li.strolch.runtime.configuration.ComponentConfiguration;
|
|||
import li.strolch.runtime.configuration.RuntimeConfiguration;
|
||||
import li.strolch.utils.helper.XmlHelper;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.Boolean.parseBoolean;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static li.strolch.persistence.api.TransactionThreadLocal.getTx;
|
||||
import static li.strolch.persistence.api.TransactionThreadLocal.hasTx;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS;
|
||||
import static li.strolch.privilege.handler.PrivilegeHandler.PARAM_PERSIST_SESSIONS_PATH;
|
||||
import static li.strolch.privilege.helper.XmlConstants.PARAM_BASE_PATH;
|
||||
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*;
|
||||
|
||||
public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements PrivilegeHandler {
|
||||
|
||||
public static final String PROP_PRIVILEGE_CONFIG_FILE = "privilegeConfigFile";
|
||||
|
@ -89,8 +91,7 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
|
|||
/**
|
||||
* Initializes the {@link DefaultPrivilegeHandler} from the configuration file
|
||||
*
|
||||
* @param privilegeXmlFile
|
||||
* a {@link File} reference to the XML file containing the configuration for Privilege
|
||||
* @param privilegeXmlFile a {@link File} reference to the XML file containing the configuration for Privilege
|
||||
*
|
||||
* @return the initialized {@link PrivilegeHandler} where the {@link EncryptionHandler} and
|
||||
* {@link PersistenceHandler} are set and initialized as well
|
||||
|
@ -185,13 +186,17 @@ public class DefaultStrolchPrivilegeHandler extends StrolchComponent implements
|
|||
|
||||
private void writeAudit(Certificate certificate, String login, AccessType accessType, String username) {
|
||||
StrolchRealm realm = getContainer().getRealm(certificate);
|
||||
try (StrolchTransaction tx = realm.openTx(certificate, login, false).silentThreshold(1, NANOSECONDS)) {
|
||||
try (StrolchTransaction tx = hasTx() ? getTx() : openTx(certificate, login, realm)) {
|
||||
tx.setSuppressAudits(true);
|
||||
Audit audit = tx.auditFrom(accessType, PRIVILEGE, CERTIFICATE, username);
|
||||
tx.getAuditTrail().add(tx, audit);
|
||||
}
|
||||
}
|
||||
|
||||
private static StrolchTransaction openTx(Certificate certificate, String login, StrolchRealm realm) {
|
||||
return realm.openTx(certificate, login, false).silentThreshold(1, NANOSECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivilegeContext validate(Certificate certificate) throws PrivilegeException {
|
||||
return this.privilegeHandler.validate(certificate);
|
||||
|
|
|
@ -302,7 +302,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
|
|||
return certificate;
|
||||
}
|
||||
|
||||
private void checkSessionsForTimeout() {
|
||||
protected void checkSessionsForTimeout() {
|
||||
ZonedDateTime maxKeepAliveTime = ZonedDateTime.now().minusMinutes(this.maxKeepAliveMinutes);
|
||||
ZonedDateTime timeOutTime = ZonedDateTime.now().minusMinutes(this.sessionTtlMinutes);
|
||||
|
||||
|
@ -326,7 +326,7 @@ public class DefaultStrolchSessionHandler extends StrolchComponent implements St
|
|||
}
|
||||
}
|
||||
|
||||
private void sessionTimeout(Certificate certificate) {
|
||||
protected void sessionTimeout(Certificate certificate) {
|
||||
DBC.PRE.assertNotNull("Certificate must be given!", certificate);
|
||||
|
||||
Certificate removedCert = this.certificateMap.remove(certificate.getAuthToken());
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package li.strolch.search;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ORDER;
|
||||
|
||||
import li.strolch.model.Order;
|
||||
import li.strolch.model.activity.Activity;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ORDER;
|
||||
|
||||
/**
|
||||
* Performs a search for {@link Activity} elements
|
||||
*/
|
||||
|
@ -20,7 +22,13 @@ public class ActivitySearch extends StrolchSearch<Activity> {
|
|||
|
||||
@Override
|
||||
public ActivitySearch types(String... types) {
|
||||
this.navigator = tx -> tx.streamActivities(types);
|
||||
this.navigator = tx -> {
|
||||
Stream<Activity> cachedStream = tx.streamCachedActivities(types);
|
||||
Stream<Activity> stream = tx
|
||||
.streamActivities(types)
|
||||
.filter(e -> !tx.isActivityCached(e.getType(), e.getId()));
|
||||
return Stream.concat(cachedStream, stream);
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -26,14 +26,12 @@ public class StrolchAccessDeniedException extends StrolchException {
|
|||
|
||||
private final Certificate certificate;
|
||||
private final Restrictable restrictable;
|
||||
private final I18nMessage i18n;
|
||||
|
||||
public StrolchAccessDeniedException(Certificate certificate, Restrictable restrictable, I18nMessage i18n,
|
||||
Throwable cause) {
|
||||
super(i18n.getMessage(), cause);
|
||||
super(i18n, cause);
|
||||
this.certificate = certificate;
|
||||
this.restrictable = restrictable;
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
public Certificate getCertificate() {
|
||||
|
@ -43,8 +41,4 @@ public class StrolchAccessDeniedException extends StrolchException {
|
|||
public Restrictable getRestrictable() {
|
||||
return restrictable;
|
||||
}
|
||||
|
||||
public I18nMessage getI18n() {
|
||||
return this.i18n;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,16 +15,17 @@
|
|||
*/
|
||||
package li.strolch.exception;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import li.strolch.utils.I18nMessage;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
public class StrolchException extends RuntimeException {
|
||||
|
||||
private I18nMessage i18n;
|
||||
protected I18nMessage i18n;
|
||||
|
||||
public StrolchException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
|
@ -36,6 +37,41 @@ public class StrolchException extends RuntimeException {
|
|||
|
||||
public StrolchException(I18nMessage i18n) {
|
||||
super(i18n.getMessage(Locale.getDefault()));
|
||||
this.i18n = i18n;
|
||||
if (i18n.hasException())
|
||||
initCause(i18n.getException());
|
||||
}
|
||||
|
||||
public StrolchException(I18nMessage i18n, Throwable cause) {
|
||||
super(i18n.getMessage(Locale.getDefault()), cause);
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key) {
|
||||
this(new I18nMessage(bundle, key));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop, Object value) {
|
||||
this(new I18nMessage(bundle, key).value(prop, value));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2) {
|
||||
this(new I18nMessage(bundle, key).value(prop1, value1).value(prop2, value2));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2, Object value2,
|
||||
String prop3, Object value3) {
|
||||
this(new I18nMessage(bundle, key).value(prop1, value1).value(prop2, value2).value(prop3, value3));
|
||||
}
|
||||
|
||||
public StrolchException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2, Object value2,
|
||||
String prop3, Object value3, String prop4, Object value4) {
|
||||
this(new I18nMessage(bundle, key)
|
||||
.value(prop1, value1)
|
||||
.value(prop2, value2)
|
||||
.value(prop3, value3)
|
||||
.value(prop4, value4));
|
||||
}
|
||||
|
||||
public boolean hasI18n() {
|
||||
|
@ -54,4 +90,9 @@ public class StrolchException extends RuntimeException {
|
|||
this.i18n = i18n;
|
||||
return this;
|
||||
}
|
||||
|
||||
public StrolchException cause(Throwable cause) {
|
||||
initCause(cause);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +1,40 @@
|
|||
package li.strolch.exception;
|
||||
|
||||
import li.strolch.utils.I18nMessage;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import li.strolch.utils.I18nMessage;
|
||||
|
||||
public class StrolchUserMessageException extends StrolchException {
|
||||
|
||||
private I18nMessage i18n;
|
||||
|
||||
public StrolchUserMessageException(I18nMessage i18n) {
|
||||
super(i18n.getMessage(Locale.getDefault()));
|
||||
this.i18n = i18n;
|
||||
if (i18n.hasException())
|
||||
initCause(i18n.getException());
|
||||
super(i18n);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(I18nMessage i18n, Throwable cause) {
|
||||
super(i18n.getMessage(Locale.getDefault()), cause);
|
||||
this.i18n = i18n;
|
||||
super(i18n, cause);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key) {
|
||||
this(new I18nMessage(bundle, key));
|
||||
super(bundle, key);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop, Object value) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop, value));
|
||||
super(bundle, key, prop, value);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop1, value1) //
|
||||
.value(prop2, value2));
|
||||
super(bundle, key, prop1, value1, prop2, value2);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2, String prop3, Object value3) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop1, value1) //
|
||||
.value(prop2, value2) //
|
||||
.value(prop3, value3));
|
||||
super(bundle, key, prop1, value1, prop2, value2, prop3, value3);
|
||||
}
|
||||
|
||||
public StrolchUserMessageException(ResourceBundle bundle, String key, String prop1, Object value1, String prop2,
|
||||
Object value2, String prop3, Object value3, String prop4, Object value4) {
|
||||
this(new I18nMessage(bundle, key) //
|
||||
.value(prop1, value1) //
|
||||
.value(prop2, value2) //
|
||||
.value(prop3, value3) //
|
||||
.value(prop4, value4));
|
||||
}
|
||||
|
||||
public StrolchUserMessageException cause(Throwable cause) {
|
||||
initCause(cause);
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasI18n() {
|
||||
return this.i18n != null;
|
||||
}
|
||||
|
||||
public I18nMessage getI18n() {
|
||||
return this.i18n;
|
||||
}
|
||||
|
||||
public void setI18n(I18nMessage i18n) {
|
||||
this.i18n = i18n;
|
||||
}
|
||||
|
||||
public StrolchUserMessageException i18n(I18nMessage i18n) {
|
||||
this.i18n = i18n;
|
||||
return this;
|
||||
super(bundle, key, prop1, value1, prop2, value2, prop3, value3, prop4, value4);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,10 +42,8 @@ public abstract class AbstractStrolchElement implements StrolchElement {
|
|||
/**
|
||||
* Default constructor
|
||||
*
|
||||
* @param id
|
||||
* id of this {@link StrolchElement}
|
||||
* @param name
|
||||
* name of this {@link StrolchElement}
|
||||
* @param id id of this {@link StrolchElement}
|
||||
* @param name name of this {@link StrolchElement}
|
||||
*/
|
||||
public AbstractStrolchElement(String id, String name) {
|
||||
setId(id);
|
||||
|
@ -98,16 +96,15 @@ public abstract class AbstractStrolchElement implements StrolchElement {
|
|||
* Used to build a {@link Locator} for this {@link StrolchElement}. It must be implemented by the concrete
|
||||
* implemented as parents must first add their {@link Locator} information
|
||||
*
|
||||
* @param locatorBuilder
|
||||
* the {@link LocatorBuilder} to which the {@link StrolchElement} must add its locator information
|
||||
* @param locatorBuilder the {@link LocatorBuilder} to which the {@link StrolchElement} must add its locator
|
||||
* information
|
||||
*/
|
||||
protected abstract void fillLocator(LocatorBuilder locatorBuilder);
|
||||
|
||||
/**
|
||||
* fills the {@link StrolchElement} clone with the id, name and type
|
||||
*
|
||||
* @param clone
|
||||
* the clone to fill
|
||||
* @param clone the clone to fill
|
||||
*/
|
||||
protected void fillClone(AbstractStrolchElement clone) {
|
||||
clone.id = this.id;
|
||||
|
@ -139,5 +136,7 @@ public abstract class AbstractStrolchElement implements StrolchElement {
|
|||
public abstract int hashCode();
|
||||
|
||||
@Override
|
||||
public abstract String toString();
|
||||
public String toString() {
|
||||
return getLocator().toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import li.strolch.model.policy.PolicyDefs;
|
|||
import li.strolch.model.visitor.StrolchElementVisitor;
|
||||
import li.strolch.model.xml.StrolchXmlHelper;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.iso8601.ISO8601;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.time.*;
|
||||
|
@ -331,9 +330,9 @@ public class Order extends AbstractStrolchRootElement implements StrolchRootElem
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "Order [id=" + this.id + ", name=" + this.name + ", type=" + this.type + ", state=" + this.state +
|
||||
", date=" + ISO8601.toString(this.date) + ", version=" + this.version + "]";
|
||||
if (this.version == null)
|
||||
return getLocator().toString();
|
||||
return getLocator() + ", Version: " + this.version.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -191,7 +191,9 @@ public class Resource extends AbstractStrolchRootElement implements StrolchRootE
|
|||
if (this.timedStateMap == null || this.timedStateMap.isEmpty())
|
||||
return Stream.empty();
|
||||
|
||||
return this.timedStateMap.values().stream()
|
||||
return this.timedStateMap
|
||||
.values()
|
||||
.stream()
|
||||
.filter(s -> s.getInterpretation().equals(interpretation) && s.getUom().equals(uom));
|
||||
}
|
||||
|
||||
|
@ -399,8 +401,9 @@ public class Resource extends AbstractStrolchRootElement implements StrolchRootE
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return "Resource [id=" + this.id + ", name=" + this.name + ", type=" + this.type + ", version=" + this.version;
|
||||
if (this.version == null)
|
||||
return getLocator().toString();
|
||||
return getLocator() + ", Version: " + this.version.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -72,6 +72,7 @@ public class StrolchModelConstants {
|
|||
|
||||
public static final String BAG_RELATIONS = "relations";
|
||||
public static final String BAG_PARAMETERS = "parameters";
|
||||
public static final String BAG_VISIBILITY = "visibility";
|
||||
public static final String TYPE_PARAMETERS = "Parameters";
|
||||
public static final String TYPE_VERSION = "Version";
|
||||
public static final String TYPE_MEMORY = "Memory";
|
||||
|
@ -82,6 +83,10 @@ public class StrolchModelConstants {
|
|||
public static final String TYPE_CONFIGURATION = "Configuration";
|
||||
public static final String TYPE_OBJECTIVES = "Objectives";
|
||||
public static final String TYPE_METRIC = "Metric";
|
||||
public static final String TYPE_NOTIFICATION = "Notification";
|
||||
public static final String TYPE_VISIBILITY = "Visibility";
|
||||
public static final String TYPE_TEXT = "Text";
|
||||
public static final String TYPE_LOCATION = "Location";
|
||||
|
||||
public static final String RES_CONFIGURATION = "configuration";
|
||||
|
||||
|
@ -99,6 +104,17 @@ public class StrolchModelConstants {
|
|||
public static final String PARAM_START_DATE = "startDate";
|
||||
public static final String PARAM_MODE = "mode";
|
||||
public static final String PARAM_GROUP = "group";
|
||||
public static final String PARAM_ENABLED = "enabled";
|
||||
public static final String PARAM_VISIBLE_FROM = "visibleFrom";
|
||||
public static final String PARAM_VISIBLE_TO = "visibleTo";
|
||||
public static final String PARAM_TITLE = "title";
|
||||
public static final String PARAM_TEXT = "text";
|
||||
public static final String PARAM_FOR_ALL = "forAll";
|
||||
public static final String PARAM_ROLES = "roles";
|
||||
public static final String PARAM_LOCATIONS = "locations";
|
||||
public static final String PARAM_LOCATION_NAMES = "locationNames";
|
||||
public static final String PARAM_LANGUAGES = "languages";
|
||||
public static final String PARAM_GROUPS = "groups";
|
||||
|
||||
public static class PolicyConstants {
|
||||
public static final String POLICY_DEFAULT = "Default";
|
||||
|
|
|
@ -16,15 +16,6 @@
|
|||
|
||||
package li.strolch.model.activity;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_RELATIONS;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
|
||||
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
import li.strolch.exception.StrolchModelException;
|
||||
import li.strolch.exception.StrolchPolicyException;
|
||||
import li.strolch.model.*;
|
||||
|
@ -37,6 +28,15 @@ import li.strolch.model.timevalue.IValueChange;
|
|||
import li.strolch.model.visitor.StrolchElementVisitor;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_PARAMETERS;
|
||||
import static li.strolch.model.StrolchModelConstants.BAG_RELATIONS;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
|
||||
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;
|
||||
|
||||
/**
|
||||
* An {@link Action} represents a single step within an {@link Activity}, that is, one that is not further decomposed
|
||||
* within the {@link Activity}. A {@link Activity} applies {@link IValueChange} objects at the start and end time of the
|
||||
|
@ -107,8 +107,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resourceId
|
||||
* the id of the {@link Resource} the {@link Action} acts on
|
||||
* @param resourceId the id of the {@link Resource} the {@link Action} acts on
|
||||
*/
|
||||
public void setResourceId(String resourceId) {
|
||||
assertNotReadonly();
|
||||
|
@ -124,8 +123,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param state
|
||||
* the target {@code State} of the a {@code Action}
|
||||
* @param state the target {@code State} of the a {@code Action}
|
||||
*/
|
||||
public void setState(State state) {
|
||||
assertNotReadonly();
|
||||
|
@ -140,8 +138,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param resourceType
|
||||
* the resource type
|
||||
* @param resourceType the resource type
|
||||
*/
|
||||
public void setResourceType(String resourceType) {
|
||||
assertNotReadonly();
|
||||
|
@ -151,8 +148,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
/**
|
||||
* Sets the resource type and id from the given {@link Resource}
|
||||
*
|
||||
* @param resource
|
||||
* the resource from which to get the type and id
|
||||
* @param resource the resource from which to get the type and id
|
||||
*/
|
||||
public void setResource(Resource resource) {
|
||||
assertNotReadonly();
|
||||
|
@ -182,8 +178,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
*
|
||||
* @return the {@link Locator} for the {@link Resource} for this action
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if the resource is not defined
|
||||
* @throws IllegalStateException if the resource is not defined
|
||||
*/
|
||||
public Locator getResourceLocator() {
|
||||
if (!isResourceDefined())
|
||||
|
@ -201,8 +196,7 @@ public class Action extends GroupedParameterizedElement implements IActivityElem
|
|||
}
|
||||
|
||||
/**
|
||||
* @param change
|
||||
* {@code IValueChange} to be applied to the {@code Resource}
|
||||
* @param change {@code IValueChange} to be applied to the {@code Resource}
|
||||
*
|
||||
* @return <tt>true</tt> (as specified by {@link Collection#add})
|
||||
*/
|
||||
|
@ -383,8 +377,7 @@ 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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import li.strolch.model.PolicyContainer;
|
|||
import li.strolch.model.activity.Action;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public class ActionBuilder extends PolicyContainerBuilder<ActionBuilder> implements ActivityElementBuilder {
|
||||
|
||||
private final ActivityBuilder builder;
|
||||
|
@ -11,11 +13,19 @@ public class ActionBuilder extends PolicyContainerBuilder<ActionBuilder> impleme
|
|||
private String resourceId;
|
||||
private String resourceType;
|
||||
|
||||
public ActionBuilder(String id, String type) {
|
||||
this(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ActionBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
}
|
||||
|
||||
public ActionBuilder(ActivityBuilder builder, String id, String type) {
|
||||
this(builder, id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ActionBuilder(ActivityBuilder builder, String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = builder;
|
||||
|
|
|
@ -7,6 +7,8 @@ import li.strolch.model.activity.Activity;
|
|||
import li.strolch.model.activity.TimeOrdering;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.*;
|
||||
|
||||
public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> implements ActivityElementBuilder {
|
||||
|
||||
private final StrolchElementBuilder builder;
|
||||
|
@ -15,6 +17,10 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
|
||||
private final List<ActivityElementBuilder> builders;
|
||||
|
||||
public ActivityBuilder(String id, String type, TimeOrdering timeOrdering) {
|
||||
this(id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder(String id, String name, String type, TimeOrdering timeOrdering) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
|
@ -23,6 +29,10 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
this.builders = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, String id, String type, TimeOrdering timeOrdering) {
|
||||
this(builder, id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, String id, String name, String type,
|
||||
TimeOrdering timeOrdering) {
|
||||
super(id, name, type);
|
||||
|
@ -32,6 +42,11 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
this.builders = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, ActivityBuilder parentBuilder, String id, String type,
|
||||
TimeOrdering timeOrdering) {
|
||||
this(builder, parentBuilder, id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder(StrolchElementBuilder builder, ActivityBuilder parentBuilder, String id, String name,
|
||||
String type, TimeOrdering timeOrdering) {
|
||||
super(id, name, type);
|
||||
|
@ -41,12 +56,20 @@ public class ActivityBuilder extends RootElementBuilder<ActivityBuilder> impleme
|
|||
this.builders = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ActivityBuilder subActivity(String id, String type, TimeOrdering timeOrdering) {
|
||||
return subActivity(id, buildParamName(id), type, timeOrdering);
|
||||
}
|
||||
|
||||
public ActivityBuilder subActivity(String id, String name, String type, TimeOrdering timeOrdering) {
|
||||
ActivityBuilder builder = new ActivityBuilder(this.builder, this, id, name, type, timeOrdering);
|
||||
this.builders.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public ActionBuilder action(String id, String type) {
|
||||
return action(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ActionBuilder action(String id, String name, String type) {
|
||||
ActionBuilder builder = new ActionBuilder(this, id, name, type);
|
||||
this.builders.add(builder);
|
||||
|
|
|
@ -7,6 +7,8 @@ import li.strolch.model.ParameterBag;
|
|||
import li.strolch.model.ParameterBagContainer;
|
||||
import li.strolch.model.builder.params.*;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public class BagBuilder<T extends ParameterBagContainerBuilder<T>> {
|
||||
|
||||
private final T builder;
|
||||
|
@ -15,6 +17,10 @@ public class BagBuilder<T extends ParameterBagContainerBuilder<T>> {
|
|||
private final String type;
|
||||
private final List<ParameterBuilder<?, ?, ?>> parameters;
|
||||
|
||||
public BagBuilder(T builder, String id, String type) {
|
||||
this(builder, id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public BagBuilder(T builder, String id, String name, String type) {
|
||||
this.builder = builder;
|
||||
this.id = id;
|
||||
|
@ -23,72 +29,120 @@ public class BagBuilder<T extends ParameterBagContainerBuilder<T>> {
|
|||
this.parameters = new ArrayList<>();
|
||||
}
|
||||
|
||||
public StringParamBuilder<T> string(String id) {
|
||||
return string(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public StringParamBuilder<T> string(String id, String name) {
|
||||
StringParamBuilder<T> builder = new StringParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public TextParamBuilder<T> text(String id) {
|
||||
return text(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public TextParamBuilder<T> text(String id, String name) {
|
||||
TextParamBuilder<T> builder = new TextParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public BooleanParamBuilder<T> booleanB(String id) {
|
||||
return booleanB(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public BooleanParamBuilder<T> booleanB(String id, String name) {
|
||||
BooleanParamBuilder<T> builder = new BooleanParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerParamBuilder<T> integer(String id) {
|
||||
return integer(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerParamBuilder<T> integer(String id, String name) {
|
||||
IntegerParamBuilder<T> builder = new IntegerParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public LongParamBuilder<T> longB(String id) {
|
||||
return longB(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public LongParamBuilder<T> longB(String id, String name) {
|
||||
LongParamBuilder<T> builder = new LongParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatParamBuilder<T> floatB(String id) {
|
||||
return floatB(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatParamBuilder<T> floatB(String id, String name) {
|
||||
FloatParamBuilder<T> builder = new FloatParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public DateParamBuilder<T> date(String id) {
|
||||
return date(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public DateParamBuilder<T> date(String id, String name) {
|
||||
DateParamBuilder<T> builder = new DateParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public DurationParamBuilder<T> duration(String id) {
|
||||
return duration(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public DurationParamBuilder<T> duration(String id, String name) {
|
||||
DurationParamBuilder<T> builder = new DurationParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public StringListParamBuilder<T> stringList(String id) {
|
||||
return stringList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public StringListParamBuilder<T> stringList(String id, String name) {
|
||||
StringListParamBuilder<T> builder = new StringListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerListParamBuilder<T> integerList(String id) {
|
||||
return integerList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerListParamBuilder<T> integerList(String id, String name) {
|
||||
IntegerListParamBuilder<T> builder = new IntegerListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public LongListParamBuilder<T> longList(String id) {
|
||||
return longList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public LongListParamBuilder<T> longList(String id, String name) {
|
||||
LongListParamBuilder<T> builder = new LongListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatListParamBuilder<T> floatList(String id) {
|
||||
return floatList(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatListParamBuilder<T> floatList(String id, String name) {
|
||||
FloatListParamBuilder<T> builder = new FloatListParamBuilder<>(this, id, name);
|
||||
this.parameters.add(builder);
|
||||
|
|
|
@ -5,15 +5,25 @@ import li.strolch.model.State;
|
|||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.iso8601.ISO8601;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.*;
|
||||
|
||||
public class OrderBuilder extends RootElementBuilder<OrderBuilder> {
|
||||
|
||||
private final StrolchElementBuilder builder;
|
||||
|
||||
public OrderBuilder(String id, String type) {
|
||||
this(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public OrderBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
}
|
||||
|
||||
public OrderBuilder(StrolchElementBuilder builder, String id, String type) {
|
||||
this(builder, id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public OrderBuilder(StrolchElementBuilder builder, String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = builder;
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
package li.strolch.model.builder;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamId;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import li.strolch.model.ParameterBag;
|
||||
import li.strolch.model.ParameterBagContainer;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.model.parameter.StringListParameter;
|
||||
import li.strolch.model.parameter.StringParameter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.model.StrolchModelConstants.PolicyConstants.BAG_OBJECTIVES;
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamId;
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public abstract class ParameterBagContainerBuilder<T extends ParameterBagContainerBuilder<T>> {
|
||||
|
||||
private final String id;
|
||||
|
@ -24,6 +25,10 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
private final Map<String, String[]> singleRelations;
|
||||
private final Map<String, String[]> multiRelations;
|
||||
|
||||
public ParameterBagContainerBuilder(String id, String type) {
|
||||
this(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public ParameterBagContainerBuilder(String id, String name, String type) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
|
@ -58,9 +63,12 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return bag(BAG_RELATIONS, TYPE_RELATIONS, TYPE_RELATIONS);
|
||||
}
|
||||
|
||||
public BagBuilder<T> bag(String id, String type) {
|
||||
return bag(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public BagBuilder<T> bag(String id, String name, String type) {
|
||||
@SuppressWarnings("unchecked")
|
||||
BagBuilder<T> bagBuilder = new BagBuilder<>((T) this, id, name, type);
|
||||
@SuppressWarnings("unchecked") BagBuilder<T> bagBuilder = new BagBuilder<>((T) this, id, name, type);
|
||||
if (this.parametersBags.put(id, bagBuilder) != null)
|
||||
throw new IllegalArgumentException("Bag builder for " + id + " already exists!");
|
||||
return bagBuilder;
|
||||
|
@ -70,11 +78,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return resourceRelation(buildParamId(type), type, type);
|
||||
}
|
||||
|
||||
public T resourceRelation(String paramId, String type) {
|
||||
return resourceRelation(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T resourceRelation(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.singleRelations.put(paramId, new String[] { paramName, type, Tags.RESOURCE });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.singleRelations.put(paramId, new String[]{paramName, type, Tags.RESOURCE});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -82,11 +93,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return resourceRelations(buildParamId(type), type + "s", type);
|
||||
}
|
||||
|
||||
public T resourceRelations(String paramId, String type) {
|
||||
return resourceRelations(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T resourceRelations(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.multiRelations.put(paramId, new String[] { paramName, type, Tags.RESOURCE });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.multiRelations.put(paramId, new String[]{paramName, type, Tags.RESOURCE});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -94,11 +108,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return orderRelation(buildParamId(type), type, type);
|
||||
}
|
||||
|
||||
public T orderRelation(String paramId, String type) {
|
||||
return orderRelation(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T orderRelation(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.singleRelations.put(paramId, new String[] { paramName, type, Tags.ORDER });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.singleRelations.put(paramId, new String[]{paramName, type, Tags.ORDER});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -106,11 +123,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return orderRelations(buildParamId(type), type + "s", type);
|
||||
}
|
||||
|
||||
public T orderRelations(String paramId, String type) {
|
||||
return orderRelations(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T orderRelations(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.multiRelations.put(paramId, new String[] { paramName, type, Tags.ORDER });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.multiRelations.put(paramId, new String[]{paramName, type, Tags.ORDER});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -118,11 +138,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return activityRelation(buildParamId(type), type, type);
|
||||
}
|
||||
|
||||
public T activityRelation(String paramId, String type) {
|
||||
return activityRelation(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T activityRelation(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.singleRelations.put(paramId, new String[] { paramName, type, Tags.ACTIVITY });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.singleRelations.put(paramId, new String[]{paramName, type, Tags.ACTIVITY});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -130,11 +153,14 @@ public abstract class ParameterBagContainerBuilder<T extends ParameterBagContain
|
|||
return activityRelations(buildParamId(type), type + "s", type);
|
||||
}
|
||||
|
||||
public T activityRelations(String paramId, String type) {
|
||||
return activityRelations(paramId, buildParamName(paramId), type);
|
||||
}
|
||||
|
||||
public T activityRelations(String paramId, String paramName, String type) {
|
||||
assertNotMapped(paramId);
|
||||
this.multiRelations.put(paramId, new String[] { paramName, type, Tags.ACTIVITY });
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
this.multiRelations.put(paramId, new String[]{paramName, type, Tags.ACTIVITY});
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
return t;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,18 +3,23 @@ package li.strolch.model.builder;
|
|||
import li.strolch.model.ParameterBagContainer;
|
||||
import li.strolch.model.PolicyContainer;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public class PolicyContainerBuilder<T extends ParameterBagContainerBuilder<T>> extends ParameterBagContainerBuilder<T> {
|
||||
|
||||
private PoliciesBuilder<T> policies;
|
||||
|
||||
public PolicyContainerBuilder(String id, String type) {
|
||||
super(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public PolicyContainerBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
}
|
||||
|
||||
public PoliciesBuilder<T> policies() {
|
||||
if (this.policies == null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T t = (T) this;
|
||||
@SuppressWarnings("unchecked") T t = (T) this;
|
||||
this.policies = new PoliciesBuilder<>(t);
|
||||
}
|
||||
return policies;
|
||||
|
|
|
@ -1,65 +1,104 @@
|
|||
package li.strolch.model.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.builder.states.*;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
import static li.strolch.utils.helper.StringHelper.getUniqueId;
|
||||
|
||||
public class ResourceBuilder extends RootElementBuilder<ResourceBuilder> {
|
||||
|
||||
private final StrolchElementBuilder builder;
|
||||
private final List<TimedStateBuilder<?>> timedStates;
|
||||
|
||||
public ResourceBuilder(String name, String type) {
|
||||
this(getUniqueId(), name, type);
|
||||
}
|
||||
|
||||
public ResourceBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = null;
|
||||
this.timedStates = new ArrayList<>();
|
||||
}
|
||||
|
||||
public ResourceBuilder(StrolchElementBuilder builder, String name, String type) {
|
||||
this(builder, getUniqueId(), name, type);
|
||||
}
|
||||
|
||||
public ResourceBuilder(StrolchElementBuilder builder, String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
this.builder = builder;
|
||||
this.timedStates = new ArrayList<>();
|
||||
}
|
||||
|
||||
public BooleanStateBuilder booleanState(String id) {
|
||||
return booleanState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public BooleanStateBuilder booleanState(String id, String name) {
|
||||
BooleanStateBuilder builder = new BooleanStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatStateBuilder floatState(String id) {
|
||||
return floatState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatStateBuilder floatState(String id, String name) {
|
||||
FloatStateBuilder builder = new FloatStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerStateBuilder integerState(String id) {
|
||||
return integerState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerStateBuilder integerState(String id, String name) {
|
||||
IntegerStateBuilder builder = new IntegerStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public LongStateBuilder longState(String id) {
|
||||
return longState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public LongStateBuilder longState(String id, String name) {
|
||||
LongStateBuilder builder = new LongStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public FloatListStateBuilder floatListState(String id) {
|
||||
return floatListState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public FloatListStateBuilder floatListState(String id, String name) {
|
||||
FloatListStateBuilder builder = new FloatListStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public IntegerListStateBuilder integerListState(String id) {
|
||||
return integerListState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public IntegerListStateBuilder integerListState(String id, String name) {
|
||||
IntegerListStateBuilder builder = new IntegerListStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public StringSetStateBuilder stringSetState(String id) {
|
||||
return stringSetState(id, buildParamName(id));
|
||||
}
|
||||
|
||||
public StringSetStateBuilder stringSetState(String id, String name) {
|
||||
StringSetStateBuilder builder = new StringSetStateBuilder(this, id, name);
|
||||
this.timedStates.add(builder);
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
package li.strolch.model.builder;
|
||||
|
||||
import li.strolch.model.PolicyContainer;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
|
||||
import static li.strolch.model.builder.BuilderHelper.buildParamName;
|
||||
|
||||
public abstract class RootElementBuilder<T extends ParameterBagContainerBuilder<T>> extends PolicyContainerBuilder<T> {
|
||||
|
||||
public RootElementBuilder(String id, String type) {
|
||||
super(id, buildParamName(id), type);
|
||||
}
|
||||
|
||||
public RootElementBuilder(String id, String name, String type) {
|
||||
super(id, name, type);
|
||||
}
|
||||
|
|
|
@ -27,19 +27,19 @@ public class StrolchElementBuilder {
|
|||
this.activityBuilders = new HashMap<>();
|
||||
}
|
||||
|
||||
public ResourceBuilder resource(String name, String type) {
|
||||
public ResourceBuilder resourceTemplate(String name, String type) {
|
||||
ResourceBuilder builder = new ResourceBuilder(this, type, name, TEMPLATE);
|
||||
this.resourceBuilders.put(type, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public OrderBuilder order(String name, String type) {
|
||||
public OrderBuilder orderTemplate(String name, String type) {
|
||||
OrderBuilder builder = new OrderBuilder(this, type, name, TEMPLATE);
|
||||
this.orderBuilders.put(type, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public ActivityBuilder activity(String name, String type, TimeOrdering timeOrdering) {
|
||||
public ActivityBuilder activityTemplate(String name, String type, TimeOrdering timeOrdering) {
|
||||
ActivityBuilder builder = new ActivityBuilder(this, type, name, TEMPLATE, timeOrdering);
|
||||
this.activityBuilders.put(type, builder);
|
||||
return builder;
|
||||
|
@ -62,7 +62,7 @@ public class StrolchElementBuilder {
|
|||
|
||||
public List<StrolchRootElement> buildTemplates() {
|
||||
return concat(concat(this.resourceBuilders.values().stream(), //
|
||||
this.orderBuilders.values().stream()), //
|
||||
this.orderBuilders.values().stream()), //
|
||||
this.activityBuilders.values().stream()) //
|
||||
.map(RootElementBuilder::build).collect(toList());
|
||||
}
|
||||
|
@ -71,24 +71,24 @@ public class StrolchElementBuilder {
|
|||
ResourceBuilder builder = this.resourceBuilders.get(type);
|
||||
if (builder == null)
|
||||
throw new IllegalArgumentException("No resource template defined for type " + type);
|
||||
return updateFields(type, newName, builder.build());
|
||||
return setInitialFields(type, newName, builder.build());
|
||||
}
|
||||
|
||||
public Order newOrder(String type, String newName) {
|
||||
OrderBuilder builder = this.orderBuilders.get(type);
|
||||
if (builder == null)
|
||||
throw new IllegalArgumentException("No resource template defined for type " + type);
|
||||
return updateFields(type, newName, builder.build());
|
||||
return setInitialFields(type, newName, builder.build());
|
||||
}
|
||||
|
||||
public Activity newActivity(String type, String newName) {
|
||||
ActivityBuilder builder = this.activityBuilders.get(type);
|
||||
if (builder == null)
|
||||
throw new IllegalArgumentException("No resource template defined for type " + type);
|
||||
return updateFields(type, newName, builder.build());
|
||||
return setInitialFields(type, newName, builder.build());
|
||||
}
|
||||
|
||||
private <T extends StrolchRootElement> T updateFields(String type, String newName, T element) {
|
||||
private <T extends StrolchRootElement> T setInitialFields(String type, String newName, T element) {
|
||||
element.setId(StringHelper.getUniqueId());
|
||||
element.setName(newName);
|
||||
element.setType(type);
|
||||
|
|
|
@ -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() //
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
16
pom.xml
16
pom.xml
|
@ -5,7 +5,7 @@
|
|||
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
|
||||
<name>strolch</name>
|
||||
<description>Module build for strolch</description>
|
||||
|
@ -83,7 +83,7 @@
|
|||
<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>
|
||||
|
@ -91,16 +91,15 @@
|
|||
<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>
|
||||
|
@ -388,6 +387,7 @@
|
|||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<finalName>strolch-${artifactId}</finalName>
|
||||
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ 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;
|
||||
|
@ -292,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);
|
||||
|
@ -355,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();
|
||||
|
@ -444,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
|
||||
|
@ -554,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!.");
|
||||
|
@ -694,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(),
|
||||
|
@ -1075,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()))
|
||||
|
@ -1088,7 +1101,8 @@ public class DefaultPrivilegeHandler implements PrivilegeHandler {
|
|||
replacePrivilegeContextForCert(newUser, ctx.getCertificate());
|
||||
}
|
||||
|
||||
persistSessionsAsync();
|
||||
if (persistSessions)
|
||||
persistSessionsAsync();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -172,8 +172,15 @@ public class PrivilegeCrudHandler {
|
|||
// properties
|
||||
propertySelected = isSelectedByProperty(selPropertyMap, user.getProperties());
|
||||
|
||||
boolean selected = userIdSelected && usernameSelected && firstNameSelected && lastNameSelected &&
|
||||
userStateSelected && localeSelected && groupSelected && roleSelected && propertySelected;
|
||||
boolean selected = userIdSelected
|
||||
&& usernameSelected
|
||||
&& firstNameSelected
|
||||
&& lastNameSelected
|
||||
&& userStateSelected
|
||||
&& localeSelected
|
||||
&& groupSelected
|
||||
&& roleSelected
|
||||
&& propertySelected;
|
||||
|
||||
if (selected)
|
||||
result.add(user.asUserRep());
|
||||
|
@ -376,7 +383,10 @@ public class PrivilegeCrudHandler {
|
|||
|
||||
// delegate to persistence handler
|
||||
toCreate.forEach(this.persistenceHandler::addUser);
|
||||
toUpdate.forEach(this.persistenceHandler::replaceUser);
|
||||
for (User user : toUpdate) {
|
||||
this.persistenceHandler.replaceUser(user);
|
||||
this.privilegeHandler.updateExistingSessionsForUser(user, false);
|
||||
}
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Created " + toCreate.size() + " users");
|
||||
|
@ -530,7 +540,7 @@ public class PrivilegeCrudHandler {
|
|||
|
||||
// delegate to persistence handler
|
||||
this.persistenceHandler.replaceUser(newUser);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
this.privilegeHandler.updateExistingSessionsForUser(newUser, true);
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Replaced user " + newUser.getUsername());
|
||||
|
||||
|
@ -559,9 +569,8 @@ public class PrivilegeCrudHandler {
|
|||
new SimpleRestrictable(DefaultPrivilegeHandler.PRIVILEGE_REMOVE_USER, new Tuple(null, existingUser)));
|
||||
|
||||
// delegate user removal to persistence handler
|
||||
this.privilegeHandler.invalidSessionsFor(existingUser);
|
||||
this.privilegeHandler.invalidateSessionsFor(existingUser);
|
||||
this.persistenceHandler.removeUser(username);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Removed user " + username);
|
||||
|
||||
|
@ -715,7 +724,7 @@ public class PrivilegeCrudHandler {
|
|||
|
||||
// delegate user replacement to persistence handler
|
||||
this.persistenceHandler.replaceUser(newUser);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
this.privilegeHandler.invalidateSessionsFor(newUser);
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Set state of user " + newUser.getUsername() + " to " + state);
|
||||
|
||||
|
@ -789,11 +798,11 @@ public class PrivilegeCrudHandler {
|
|||
this.persistenceHandler.replaceRole(newRole);
|
||||
this.privilegeHandler.persistModelAsync();
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Replaced role " + newRole.getName());
|
||||
|
||||
// update any existing certificates with new role
|
||||
this.privilegeHandler.updateExistingSessionsWithNewRole(newRole);
|
||||
|
||||
DefaultPrivilegeHandler.logger.info("Replaced role " + newRole.getName());
|
||||
|
||||
return newRole.asRoleRep();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package li.strolch.privilege.model;
|
||||
|
||||
public class CertificateThreadLocal extends ThreadLocal<Certificate> {
|
||||
|
||||
private static final CertificateThreadLocal instance = new CertificateThreadLocal();
|
||||
|
||||
public static boolean hasCert() {
|
||||
return instance.get() != null;
|
||||
}
|
||||
|
||||
public static Certificate getCert() {
|
||||
Certificate cert = instance.get();
|
||||
if (cert == null)
|
||||
throw new IllegalStateException("No Cert available on thread " + Thread.currentThread().getName());
|
||||
return cert;
|
||||
}
|
||||
|
||||
public static void setCert(Certificate cert) {
|
||||
if (instance.get() != null)
|
||||
throw new IllegalStateException("THIS THREAD HAS ALREADY HAS A CERT!");
|
||||
instance.set(cert);
|
||||
}
|
||||
|
||||
public static void removeCert() {
|
||||
instance.remove();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,12 +91,12 @@ public class Report implements AutoCloseable {
|
|||
return this.reportPolicy.doReportWithPage(offset, limit);
|
||||
}
|
||||
|
||||
public MapOfSets<String, StrolchRootElement> generateFilterCriteria(int limit) {
|
||||
public MapOfSets<String, JsonObject> generateFilterCriteria(int limit) {
|
||||
return this.reportPolicy.generateFilterCriteria(limit);
|
||||
}
|
||||
|
||||
public Stream<StrolchRootElement> generateFilterCriteria(String type) {
|
||||
return this.reportPolicy.generateFilterCriteria(type);
|
||||
public Stream<JsonObject> generateFilterCriteria(String type, int limit, String query) {
|
||||
return this.reportPolicy.generateFilterCriteria(type,limit, query);
|
||||
}
|
||||
|
||||
public long getCounter() {
|
||||
|
|
|
@ -1,17 +1,5 @@
|
|||
package li.strolch.report.policy;
|
||||
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.report.ReportConstants.*;
|
||||
import static li.strolch.utils.helper.StringHelper.EMPTY;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.*;
|
||||
import li.strolch.model.parameter.AbstractParameter;
|
||||
|
@ -33,6 +21,20 @@ import li.strolch.utils.collections.TypedTuple;
|
|||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.iso8601.ISO8601;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.text.MessageFormat.format;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.report.ReportConstants.*;
|
||||
import static li.strolch.utils.ObjectHelper.*;
|
||||
import static li.strolch.utils.helper.StringHelper.EMPTY;
|
||||
|
||||
/**
|
||||
* A Generic Report defines a report as is described at <a href="https://strolch.li/documentation-reports.html">Strolch
|
||||
* Reports</a>
|
||||
|
@ -88,9 +90,11 @@ 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);
|
||||
|
@ -167,36 +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))) {
|
||||
throw new IllegalArgumentException("Filter "
|
||||
+ filterBag.getLocator()
|
||||
+ " can not have combination of "
|
||||
+ PARAM_FIELD_REF
|
||||
+ " and any of "
|
||||
+ PARAM_FIELD_REF1
|
||||
+ ", "
|
||||
+ PARAM_FIELD_REF2);
|
||||
if (filterBag.hasParameter(PARAM_FIELD_REF) && (
|
||||
filterBag.hasParameter(PARAM_FIELD_REF1) || filterBag.hasParameter(PARAM_FIELD_REF2))) {
|
||||
throw new IllegalArgumentException(
|
||||
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))) {
|
||||
throw new IllegalArgumentException("Filter "
|
||||
+ filterBag.getLocator()
|
||||
+ " is missing the "
|
||||
+ PARAM_FIELD_REF
|
||||
+ " or "
|
||||
+ PARAM_FIELD_REF1
|
||||
+ ", "
|
||||
+ PARAM_FIELD_REF2
|
||||
+ " combination!");
|
||||
throw new IllegalArgumentException(
|
||||
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(
|
||||
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
|
||||
|
@ -379,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);
|
||||
|
@ -458,10 +447,9 @@ public class GenericReport extends ReportPolicy {
|
|||
StringParameter joinParamP = additionalTypeBag.getStringP(PARAM_JOIN_PARAM);
|
||||
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());
|
||||
throw new IllegalStateException(
|
||||
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];
|
||||
|
||||
|
@ -476,19 +464,15 @@ 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());
|
||||
throw new IllegalStateException(
|
||||
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());
|
||||
throw new IllegalStateException(
|
||||
format("Parameter reference ({0}) for {1} not found on {2}", joinWithP.getValue(),
|
||||
joinWithP.getLocator(), joinElement.getLocator()));
|
||||
}
|
||||
|
||||
StringParameter joinP = (StringParameter) refP.get();
|
||||
|
@ -520,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) {
|
||||
|
@ -570,76 +552,29 @@ public class GenericReport extends ReportPolicy {
|
|||
* @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);
|
||||
if (filterCriteriaP == null) {
|
||||
logger.warn("Filter criteria not found for " + type);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
@ -656,7 +591,7 @@ 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
|
||||
|
@ -671,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -705,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)
|
||||
|
@ -765,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)
|
||||
|
@ -799,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
|
||||
|
@ -806,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) {
|
||||
|
@ -893,7 +886,7 @@ 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 " + (
|
||||
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();
|
||||
|
@ -961,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;
|
||||
|
@ -982,20 +975,18 @@ 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());
|
||||
throw new IllegalStateException(
|
||||
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];
|
||||
|
||||
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());
|
||||
throw new IllegalStateException(
|
||||
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];
|
||||
|
@ -1018,22 +1009,18 @@ 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());
|
||||
throw new IllegalStateException(
|
||||
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];
|
||||
|
||||
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());
|
||||
throw new IllegalStateException(
|
||||
format("Parameter reference ({0}) for {1} not found on {2}", paramRef, paramRefP.getLocator(),
|
||||
element.getLocator()));
|
||||
|
||||
return Optional.ofNullable(param);
|
||||
}
|
||||
|
@ -1058,15 +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) {
|
||||
|
@ -1146,38 +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;
|
||||
|
||||
|
@ -1189,4 +1147,33 @@ public class GenericReport extends ReportPolicy {
|
|||
refs.put(joinType, joinElem);
|
||||
return joinElem;
|
||||
}
|
||||
|
||||
private static StringParameter getJoinRelationParam(StrolchRootElement dependency, StringParameter joinP,
|
||||
String joinType, String interpretation) {
|
||||
|
||||
ParameterBag relationsBag = dependency.getParameterBag(BAG_RELATIONS);
|
||||
if (relationsBag == null)
|
||||
throw new IllegalStateException(
|
||||
format("Invalid join definition value: {0} on: {1} as {2} has no ParameterBag {3}",
|
||||
joinP.getValue(), joinP.getLocator(), dependency.getLocator(), BAG_RELATIONS));
|
||||
|
||||
List<Parameter<?>> relationParams = relationsBag
|
||||
.getParametersByInterpretationAndUom(interpretation, joinType)
|
||||
.stream()
|
||||
.filter(p -> p.getValueType() == StrolchValueType.STRING)
|
||||
.toList();
|
||||
|
||||
if (relationParams.isEmpty())
|
||||
throw new IllegalStateException(
|
||||
format("Found no relation parameters with UOM {0} of type {1} on dependency {2}", joinType,
|
||||
StrolchValueType.STRING.getType(), dependency.getLocator()));
|
||||
if (relationParams.size() > 1)
|
||||
throw new IllegalStateException("Found multiple possible relation parameters for UOM "
|
||||
+ joinType
|
||||
+ " on dependency "
|
||||
+ dependency.getLocator());
|
||||
|
||||
Parameter<?> relationParam = relationParams.getFirst();
|
||||
return (StringParameter) relationParam;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,5 @@
|
|||
package li.strolch.report.policy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
|
@ -14,6 +9,11 @@ import li.strolch.report.ReportElement;
|
|||
import li.strolch.utils.collections.DateRange;
|
||||
import li.strolch.utils.collections.MapOfSets;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class ReportPolicy extends StrolchPolicy {
|
||||
|
||||
public ReportPolicy(StrolchTransaction tx) {
|
||||
|
@ -48,9 +48,9 @@ public abstract class ReportPolicy extends StrolchPolicy {
|
|||
|
||||
public abstract Stream<ReportElement> doReportWithPage(int offset, int limit);
|
||||
|
||||
public abstract MapOfSets<String, StrolchRootElement> generateFilterCriteria(int limit);
|
||||
public abstract MapOfSets<String, JsonObject> generateFilterCriteria(int limit);
|
||||
|
||||
public abstract Stream<StrolchRootElement> generateFilterCriteria(String type);
|
||||
public abstract Stream<JsonObject> generateFilterCriteria(String type, int limit, String query);
|
||||
|
||||
public abstract long getCounter();
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package li.strolch.service.notifications;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.agent.api.StrolchAgent;
|
||||
import li.strolch.model.ParameterBag;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.builder.ResourceBuilder;
|
||||
import li.strolch.persistence.api.Operation;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.privilege.model.PrivilegeContext;
|
||||
import li.strolch.privilege.model.SimpleRestrictable;
|
||||
import li.strolch.runtime.configuration.SupportedLanguage;
|
||||
import li.strolch.service.JsonServiceArgument;
|
||||
import li.strolch.service.api.AbstractService;
|
||||
import li.strolch.service.api.ServiceResult;
|
||||
import li.strolch.utils.collections.Tuple;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.privilege.handler.DefaultPrivilegeHandler.PRIVILEGE_GET_ROLE;
|
||||
import static li.strolch.utils.iso8601.ISO8601.parseToZdt;
|
||||
|
||||
public class CreateNotificationService extends AbstractService<JsonServiceArgument, ServiceResult> {
|
||||
@Override
|
||||
protected ServiceResult getResultInstance() {
|
||||
return new ServiceResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonServiceArgument getArgumentInstance() {
|
||||
return new JsonServiceArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServiceResult internalDoService(JsonServiceArgument arg) throws Exception {
|
||||
DBC.PRE.assertNotNull("JsonElement must be set", arg.jsonElement);
|
||||
DBC.PRE.assertNotNull("JsonElement must be a JsonObject", arg.jsonElement.isJsonObject());
|
||||
|
||||
JsonObject jsonObject = arg.jsonElement.getAsJsonObject();
|
||||
|
||||
try (StrolchTransaction tx = openArgOrUserTx(arg)) {
|
||||
Resource notification = buildNotification(tx, jsonObject, getSupportedLanguages(getAgent()));
|
||||
tx.add(notification);
|
||||
tx.commitOnClose();
|
||||
}
|
||||
|
||||
return ServiceResult.success();
|
||||
}
|
||||
|
||||
protected static Resource buildNotification(StrolchTransaction tx, JsonObject jsonObject,
|
||||
Set<String> supportedLanguages) {
|
||||
Resource notification = newNotification();
|
||||
PrivilegeContext ctx = tx.getPrivilegeContext();
|
||||
|
||||
JsonObject visibilityJ = jsonObject.get(BAG_VISIBILITY).getAsJsonObject();
|
||||
ParameterBag visibility = notification.getParameterBag(BAG_VISIBILITY);
|
||||
|
||||
visibility.setBoolean(PARAM_ENABLED,
|
||||
visibilityJ.has(PARAM_ENABLED) && visibilityJ.get(PARAM_ENABLED).getAsBoolean());
|
||||
visibility.setBoolean(PARAM_FOR_ALL,
|
||||
visibilityJ.has(PARAM_FOR_ALL) && visibilityJ.get(PARAM_FOR_ALL).getAsBoolean());
|
||||
if (visibilityJ.has(PARAM_VISIBLE_FROM))
|
||||
visibility.setDate(PARAM_VISIBLE_FROM, parseToZdt(visibilityJ.get(PARAM_VISIBLE_FROM).getAsString()));
|
||||
if (visibilityJ.has(PARAM_VISIBLE_TO))
|
||||
visibility.setDate(PARAM_VISIBLE_TO, parseToZdt(visibilityJ.get(PARAM_VISIBLE_TO).getAsString()));
|
||||
|
||||
if (visibilityJ.has(PARAM_ROLES)) {
|
||||
String rolesJ = visibilityJ.get(PARAM_ROLES).getAsString();
|
||||
visibility.getStringListP(PARAM_ROLES).setValueFromString(rolesJ);
|
||||
for (String role : visibility.getStringList(PARAM_ROLES)) {
|
||||
ctx.validateAction(new SimpleRestrictable(PRIVILEGE_GET_ROLE, new Tuple(null, role)));
|
||||
}
|
||||
}
|
||||
|
||||
if (visibilityJ.has(PARAM_LOCATIONS)) {
|
||||
String locationsJ = visibilityJ.get(PARAM_LOCATIONS).getAsString();
|
||||
visibility.getStringListP(PARAM_LOCATIONS).setValueFromString(locationsJ);
|
||||
for (String locationId : visibility.getStringList(PARAM_LOCATIONS)) {
|
||||
tx.assertHasPrivilege(Operation.GET, tx.getResourceBy(TYPE_LOCATION, locationId, true));
|
||||
}
|
||||
}
|
||||
|
||||
for (String language : supportedLanguages) {
|
||||
if (!jsonObject.has(language))
|
||||
continue;
|
||||
JsonObject languageJ = jsonObject.get(language).getAsJsonObject();
|
||||
String title = languageJ.get(PARAM_TITLE).getAsString();
|
||||
String text = languageJ.get(PARAM_TEXT).getAsString();
|
||||
|
||||
ParameterBag languageBag = new ParameterBag(language, language, TYPE_TEXT);
|
||||
languageBag.setString(PARAM_TITLE, title);
|
||||
languageBag.setString(PARAM_TEXT, text);
|
||||
notification.addParameterBag(languageBag);
|
||||
}
|
||||
return notification;
|
||||
}
|
||||
|
||||
public static Set<String> getSupportedLanguages(StrolchAgent agent) {
|
||||
return agent
|
||||
.getStrolchConfiguration()
|
||||
.getRuntimeConfiguration()
|
||||
.getSupportedLanguages()
|
||||
.stream()
|
||||
.map(SupportedLanguage::locale)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static Resource newNotification() {
|
||||
ResourceBuilder notificationBuilder = new ResourceBuilder(TYPE_NOTIFICATION, TYPE_NOTIFICATION)
|
||||
|
||||
.bag(BAG_VISIBILITY, TYPE_VISIBILITY)
|
||||
|
||||
.booleanB(PARAM_ENABLED).end()
|
||||
|
||||
.date(PARAM_VISIBLE_FROM).end()
|
||||
|
||||
.date(PARAM_VISIBLE_TO).end()
|
||||
|
||||
.booleanB(PARAM_FOR_ALL).end()
|
||||
|
||||
.stringList(PARAM_ROLES).end()
|
||||
|
||||
.stringList(PARAM_LOCATIONS).end()
|
||||
|
||||
.endBag();
|
||||
|
||||
return notificationBuilder.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package li.strolch.service.notifications;
|
||||
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.service.StringArgument;
|
||||
import li.strolch.service.api.AbstractService;
|
||||
import li.strolch.service.api.ServiceResult;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.model.StrolchModelConstants.TYPE_NOTIFICATION;
|
||||
|
||||
public class RemoveNotificationService extends AbstractService<StringArgument, ServiceResult> {
|
||||
@Override
|
||||
protected ServiceResult getResultInstance() {
|
||||
return new ServiceResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringArgument getArgumentInstance() {
|
||||
return new StringArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServiceResult internalDoService(StringArgument arg) throws Exception {
|
||||
DBC.PRE.assertNotEmpty("value must be set", arg.value);
|
||||
|
||||
try (StrolchTransaction tx = openArgOrUserTx(arg)) {
|
||||
Resource notification = tx.getResourceBy(TYPE_NOTIFICATION, arg.value, true);
|
||||
tx.remove(notification);
|
||||
tx.commitOnClose();
|
||||
}
|
||||
|
||||
return ServiceResult.success();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package li.strolch.service.notifications;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.service.JsonServiceArgument;
|
||||
import li.strolch.service.api.AbstractService;
|
||||
import li.strolch.service.api.ServiceResult;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
|
||||
import static li.strolch.service.notifications.CreateNotificationService.buildNotification;
|
||||
import static li.strolch.service.notifications.CreateNotificationService.getSupportedLanguages;
|
||||
|
||||
public class UpdateNotificationService extends AbstractService<JsonServiceArgument, ServiceResult> {
|
||||
@Override
|
||||
protected ServiceResult getResultInstance() {
|
||||
return new ServiceResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonServiceArgument getArgumentInstance() {
|
||||
return new JsonServiceArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServiceResult internalDoService(JsonServiceArgument arg) throws Exception {
|
||||
DBC.PRE.assertNotEmpty("objectId must be set", arg.objectId);
|
||||
DBC.PRE.assertNotNull("JsonElement must be set", arg.jsonElement);
|
||||
DBC.PRE.assertNotNull("JsonElement must be a JsonObject", arg.jsonElement.isJsonObject());
|
||||
|
||||
JsonObject jsonObject = arg.jsonElement.getAsJsonObject();
|
||||
DBC.PRE.assertEquals("arg ID and jsonObject ID must be the same", arg.objectId,
|
||||
jsonObject.get(Tags.Json.ID).getAsString());
|
||||
|
||||
try (StrolchTransaction tx = openArgOrUserTx(arg)) {
|
||||
|
||||
Resource notification = buildNotification(tx, jsonObject, getSupportedLanguages(getAgent()));
|
||||
notification.setId(arg.objectId);
|
||||
|
||||
tx.update(notification);
|
||||
tx.commitOnClose();
|
||||
}
|
||||
|
||||
return ServiceResult.success();
|
||||
}
|
||||
}
|
|
@ -1,18 +1,7 @@
|
|||
package li.strolch.report;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import li.strolch.model.StrolchElement;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.privilege.model.Certificate;
|
||||
import li.strolch.testbase.runtime.RuntimeMock;
|
||||
|
@ -23,6 +12,16 @@ import org.junit.AfterClass;
|
|||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class GenericReportTest {
|
||||
|
||||
private static final String RUNTIME_PATH = "target/GenericReportTest/";
|
||||
|
@ -48,49 +47,49 @@ public class GenericReportTest {
|
|||
public void shouldGenerateJsonReport() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
report.doReportAsJson() //
|
||||
.forEach(e -> {
|
||||
|
||||
switch (e.get("slot").getAsString()) {
|
||||
case "Slot 1" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("20.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 2" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("18.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 3" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("11.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 4" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("16.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
default -> fail("Unhandled result element: \n" + e);
|
||||
case "Slot 1" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("20.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 2" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("18.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("4.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 001", e.get("section").getAsString());
|
||||
assertEquals("Storage 01", e.get("storage").getAsString());
|
||||
assertEquals("Location 01", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 3" -> {
|
||||
assertEquals("Product 01", e.get("product").getAsString());
|
||||
assertEquals("11.0", e.get("quantity").getAsString());
|
||||
assertEquals("40.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
case "Slot 4" -> {
|
||||
assertEquals("Product 02", e.get("product").getAsString());
|
||||
assertEquals("16.0", e.get("quantity").getAsString());
|
||||
assertEquals("20.0", e.get("maxQuantity").getAsString());
|
||||
assertEquals("6.0", e.get("minQuantity").getAsString());
|
||||
assertEquals("Section 002", e.get("section").getAsString());
|
||||
assertEquals("Storage 02", e.get("storage").getAsString());
|
||||
assertEquals("Location 02", e.get("location").getAsString());
|
||||
}
|
||||
default -> fail("Unhandled result element: \n" + e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -100,7 +99,7 @@ public class GenericReportTest {
|
|||
public void shouldFilterReport1() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
report.filter("Product", "product01") //
|
||||
.doReportAsJson() //
|
||||
|
@ -108,12 +107,12 @@ public class GenericReportTest {
|
|||
|
||||
String slotName = e.get("slot").getAsString();
|
||||
switch (slotName) {
|
||||
case "Slot 1":
|
||||
case "Slot 3":
|
||||
break;
|
||||
default:
|
||||
fail("Unexpected slot name " + slotName + ", should have been filtered!");
|
||||
break;
|
||||
case "Slot 1":
|
||||
case "Slot 3":
|
||||
break;
|
||||
default:
|
||||
fail("Unexpected slot name " + slotName + ", should have been filtered!");
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -123,7 +122,7 @@ public class GenericReportTest {
|
|||
public void shouldFilterReport2() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
report.filter("Product", "product01") //
|
||||
.filter("Location", "location02") //
|
||||
|
@ -144,7 +143,7 @@ public class GenericReportTest {
|
|||
Date from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000);
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
@ -158,7 +157,7 @@ public class GenericReportTest {
|
|||
}
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
Date to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
@ -185,7 +184,10 @@ public class GenericReportTest {
|
|||
Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
List<JsonObject> result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
|
||||
List<JsonObject> result = report
|
||||
.filter("Product", "product01")
|
||||
.dateRange(dateRange)
|
||||
.doReportAsJson()
|
||||
.toList();
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
@ -196,7 +198,10 @@ public class GenericReportTest {
|
|||
Date to = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
List<JsonObject> result = report.filter("Product", "product01").dateRange(dateRange).doReportAsJson()
|
||||
List<JsonObject> result = report
|
||||
.filter("Product", "product01")
|
||||
.dateRange(dateRange)
|
||||
.doReportAsJson()
|
||||
.toList();
|
||||
assertEquals(2, result.size());
|
||||
}
|
||||
|
@ -207,8 +212,11 @@ public class GenericReportTest {
|
|||
Date to = new Date(LocalDate.of(2017, 3, 2).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
List<JsonObject> result = report.filter("Product", "product01", "product02").dateRange(dateRange)
|
||||
.doReportAsJson().toList();
|
||||
List<JsonObject> result = report
|
||||
.filter("Product", "product01", "product02")
|
||||
.dateRange(dateRange)
|
||||
.doReportAsJson()
|
||||
.toList();
|
||||
assertEquals(4, result.size());
|
||||
}
|
||||
}
|
||||
|
@ -218,21 +226,27 @@ public class GenericReportTest {
|
|||
public void shouldCreateFilterCriteria() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
MapOfSets<String, StrolchRootElement> filterCriteria = report.generateFilterCriteria(1000);
|
||||
MapOfSets<String, JsonObject> filterCriteria = report.generateFilterCriteria(1000);
|
||||
|
||||
assertFalse(filterCriteria.containsSet("Location"));
|
||||
assertFalse(filterCriteria.containsSet("Slot"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Product").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("product01", "product02"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Storage").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Section").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("section001", "section002"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Product")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("product01", "product02"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Storage")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Section")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("section001", "section002"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,9 +254,10 @@ public class GenericReportTest {
|
|||
public void shouldCreateFilterCriteriaFiltered1() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
MapOfSets<String, StrolchRootElement> filterCriteria = report.filter("Product", "product01")
|
||||
MapOfSets<String, JsonObject> filterCriteria = report
|
||||
.filter("Product", "product01")
|
||||
.generateFilterCriteria(1000);
|
||||
|
||||
// assert sequence of filter criteria is correct
|
||||
|
@ -254,15 +269,21 @@ public class GenericReportTest {
|
|||
|
||||
assertFalse(filterCriteria.containsSet("Location"));
|
||||
assertFalse(filterCriteria.containsSet("Slot"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Product").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("product01"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Storage").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert
|
||||
.assertThat(filterCriteria.getSet("Section").stream().map(StrolchElement::getId).collect(toSet()),
|
||||
containsInAnyOrder("section001", "section002"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Product")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("product01"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Storage")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("storage01", "storage02"));
|
||||
MatcherAssert.assertThat(filterCriteria
|
||||
.getSet("Section")
|
||||
.stream()
|
||||
.map(e -> e.get(Tags.Json.ID).getAsString())
|
||||
.collect(toSet()), containsInAnyOrder("section001", "section002"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,13 +291,13 @@ public class GenericReportTest {
|
|||
public void shouldCreateFilterCriteriaFiltered2() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
Report report = new Report(tx, "stockReport")) {
|
||||
|
||||
Date from = new Date(LocalDate.of(2017, 3, 1).toEpochDay() * 86400000);
|
||||
Date to = new Date(LocalDate.of(2017, 3, 5).toEpochDay() * 86400000);
|
||||
DateRange dateRange = new DateRange().from(from, true).to(to, false);
|
||||
|
||||
MapOfSets<String, StrolchRootElement> filterCriteria = report //
|
||||
MapOfSets<String, JsonObject> filterCriteria = report //
|
||||
.dateRange(dateRange) //
|
||||
.filter("Product", "product01") //
|
||||
.generateFilterCriteria(1000);
|
||||
|
@ -289,26 +310,26 @@ public class GenericReportTest {
|
|||
public void shouldDoAdditionalJoin() {
|
||||
|
||||
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true);
|
||||
Report report = new Report(tx, "slotsByOrderUsageReport")) {
|
||||
Report report = new Report(tx, "slotsByOrderUsageReport")) {
|
||||
|
||||
AtomicInteger slotsFound = new AtomicInteger();
|
||||
report.doReportAsJson().forEach(e -> {
|
||||
|
||||
switch (e.get("slot").getAsString()) {
|
||||
case "Slot 1", "Slot 3" -> {
|
||||
assertEquals("Harry", e.get("firstName").getAsString());
|
||||
assertEquals("Barns", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 2", "Slot 4", "Slot 5" -> {
|
||||
assertEquals("Geoffrey", e.get("firstName").getAsString());
|
||||
assertEquals("Bobcat", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 6" -> {
|
||||
assertEquals("", e.get("firstName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 1", "Slot 3" -> {
|
||||
assertEquals("Harry", e.get("firstName").getAsString());
|
||||
assertEquals("Barns", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 2", "Slot 4", "Slot 5" -> {
|
||||
assertEquals("Geoffrey", e.get("firstName").getAsString());
|
||||
assertEquals("Bobcat", e.get("lastName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
case "Slot 6" -> {
|
||||
assertEquals("", e.get("firstName").getAsString());
|
||||
slotsFound.getAndIncrement();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -104,8 +104,8 @@ public class I18nMessage {
|
|||
|
||||
} catch (MissingResourceException e) {
|
||||
if (!missingKeysMap.containsSet(baseName + "_" + locale.toLanguageTag())) {
|
||||
logger.error("Failed to find resource bundle " + baseName + " " + locale.toLanguageTag()
|
||||
+ ", returning current bundle " + this.bundle.getLocale().toLanguageTag());
|
||||
logger.error("Failed to find resource bundle {} {}, returning current bundle {}", baseName,
|
||||
locale.toLanguageTag(), this.bundle.getLocale().toLanguageTag());
|
||||
missingKeysMap.addSet(baseName + "_" + locale.toLanguageTag(), emptySet());
|
||||
}
|
||||
return this.bundle;
|
||||
|
@ -123,10 +123,10 @@ public class I18nMessage {
|
|||
if (isEmpty(this.bundleName))
|
||||
return getMessage();
|
||||
if (!missingKeysMap.containsSet(this.bundleName + "_" + locale.toLanguageTag())) {
|
||||
logger.warn("No bundle found for " + this.bundleName + " " + locale + ". Available are: ");
|
||||
logger.warn("No bundle found for {} {}. Available are: ", this.bundleName, locale);
|
||||
getBundleMap().forEach((s, map) -> {
|
||||
logger.info(" " + s);
|
||||
map.forEach((l, resourceBundle) -> logger.info(" " + l + ": " + map.keySet()));
|
||||
logger.info(" {}", s);
|
||||
map.forEach((l, resourceBundle) -> logger.info(" {}: {}", l, map.keySet()));
|
||||
});
|
||||
missingKeysMap.addSet(this.bundleName + "_" + locale.toLanguageTag(), emptySet());
|
||||
}
|
||||
|
@ -192,7 +192,7 @@ public class I18nMessage {
|
|||
String languageTag = bundle.getLocale().toLanguageTag();
|
||||
String bundleKey = baseName + "_" + languageTag;
|
||||
if (!missingKeysMap.containsElement(bundleKey, this.key)) {
|
||||
logger.error("Key " + this.key + " is missing in bundle " + baseName + " for locale " + languageTag);
|
||||
logger.error("Key {} is missing in bundle {} for locale {}", this.key, baseName, languageTag);
|
||||
missingKeysMap.addElement(bundleKey, this.key);
|
||||
}
|
||||
|
||||
|
@ -265,14 +265,14 @@ public class I18nMessage {
|
|||
try {
|
||||
CodeSource src = I18nMessage.class.getProtectionDomain().getCodeSource();
|
||||
if (src == null) {
|
||||
logger.error(
|
||||
"Failed to find CodeSource for ProtectionDomain " + I18nMessage.class.getProtectionDomain());
|
||||
logger.error("Failed to find CodeSource for ProtectionDomain {}",
|
||||
I18nMessage.class.getProtectionDomain());
|
||||
return;
|
||||
}
|
||||
|
||||
File jarLocationF = new File(src.getLocation().toURI());
|
||||
if (!(jarLocationF.exists() && jarLocationF.getParentFile().isDirectory())) {
|
||||
logger.info("Found JAR repository at " + jarLocationF.getParentFile());
|
||||
logger.info("Found JAR repository at {}", jarLocationF.getParentFile());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -292,16 +292,10 @@ public class I18nMessage {
|
|||
JarEntry je = entries.nextElement();
|
||||
|
||||
String entryName = je.getName();
|
||||
|
||||
if (entryName.startsWith("META-INF") //
|
||||
|| entryName.equals("ENV.properties") //
|
||||
|| entryName.equals("agentVersion.properties") //
|
||||
|| entryName.equals("appVersion.properties") //
|
||||
|| entryName.equals("componentVersion.properties") //
|
||||
|| entryName.equals("strolch_db_version.properties"))
|
||||
if (!entryName.endsWith(".properties"))
|
||||
continue;
|
||||
|
||||
if (!entryName.endsWith(".properties"))
|
||||
if (shouldIgnorePropertyFile(entryName))
|
||||
continue;
|
||||
|
||||
TypedTuple<String, Locale> tuple = parsePropertyName(entryName);
|
||||
|
@ -316,23 +310,21 @@ public class I18nMessage {
|
|||
bundleMap.addElement(bundle.getBaseBundleName(), bundle.getLocale(), bundle);
|
||||
|
||||
String propertyName = entryName.replace('/', '.');
|
||||
logger.info(
|
||||
" Loaded bundle " + bundle.getBaseBundleName() + " " + bundle.getLocale() + " from "
|
||||
+ propertyName + " from JAR " + file.getName());
|
||||
logger.info(" Loaded bundle {} {} from {} from JAR {}", bundle.getBaseBundleName(),
|
||||
bundle.getLocale(), propertyName, file.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File classesD = new File(jarD.getParentFile(), "classes");
|
||||
if (classesD.isDirectory()) {
|
||||
File[] propertyFiles = classesD.listFiles(
|
||||
(dir, name) -> name.endsWith(".properties") && !(name.equals("appVersion.properties")
|
||||
|| name.equals("ENV.properties")));
|
||||
File[] propertyFiles = classesD.listFiles((dir, name) -> name.endsWith(".properties") && !(
|
||||
name.equals("appVersion.properties") || name.equals("ENV.properties")));
|
||||
if (propertyFiles != null) {
|
||||
for (File propertyFile : propertyFiles) {
|
||||
|
||||
logger.info(" Found property file " + propertyFile.getName() + " in classes "
|
||||
+ classesD.getAbsolutePath());
|
||||
logger.info(" Found property file {} in classes {}", propertyFile.getName(),
|
||||
classesD.getAbsolutePath());
|
||||
|
||||
TypedTuple<String, Locale> tuple = parsePropertyName(propertyFile.getName());
|
||||
if (tuple == null)
|
||||
|
@ -346,8 +338,8 @@ public class I18nMessage {
|
|||
}
|
||||
|
||||
bundleMap.addElement(bundle.getBaseBundleName(), bundle.getLocale(), bundle);
|
||||
logger.info(" Loaded bundle " + bundle.getBaseBundleName() + " " + bundle.getLocale()
|
||||
+ " from file " + propertyFile.getName());
|
||||
logger.info(" Loaded bundle {} {} from file {}", bundle.getBaseBundleName(),
|
||||
bundle.getLocale(), propertyFile.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,17 +370,17 @@ public class I18nMessage {
|
|||
int languageI = Arrays.binarySearch(Locale.getISOLanguages(), language);
|
||||
int countryI = Arrays.binarySearch(Locale.getISOCountries(), country);
|
||||
if (languageI >= 0 && countryI >= 0)
|
||||
locale = new Locale(language, country);
|
||||
locale = Locale.of(language, country);
|
||||
else {
|
||||
logger.warn("Ignoring bad bundle locale for " + entryName);
|
||||
logger.warn("Ignoring malformed bad bundle locale for {}", entryName);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
int languageI = Arrays.binarySearch(Locale.getISOLanguages(), localeS);
|
||||
if (languageI >= 0)
|
||||
locale = new Locale(localeS);
|
||||
locale = Locale.forLanguageTag(localeS);
|
||||
else {
|
||||
logger.warn("Ignoring bad bundle locale for " + entryName);
|
||||
logger.warn("Ignoring bad bundle locale for {}", entryName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -401,6 +393,7 @@ public class I18nMessage {
|
|||
}
|
||||
|
||||
private static class CustomControl extends ResourceBundle.Control {
|
||||
|
||||
private final InputStream stream;
|
||||
|
||||
public CustomControl(InputStream stream) {
|
||||
|
@ -414,65 +407,76 @@ public class I18nMessage {
|
|||
}
|
||||
}
|
||||
|
||||
private static boolean shouldIgnorePropertyFile(String name) {
|
||||
return name.startsWith("META-INF")
|
||||
|| name.equals("ENV.properties")
|
||||
|| name.equals("agentVersion.properties")
|
||||
|| name.equals("appVersion.properties")
|
||||
|| name.equals("componentVersion.properties")
|
||||
|| name.contains("_db_version");
|
||||
}
|
||||
|
||||
private static boolean shouldIgnoreFile(File file) {
|
||||
return file.getName().contains("aopalliance") //
|
||||
|| file.getName().contains("activation") //
|
||||
|| file.getName().contains("antlr") //
|
||||
|| file.getName().contains("assertj-core") //
|
||||
|| file.getName().startsWith("com.sun") //
|
||||
|| file.getName().startsWith("commonj.") //
|
||||
|| file.getName().startsWith("commons-") //
|
||||
|| file.getName().startsWith("jackson-") //
|
||||
|| file.getName().startsWith("hapi-") //
|
||||
|| file.getName().startsWith("jaxb-") //
|
||||
|| file.getName().startsWith("org.hl7.") //
|
||||
|| file.getName().startsWith("listenablefuture-") //
|
||||
|| file.getName().startsWith("j2objc-annotations") //
|
||||
|| file.getName().startsWith("failureaccess-") //
|
||||
|| file.getName().startsWith("error_prone_") //
|
||||
|| file.getName().startsWith("guava-") //
|
||||
|| file.getName().startsWith("org.eclipse") //
|
||||
|| file.getName().startsWith("javax") //
|
||||
|| file.getName().startsWith("jaxws") //
|
||||
|| file.getName().startsWith("jaxrs") //
|
||||
|| file.getName().startsWith("jaxb") //
|
||||
|| file.getName().contains("jsr305") //
|
||||
|| file.getName().contains("c3p0") //
|
||||
|| file.getName().contains("camel") //
|
||||
|| file.getName().contains("checker-qual") //
|
||||
|| file.getName().contains("cron") //
|
||||
|| file.getName().contains("FastInfoset") //
|
||||
|| file.getName().contains("gmbal") //
|
||||
|| file.getName().contains("grizzly") //
|
||||
|| file.getName().contains("gson") //
|
||||
|| file.getName().contains("ha-api") //
|
||||
|| file.getName().contains("HikariCP") //
|
||||
|| file.getName().contains("hk2") //
|
||||
|| file.getName().contains("icu4j") //
|
||||
|| file.getName().contains("jakarta") //
|
||||
|| file.getName().contains("javassist") //
|
||||
|| file.getName().contains("jersey") //
|
||||
|| file.getName().contains("joda-time") //
|
||||
|| file.getName().contains("logback") //
|
||||
|| file.getName().contains("management-api") //
|
||||
|| file.getName().contains("mchange-commons-java") //
|
||||
|| file.getName().contains("mimepull") //
|
||||
|| file.getName().contains("org.abego.treelayout") //
|
||||
|| file.getName().contains("osgi") //
|
||||
|| file.getName().contains("pfl-basic") //
|
||||
|| file.getName().contains("pfl-tf") //
|
||||
|| file.getName().contains("policy-2.7.10") //
|
||||
|| file.getName().contains("postgresql") //
|
||||
|| file.getName().contains("quartz") //
|
||||
|| file.getName().contains("saaj-impl") //
|
||||
|| file.getName().contains("sax") //
|
||||
|| file.getName().contains("slf4j") //
|
||||
|| file.getName().contains("ST4") //
|
||||
|| file.getName().contains("stax-ex") //
|
||||
|| file.getName().contains("stax2-api") //
|
||||
|| file.getName().contains("streambuffer") //
|
||||
|| file.getName().contains("tyrus") //
|
||||
|| file.getName().contains("validation-api") //
|
||||
|| file.getName().contains("yasson");
|
||||
String name = file.getName();
|
||||
return name.contains("aopalliance")
|
||||
|| name.contains("activation")
|
||||
|| name.contains("antlr")
|
||||
|| name.contains("assertj-core")
|
||||
|| name.startsWith("com.sun")
|
||||
|| name.startsWith("commonj.")
|
||||
|| name.startsWith("commons-")
|
||||
|| name.startsWith("jackson-")
|
||||
|| name.startsWith("hapi-")
|
||||
|| name.startsWith("jaxb-")
|
||||
|| name.startsWith("org.hl7.")
|
||||
|| name.startsWith("org.glassfish.")
|
||||
|| name.startsWith("listenablefuture-")
|
||||
|| name.startsWith("j2objc-annotations")
|
||||
|| name.startsWith("failureaccess-")
|
||||
|| name.startsWith("error_prone_")
|
||||
|| name.startsWith("guava-")
|
||||
|| name.startsWith("org.eclipse")
|
||||
|| name.startsWith("javax")
|
||||
|| name.startsWith("jaxws")
|
||||
|| name.startsWith("jaxrs")
|
||||
|| name.startsWith("jaxb")
|
||||
|| name.contains("jsr305")
|
||||
|| name.contains("c3p0")
|
||||
|| name.contains("camel")
|
||||
|| name.contains("checker-qual")
|
||||
|| name.contains("cron")
|
||||
|| name.contains("FastInfoset")
|
||||
|| name.contains("gmbal")
|
||||
|| name.contains("grizzly")
|
||||
|| name.contains("gson")
|
||||
|| name.contains("ha-api")
|
||||
|| name.contains("HikariCP")
|
||||
|| name.contains("hk2")
|
||||
|| name.contains("icu4j")
|
||||
|| name.contains("jakarta")
|
||||
|| name.contains("javassist")
|
||||
|| name.contains("jersey")
|
||||
|| name.contains("joda-time")
|
||||
|| name.contains("logback")
|
||||
|| name.contains("management-api")
|
||||
|| name.contains("mchange-commons-java")
|
||||
|| name.contains("mimepull")
|
||||
|| name.contains("org.abego.treelayout")
|
||||
|| name.contains("osgi")
|
||||
|| name.contains("pfl-basic")
|
||||
|| name.contains("pfl-tf")
|
||||
|| name.contains("policy-2.7.10")
|
||||
|| name.contains("postgresql")
|
||||
|| name.contains("quartz")
|
||||
|| name.contains("saaj-impl")
|
||||
|| name.contains("sax")
|
||||
|| name.contains("slf4j")
|
||||
|| name.contains("ST4")
|
||||
|| name.contains("stax-ex")
|
||||
|| name.contains("stax2-api")
|
||||
|| name.contains("streambuffer")
|
||||
|| name.contains("tyrus")
|
||||
|| name.contains("validation-api")
|
||||
|| name.contains("yasson");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,5 +10,6 @@ public enum TempFileOptions {
|
|||
SEPARATE_DATE_SEGMENTS,
|
||||
WITH_HOURS,
|
||||
SEPARATE_HOURS,
|
||||
APPEND_MILLIS
|
||||
APPEND_MILLIS,
|
||||
MILLIS_FIRST
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -6,14 +6,18 @@ import static li.strolch.utils.helper.TempFileOptions.*;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.MessageFormat;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class FileHelperTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(FileHelperTest.class);
|
||||
|
||||
private static File tempPath;
|
||||
|
||||
|
@ -25,6 +29,8 @@ public class FileHelperTest {
|
|||
@Test
|
||||
public void shouldCreateTempFileNoOptions() {
|
||||
File path = getTempFile(tempPath, "NoOptions", ".txt");
|
||||
logger.info("Temp path: " + path.getAbsolutePath());
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
String expected = today + "/NoOptions.txt";
|
||||
assertEquals(new File(tempPath, expected), path);
|
||||
|
@ -32,35 +38,82 @@ public class FileHelperTest {
|
|||
|
||||
@Test
|
||||
public void shouldCreateTempFileSeparateDateSegments() {
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt", Set.of(SEPARATE_DATE_SEGMENTS));
|
||||
Set<TempFileOptions> options = Set.of(SEPARATE_DATE_SEGMENTS);
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt", options);
|
||||
logger.info("Temp path: " + path.getAbsolutePath());
|
||||
|
||||
LocalDate today = LocalDate.now();
|
||||
String expected =
|
||||
today.getYear() + "/" + normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0') + "/"
|
||||
+ normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0')
|
||||
+ "/SeparateDateSegments.txt";
|
||||
String month = normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0');
|
||||
String day = normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0');
|
||||
String expected = MessageFormat.format("{0,number,#}/{1}/{2}/SeparateDateSegments.txt", today.getYear(), month,
|
||||
day);
|
||||
assertEquals(new File(tempPath, expected), path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateTempFileSeparateDateSegments_WithHours() {
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt", Set.of(SEPARATE_DATE_SEGMENTS, WITH_HOURS));
|
||||
Set<TempFileOptions> options = Set.of(SEPARATE_DATE_SEGMENTS, WITH_HOURS);
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt", options);
|
||||
logger.info("Temp path: " + path.getAbsolutePath());
|
||||
|
||||
LocalDateTime today = LocalDateTime.now();
|
||||
String expected =
|
||||
today.getYear() + "/" + normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0') + "/"
|
||||
+ normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0') + "_" + normalizeLength(
|
||||
String.valueOf(today.getHour()), 2, true, '0') + "/SeparateDateSegments.txt";
|
||||
String month = normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0');
|
||||
String day = normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0');
|
||||
String hour = normalizeLength(String.valueOf(today.getHour()), 2, true, '0');
|
||||
String expected = MessageFormat.format("{0,number,#}/{1}/{2}_{3}/SeparateDateSegments.txt", today.getYear(),
|
||||
month, day, hour);
|
||||
assertEquals(new File(tempPath, expected), path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateTempFileSeparateDateSegments_WithHours_SeparateHours() {
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt",
|
||||
Set.of(SEPARATE_DATE_SEGMENTS, WITH_HOURS, SEPARATE_HOURS));
|
||||
Set<TempFileOptions> options = Set.of(SEPARATE_DATE_SEGMENTS, WITH_HOURS, SEPARATE_HOURS);
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt", options);
|
||||
logger.info("Temp path: " + path.getAbsolutePath());
|
||||
|
||||
LocalDateTime today = LocalDateTime.now();
|
||||
String expected =
|
||||
today.getYear() + "/" + normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0') + "/"
|
||||
+ normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0') + "/" + normalizeLength(
|
||||
String.valueOf(today.getHour()), 2, true, '0') + "/SeparateDateSegments.txt";
|
||||
String month = normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0');
|
||||
String day = normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0');
|
||||
String hour = normalizeLength(String.valueOf(today.getHour()), 2, true, '0');
|
||||
String expected = MessageFormat.format("{0,number,#}/{1}/{2}/{3}/SeparateDateSegments.txt", today.getYear(),
|
||||
month, day, hour);
|
||||
assertEquals(new File(tempPath, expected), path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateTempFileSeparateDateSegments_WithHours_SeparateHoursWithMillis() {
|
||||
LocalDateTime today = LocalDateTime.now();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
Set<TempFileOptions> options = Set.of(SEPARATE_DATE_SEGMENTS, WITH_HOURS, SEPARATE_HOURS, APPEND_MILLIS);
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt", options, today, timestamp);
|
||||
logger.info("Temp path: " + path.getAbsolutePath());
|
||||
|
||||
String month = normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0');
|
||||
String day = normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0');
|
||||
String hour = normalizeLength(String.valueOf(today.getHour()), 2, true, '0');
|
||||
int year = today.getYear();
|
||||
String expected = MessageFormat.format("{0,number,#}/{1}/{2}/{3}/SeparateDateSegments_{4,number,#}.txt", year,
|
||||
month, day, hour, timestamp);
|
||||
assertEquals(new File(tempPath, expected), path);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateTempFileSeparateDateSegments_WithHours_SeparateHoursWithMillisFirst() {
|
||||
LocalDateTime today = LocalDateTime.now();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
Set<TempFileOptions> options = Set.of(SEPARATE_DATE_SEGMENTS, WITH_HOURS, SEPARATE_HOURS, APPEND_MILLIS,
|
||||
MILLIS_FIRST);
|
||||
File path = getTempFile(tempPath, "SeparateDateSegments", ".txt", options, today, timestamp);
|
||||
logger.info("Temp path: " + path.getAbsolutePath());
|
||||
|
||||
String month = normalizeLength(String.valueOf(today.getMonthValue()), 2, true, '0');
|
||||
String day = normalizeLength(String.valueOf(today.getDayOfMonth()), 2, true, '0');
|
||||
String hour = normalizeLength(String.valueOf(today.getHour()), 2, true, '0');
|
||||
int year = today.getYear();
|
||||
String expected = MessageFormat.format("{0,number,#}/{1}/{2}/{3}/{4,number,#}_SeparateDateSegments.txt", year,
|
||||
month, day, hour, timestamp);
|
||||
assertEquals(new File(tempPath, expected), path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -204,14 +204,12 @@ public class RestfulStrolchComponent extends StrolchComponent {
|
|||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
DBC.PRE.assertNull("Instance is already set! This component is a singleton resource!", instance);
|
||||
instance = this;
|
||||
super.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
instance = null;
|
||||
super.stop();
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ public class StrolchRestfulClasses {
|
|||
restfulClasses.add(LanguagesResource.class);
|
||||
restfulClasses.add(OperationsLogResource.class);
|
||||
restfulClasses.add(AgentResource.class);
|
||||
restfulClasses.add(NotificationResource.class);
|
||||
|
||||
// privilege
|
||||
restfulClasses.add(PrivilegeUsersResource.class);
|
||||
|
@ -55,6 +56,7 @@ public class StrolchRestfulClasses {
|
|||
restfulClasses.add(AuditsResource.class);
|
||||
|
||||
Set<Class<?>> providerClasses = new HashSet<>();
|
||||
providerClasses.add(LogRequestFilter.class);
|
||||
providerClasses.add(StrolchRestfulExceptionMapper.class);
|
||||
providerClasses.add(AccessControlResponseFilter.class);
|
||||
providerClasses.add(AuthenticationRequestFilter.class);
|
||||
|
|
|
@ -27,6 +27,8 @@ public class StrolchRestfulConstants {
|
|||
public static final String STROLCH_CERTIFICATE = "strolch.certificate";
|
||||
public static final String STROLCH_REQUEST_SOURCE= "strolch.requestSource";
|
||||
public static final String STROLCH_AUTHORIZATION = "strolch.authorization";
|
||||
public static final String STROLCH_REMOTE_IP = "strolch.remote.ip";
|
||||
public static final String STROLCH_REQUEST_URL = "strolch.request.location";
|
||||
public static final String STROLCH_AUTHORIZATION_EXPIRATION_DATE = "strolch.authorization.expirationDate";
|
||||
|
||||
public static final String MSG = "msg";
|
||||
|
|
|
@ -21,15 +21,14 @@ import jakarta.ws.rs.core.Response;
|
|||
import jakarta.ws.rs.core.Response.Status;
|
||||
import jakarta.ws.rs.ext.ExceptionMapper;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import li.strolch.exception.StrolchAccessDeniedException;
|
||||
import li.strolch.exception.StrolchNotAuthenticatedException;
|
||||
import li.strolch.rest.helper.ResponseUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
@Provider
|
||||
public class StrolchRestfulExceptionMapper implements ExceptionMapper<Exception> {
|
||||
|
||||
|
@ -40,17 +39,14 @@ public class StrolchRestfulExceptionMapper implements ExceptionMapper<Exception>
|
|||
|
||||
logger.error(MessageFormat.format("Handling exception {0}", ex.getClass()), ex);
|
||||
|
||||
if (ex instanceof NotFoundException)
|
||||
return ResponseUtil.toResponse(Status.NOT_FOUND, ex);
|
||||
|
||||
if (ex instanceof StrolchNotAuthenticatedException e) {
|
||||
logger.error("User tried to access resource, but was not authenticated: " + ex.getMessage());
|
||||
return Response.status(Status.UNAUTHORIZED).entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
|
||||
if (ex instanceof StrolchAccessDeniedException e)
|
||||
return ResponseUtil.toResponse(Status.FORBIDDEN, e.getI18n());
|
||||
|
||||
return ResponseUtil.toResponse(ex);
|
||||
return switch (ex) {
|
||||
case NotFoundException ignored -> ResponseUtil.toResponse(Status.NOT_FOUND, ex);
|
||||
case StrolchAccessDeniedException e -> ResponseUtil.toResponse(Status.FORBIDDEN, e.getI18n());
|
||||
case StrolchNotAuthenticatedException e -> {
|
||||
logger.error("User tried to access resource, but was not authenticated: {}", ex.getMessage());
|
||||
yield Response.status(Status.UNAUTHORIZED).entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
default -> ResponseUtil.toResponse(ex);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION;
|
||||
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_AUTHORIZATION_EXPIRATION_DATE;
|
||||
import static li.strolch.rest.filters.AuthenticationRequestFilter.getRemoteIp;
|
||||
import static li.strolch.rest.helper.RestfulHelper.getRemoteIp;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.hasCause;
|
||||
|
||||
|
|
|
@ -13,8 +13,10 @@ import li.strolch.runtime.configuration.SupportedLanguage;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Comparator.*;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCauseMessage;
|
||||
|
||||
@Path("strolch/languages")
|
||||
|
@ -27,14 +29,20 @@ public class LanguagesResource {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getSupportedLanguages() {
|
||||
try {
|
||||
Set<SupportedLanguage> supportedLanguages = RestfulStrolchComponent.getInstance().getAgent()
|
||||
.getStrolchConfiguration().getRuntimeConfiguration().getSupportedLanguages();
|
||||
JsonArray result = supportedLanguages.stream().map(language -> {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty(Tags.Json.LOCALE, language.locale());
|
||||
jsonObject.addProperty(Tags.Json.NAME, language.name());
|
||||
return jsonObject;
|
||||
}).collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
|
||||
JsonArray result = RestfulStrolchComponent
|
||||
.getInstance()
|
||||
.getAgent()
|
||||
.getRuntimeConfiguration()
|
||||
.getSupportedLanguages()
|
||||
.stream()
|
||||
.sorted(comparing(SupportedLanguage::name))
|
||||
.map(language -> {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty(Tags.Json.LOCALE, language.locale());
|
||||
jsonObject.addProperty(Tags.Json.NAME, language.name());
|
||||
return jsonObject;
|
||||
})
|
||||
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
|
||||
|
||||
return Response.ok().entity(result.toString()).build();
|
||||
} catch (Exception e) {
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Copyright 2024 Robert von Burg <eitch@eitchnet.ch>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package li.strolch.rest.endpoint;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import li.strolch.agent.api.StrolchAgent;
|
||||
import li.strolch.model.ParameterBag;
|
||||
import li.strolch.model.Resource;
|
||||
import li.strolch.model.Tags;
|
||||
import li.strolch.model.json.StrolchRootElementToJsonVisitor;
|
||||
import li.strolch.persistence.api.Operation;
|
||||
import li.strolch.persistence.api.StrolchTransaction;
|
||||
import li.strolch.policy.notifications.NotificationsPolicy;
|
||||
import li.strolch.privilege.model.Certificate;
|
||||
import li.strolch.rest.RestfulStrolchComponent;
|
||||
import li.strolch.service.JsonServiceArgument;
|
||||
import li.strolch.service.StringArgument;
|
||||
import li.strolch.service.api.ServiceHandler;
|
||||
import li.strolch.service.api.ServiceResult;
|
||||
import li.strolch.service.notifications.CreateNotificationService;
|
||||
import li.strolch.service.notifications.RemoveNotificationService;
|
||||
import li.strolch.service.notifications.UpdateNotificationService;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import li.strolch.utils.iso8601.ISO8601;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static li.strolch.model.StrolchModelConstants.*;
|
||||
import static li.strolch.rest.RestfulStrolchComponent.getInstance;
|
||||
import static li.strolch.rest.StrolchRestfulConstants.DATA;
|
||||
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_CERTIFICATE;
|
||||
import static li.strolch.rest.helper.ResponseUtil.toResponse;
|
||||
import static li.strolch.runtime.StrolchConstants.DEFAULT_REALM;
|
||||
import static li.strolch.runtime.StrolchConstants.StrolchPrivilegeConstants.*;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getCallerMethod;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getCallerMethodNoClass;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
@Path("strolch/notifications")
|
||||
public class NotificationResource {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(NotificationResource.class);
|
||||
|
||||
private StrolchTransaction openTx(Certificate certificate) {
|
||||
String realm = certificate.getRealm();
|
||||
if (StringHelper.isEmpty(realm))
|
||||
realm = DEFAULT_REALM;
|
||||
return RestfulStrolchComponent.getInstance().openTx(certificate, realm, getCallerMethod(2));
|
||||
}
|
||||
|
||||
private static Certificate validateCertificate(HttpServletRequest request, String privilege) {
|
||||
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
|
||||
RestfulStrolchComponent rest = RestfulStrolchComponent.getInstance();
|
||||
rest.validate(cert).validateAction(privilege, getCallerMethodNoClass(2));
|
||||
return cert;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getUserNotifications(@Context HttpServletRequest request) {
|
||||
Certificate cert = validateCertificate(request, PRIVILEGE_GET_NOTIFICATIONS);
|
||||
try (StrolchTransaction tx = openTx(cert)) {
|
||||
List<Resource> notifications = NotificationsPolicy.getDefaultPolicy(tx).findUserNotifications();
|
||||
Function<Resource, JsonObject> visitor = notificationToJson(tx.getAgent(), cert);
|
||||
return toResponse(DATA, notifications.stream().map(visitor).filter(Objects::nonNull).toList());
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return toResponse(e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getNotification(@Context HttpServletRequest request, @PathParam("id") String id) {
|
||||
Certificate cert = validateCertificate(request, PRIVILEGE_GET_NOTIFICATION);
|
||||
try (StrolchTransaction tx = openTx(cert)) {
|
||||
Resource notification = tx.getResourceBy(TYPE_NOTIFICATION, id, true);
|
||||
tx.assertHasPrivilege(Operation.GET, notification);
|
||||
return toResponse(DATA, notificationToJson(tx.getAgent(), cert).apply(notification));
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return toResponse(e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("all")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getAllNotifications(@Context HttpServletRequest request) {
|
||||
Certificate cert = validateCertificate(request, PRIVILEGE_GET_NOTIFICATIONS_ALL);
|
||||
try (StrolchTransaction tx = openTx(cert)) {
|
||||
StrolchRootElementToJsonVisitor visitor = new StrolchRootElementToJsonVisitor()
|
||||
.withoutPolicies()
|
||||
.withoutStateVariables()
|
||||
.flatBagsByType(TYPE_TEXT, TYPE_VISIBILITY)
|
||||
.resourceHook((notification, notificationJ) -> addLocationNames(notification, notificationJ, tx));
|
||||
return toResponse(DATA, tx.streamResources(TYPE_NOTIFICATION).map(a -> a.accept(visitor)).toList());
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
return toResponse(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addLocationNames(Resource notification, JsonObject notificationJ, StrolchTransaction tx) {
|
||||
if (!notification.hasParameter(BAG_VISIBILITY, PARAM_LOCATIONS))
|
||||
return;
|
||||
JsonArray locationNamesJ = notification
|
||||
.getStringList(BAG_VISIBILITY, PARAM_LOCATIONS)
|
||||
.stream()
|
||||
.map(locationId -> {
|
||||
Resource location = tx.getResourceBy(TYPE_LOCATION, locationId);
|
||||
return location == null ? locationId : location.getName();
|
||||
})
|
||||
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
|
||||
notificationJ.get(BAG_VISIBILITY).getAsJsonObject().add(PARAM_LOCATION_NAMES, locationNamesJ);
|
||||
}
|
||||
|
||||
private static Function<Resource, JsonObject> notificationToJson(StrolchAgent agent, Certificate cert) {
|
||||
return notification -> {
|
||||
JsonObject notificationJ = new JsonObject();
|
||||
notificationJ.addProperty(Tags.Json.ID, notification.getId());
|
||||
|
||||
String lang = cert.getLocale().getLanguage();
|
||||
Optional<ParameterBag> textBagO = ofNullable(notification.getParameterBag(lang))
|
||||
.or(() -> ofNullable(notification.getParameterBag(agent.getLocale().getLanguage())))
|
||||
.or(() -> notification.streamOfParameterBagsByType(TYPE_TEXT).findFirst());
|
||||
if (textBagO.isEmpty())
|
||||
return null;
|
||||
ParameterBag textBag = textBagO.get();
|
||||
|
||||
notificationJ.addProperty(PARAM_TITLE, textBag.getString(PARAM_TITLE));
|
||||
notificationJ.addProperty(PARAM_TEXT, textBag.getString(PARAM_TEXT));
|
||||
notificationJ.addProperty(PARAM_VISIBLE_FROM,
|
||||
ISO8601.toString(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_FROM)));
|
||||
notificationJ.addProperty(PARAM_VISIBLE_TO,
|
||||
ISO8601.toString(notification.getDate(BAG_VISIBILITY, PARAM_VISIBLE_TO)));
|
||||
notificationJ.addProperty(PARAM_ENABLED, notification.getBoolean(BAG_VISIBILITY, PARAM_ENABLED));
|
||||
|
||||
return notificationJ;
|
||||
};
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response create(@Context HttpServletRequest request, String data) {
|
||||
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
|
||||
|
||||
// get JSON
|
||||
JsonObject jsonObject = JsonParser.parseString(data).getAsJsonObject();
|
||||
|
||||
// create service and argument
|
||||
CreateNotificationService svc = new CreateNotificationService();
|
||||
ServiceHandler svcHandler = getInstance().getServiceHandler();
|
||||
JsonServiceArgument arg = svc.getArgumentInstance();
|
||||
arg.jsonElement = jsonObject;
|
||||
|
||||
// call service
|
||||
ServiceResult result = svcHandler.doService(cert, svc, arg);
|
||||
return toResponse(result);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response update(@Context HttpServletRequest request, @PathParam("id") String id, String data) {
|
||||
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
|
||||
|
||||
// get JSON
|
||||
JsonObject jsonObject = JsonParser.parseString(data).getAsJsonObject();
|
||||
|
||||
// create service and argument
|
||||
UpdateNotificationService svc = new UpdateNotificationService();
|
||||
ServiceHandler svcHandler = getInstance().getServiceHandler();
|
||||
JsonServiceArgument arg = svc.getArgumentInstance();
|
||||
arg.objectId = id;
|
||||
arg.jsonElement = jsonObject;
|
||||
|
||||
// call service
|
||||
ServiceResult result = svcHandler.doService(cert, svc, arg);
|
||||
return toResponse(result);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response remove(@Context HttpServletRequest request, @PathParam("id") String id) {
|
||||
Certificate cert = (Certificate) request.getAttribute(STROLCH_CERTIFICATE);
|
||||
|
||||
// create service and argument
|
||||
RemoveNotificationService svc = new RemoveNotificationService();
|
||||
ServiceHandler svcHandler = getInstance().getServiceHandler();
|
||||
StringArgument arg = svc.getArgumentInstance();
|
||||
arg.value = id;
|
||||
|
||||
// call service
|
||||
ServiceResult result = svcHandler.doService(cert, svc, arg);
|
||||
return toResponse(result);
|
||||
}
|
||||
}
|
|
@ -75,13 +75,21 @@ public class ReportResource {
|
|||
|
||||
try (StrolchTransaction tx = getInstance().openTx(cert, realm, getContext())) {
|
||||
|
||||
StrolchRootElementToJsonVisitor visitor = new StrolchRootElementToJsonVisitor().flat().withoutVersion()
|
||||
.withoutObjectType().withoutPolicies().withoutStateVariables()
|
||||
.ignoreBags(BAG_JOINS, BAG_COLUMNS, BAG_ORDERING, BAG_ADDITIONAL_TYPE).ignoreBagByType(TYPE_FILTER)
|
||||
StrolchRootElementToJsonVisitor visitor = new StrolchRootElementToJsonVisitor()
|
||||
.flat()
|
||||
.withoutVersion()
|
||||
.withoutObjectType()
|
||||
.withoutPolicies()
|
||||
.withoutStateVariables()
|
||||
.ignoreBags(BAG_JOINS, BAG_COLUMNS, BAG_ORDERING, BAG_ADDITIONAL_TYPE)
|
||||
.ignoreBagByType(TYPE_FILTER)
|
||||
.resourceHook((reportRes, reportJ) -> reportJ.addProperty(PARAM_DATE_RANGE,
|
||||
reportRes.hasParameter(BAG_PARAMETERS, PARAM_DATE_RANGE_SEL)));
|
||||
JsonArray result = new ReportSearch(tx).search(tx).orderByName(false)
|
||||
.map(resource -> resource.accept(visitor)).asStream()
|
||||
JsonArray result = new ReportSearch(tx)
|
||||
.search(tx)
|
||||
.orderByName(false)
|
||||
.map(resource -> resource.accept(visitor))
|
||||
.asStream()
|
||||
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
|
||||
|
||||
return ResponseUtil.toResponse(DATA, result);
|
||||
|
@ -103,7 +111,8 @@ public class ReportResource {
|
|||
File localesF = new File(request.getServletContext().getRealPath(LOCALES_JSON));
|
||||
JsonObject localeJ = null;
|
||||
if (localesF.exists()) {
|
||||
JsonObject localesJ = JsonParser.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
JsonObject localesJ = JsonParser
|
||||
.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
.getAsJsonObject();
|
||||
if (localesJ.has(cert.getLocale().toLanguageTag()))
|
||||
localeJ = localesJ.get(cert.getLocale().toLanguageTag()).getAsJsonObject();
|
||||
|
@ -124,21 +133,19 @@ public class ReportResource {
|
|||
JsonArray facetsJ = new JsonArray();
|
||||
JsonObject finalLocaleJ = localeJ;
|
||||
|
||||
MapOfSets<String, StrolchRootElement> criteria = report.generateFilterCriteria(limit);
|
||||
MapOfSets<String, JsonObject> criteria = report.generateFilterCriteria(limit);
|
||||
|
||||
criteria.keySet().stream().sorted(comparing(type -> {
|
||||
JsonElement translatedJ = finalLocaleJ == null ? null : finalLocaleJ.get(type);
|
||||
return translatedJ == null ? type : translatedJ.getAsString();
|
||||
})).forEach(type -> {
|
||||
Set<StrolchRootElement> elements = criteria.getSet(type);
|
||||
Set<JsonObject> elements = criteria.getSet(type);
|
||||
JsonObject filter = new JsonObject();
|
||||
filter.addProperty(Tags.Json.TYPE, type);
|
||||
filter.add(Tags.Json.VALUES, elements.stream().sorted(comparing(StrolchElement::getName)).map(f -> {
|
||||
JsonObject o = new JsonObject();
|
||||
o.addProperty(Tags.Json.ID, f.getId());
|
||||
o.addProperty(Tags.Json.NAME, f.getName());
|
||||
return o;
|
||||
}).collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
|
||||
filter.add(Tags.Json.VALUES, elements
|
||||
.stream()
|
||||
.sorted(comparing(e -> e.get(Tags.Json.NAME).getAsString()))
|
||||
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll));
|
||||
facetsJ.add(filter);
|
||||
});
|
||||
|
||||
|
@ -148,7 +155,7 @@ public class ReportResource {
|
|||
result.addProperty(PARAM_DURATION, duration);
|
||||
result.addProperty(PARAM_PARALLEL, report.isParallel());
|
||||
|
||||
logger.info("Facet Generation for " + id + " took: " + duration);
|
||||
logger.info("Facet Generation for {} took: {}", id, duration);
|
||||
return ResponseUtil.toResponse(DATA, result);
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +178,8 @@ public class ReportResource {
|
|||
File localesF = new File(request.getServletContext().getRealPath(LOCALES_JSON));
|
||||
JsonObject localeJ = null;
|
||||
if (localesF.exists()) {
|
||||
JsonObject localesJ = JsonParser.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
JsonObject localesJ = JsonParser
|
||||
.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
.getAsJsonObject();
|
||||
if (localesJ.has(cert.getLocale().toLanguageTag()))
|
||||
localeJ = localesJ.get(cert.getLocale().toLanguageTag()).getAsJsonObject();
|
||||
|
@ -189,37 +197,13 @@ public class ReportResource {
|
|||
report.getReportPolicy().setI18nData(localeJ);
|
||||
|
||||
// get filter criteria
|
||||
Stream<StrolchRootElement> criteria = report.generateFilterCriteria(type);
|
||||
if (query != null && !query.isEmpty()) {
|
||||
String[] parts = query.split(" ");
|
||||
criteria = criteria.filter(f -> ObjectHelper.contains(f.getName(), parts, true));
|
||||
}
|
||||
|
||||
int maxFacetValues;
|
||||
int reportMaxFacetValues = report.getReportResource().getInteger(PARAM_MAX_FACET_VALUES);
|
||||
if (reportMaxFacetValues != 0 && reportMaxFacetValues != limit) {
|
||||
logger.warn("Report " + report.getReportResource().getId() + " has " + PARAM_MAX_FACET_VALUES +
|
||||
" defined as " + reportMaxFacetValues + ". Ignoring requested limit " + limit);
|
||||
maxFacetValues = reportMaxFacetValues;
|
||||
} else {
|
||||
maxFacetValues = limit;
|
||||
}
|
||||
|
||||
criteria = criteria.sorted(comparing(StrolchElement::getName));
|
||||
|
||||
if (maxFacetValues != 0)
|
||||
criteria = criteria.limit(maxFacetValues);
|
||||
|
||||
// add the data finally
|
||||
JsonArray array = criteria.map(f -> {
|
||||
JsonObject o = new JsonObject();
|
||||
o.addProperty(Tags.Json.ID, f.getId());
|
||||
o.addProperty(Tags.Json.NAME, f.getName());
|
||||
return o;
|
||||
}).collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
|
||||
JsonArray array = report
|
||||
.generateFilterCriteria(type, limit, query)
|
||||
.sorted(comparing(e -> e.get(Tags.Json.NAME).getAsString()))
|
||||
.collect(JsonArray::new, JsonArray::add, JsonArray::addAll);
|
||||
|
||||
String duration = formatNanoDuration(System.nanoTime() - start);
|
||||
logger.info("Facet Generation for " + id + "." + type + " took: " + duration);
|
||||
logger.info("Facet Generation for {}.{} took: {}", id, type, duration);
|
||||
return ResponseUtil.toResponse(DATA, array);
|
||||
}
|
||||
}
|
||||
|
@ -273,7 +257,8 @@ public class ReportResource {
|
|||
File localesF = new File(request.getServletContext().getRealPath(LOCALES_JSON));
|
||||
JsonObject localeJ = null;
|
||||
if (localesF.exists()) {
|
||||
JsonObject localesJ = JsonParser.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
JsonObject localesJ = JsonParser
|
||||
.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
.getAsJsonObject();
|
||||
if (localesJ.has(cert.getLocale().toLanguageTag()))
|
||||
localeJ = localesJ.get(cert.getLocale().toLanguageTag()).getAsJsonObject();
|
||||
|
@ -349,7 +334,7 @@ public class ReportResource {
|
|||
finalResult.addProperty(PARAM_DURATION, duration);
|
||||
finalResult.addProperty(PARAM_PARALLEL, report.isParallel());
|
||||
|
||||
logger.info(id + " Report took: " + duration);
|
||||
logger.info("{} Report took: {}", id, duration);
|
||||
return ResponseUtil.toResponse(DATA, finalResult);
|
||||
}
|
||||
}
|
||||
|
@ -400,7 +385,8 @@ public class ReportResource {
|
|||
File localesF = new File(request.getServletContext().getRealPath(LOCALES_JSON));
|
||||
JsonObject localeJ = null;
|
||||
if (localesF.exists()) {
|
||||
JsonObject localesJ = JsonParser.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
JsonObject localesJ = JsonParser
|
||||
.parseString(new String(Files.readAllBytes(localesF.toPath())))
|
||||
.getAsJsonObject();
|
||||
if (localesJ.has(cert.getLocale().toLanguageTag()))
|
||||
localeJ = localesJ.get(cert.getLocale().toLanguageTag()).getAsJsonObject();
|
||||
|
@ -411,8 +397,10 @@ public class ReportResource {
|
|||
|
||||
// send
|
||||
String fileName = id + "_" + System.currentTimeMillis() + ".csv";
|
||||
return Response.ok(out, TEXT_CSV_TYPE)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + fileName + "\"").build();
|
||||
return Response
|
||||
.ok(out, TEXT_CSV_TYPE)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + fileName + "\"")
|
||||
.build();
|
||||
}
|
||||
|
||||
private StreamingOutput getOut(Certificate cert, String realm, String reportId, JsonObject localeJ,
|
||||
|
@ -485,12 +473,14 @@ public class ReportResource {
|
|||
// go through all filters and add to the map of sets
|
||||
for (JsonElement elem : filters.getAsJsonArray()) {
|
||||
if (!elem.isJsonObject()) {
|
||||
logger.warn("There are wrong formatted filters:\n" + elem);
|
||||
logger.warn("There are wrong formatted filters:\n{}", elem);
|
||||
continue;
|
||||
}
|
||||
|
||||
JsonObject filter = elem.getAsJsonObject();
|
||||
filter.get(PARAM_FACET_FILTERS).getAsJsonArray()
|
||||
filter
|
||||
.get(PARAM_FACET_FILTERS)
|
||||
.getAsJsonArray()
|
||||
.forEach(f -> result.addElement(filter.get(PARAM_FACET_TYPE).getAsString(), f.getAsString()));
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
package li.strolch.rest.filters;
|
||||
|
||||
import jakarta.annotation.Priority;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.Priorities;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerRequestFilter;
|
||||
import jakarta.ws.rs.core.*;
|
||||
import jakarta.ws.rs.core.Cookie;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
import li.strolch.exception.StrolchAccessDeniedException;
|
||||
import li.strolch.exception.StrolchNotAuthenticatedException;
|
||||
|
@ -59,9 +61,6 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthenticationRequestFilter.class);
|
||||
|
||||
@Context
|
||||
private HttpServletRequest request;
|
||||
|
||||
private Set<String> unsecuredPaths;
|
||||
|
||||
protected RestfulStrolchComponent getRestful() {
|
||||
|
@ -112,9 +111,8 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext) {
|
||||
String remoteIp = getRemoteIp(this.request);
|
||||
logger.info("Remote IP: " + remoteIp + ": " + requestContext.getMethod() + " " +
|
||||
requestContext.getUriInfo().getRequestUri());
|
||||
|
||||
String remoteIp = (String) requestContext.getProperty(STROLCH_REMOTE_IP);
|
||||
|
||||
try {
|
||||
|
||||
|
@ -126,19 +124,25 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
|
||||
} catch (StrolchNotAuthenticatedException e) {
|
||||
logger.error(e.getMessage());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authenticated!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.UNAUTHORIZED)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authenticated!")
|
||||
.build());
|
||||
} catch (StrolchAccessDeniedException e) {
|
||||
logger.error(e.getMessage());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.FORBIDDEN).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authorized!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.UNAUTHORIZED)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User is not authorized!")
|
||||
.build());
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.FORBIDDEN).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User cannot access the resource.").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("User cannot access the resource.")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,9 +213,11 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
if (isEmpty(sessionId)) {
|
||||
logger.error(
|
||||
"No Authorization header or cookie on request to URL " + requestContext.getUriInfo().getPath());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Missing Authorization!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.UNAUTHORIZED)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Missing Authorization!")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -223,9 +229,11 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
|
||||
if (!getRestful().isBasicAuthEnabled()) {
|
||||
logger.error("Basic Auth is not available for URL " + requestContext.getUriInfo().getPath());
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.FORBIDDEN).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Basic Auth not available").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.FORBIDDEN)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Basic Auth not available")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -233,9 +241,11 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
basicAuth = new String(Base64.getDecoder().decode(basicAuth.getBytes()), StandardCharsets.UTF_8);
|
||||
String[] parts = basicAuth.split(":");
|
||||
if (parts.length != 2) {
|
||||
requestContext.abortWith(
|
||||
Response.status(Response.Status.BAD_REQUEST).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Invalid Basic Authorization!").build());
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Invalid Basic Authorization!")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -256,10 +266,14 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
Certificate certificate = sessionHandler.validate(sessionId, remoteIp);
|
||||
|
||||
if (certificate.getUsage() == Usage.SET_PASSWORD) {
|
||||
if (!requestContext.getUriInfo().getMatchedURIs()
|
||||
if (!requestContext
|
||||
.getUriInfo()
|
||||
.getMatchedURIs()
|
||||
.contains("strolch/privilege/users/" + certificate.getUsername() + "/password")) {
|
||||
requestContext.abortWith(Response.status(Response.Status.FORBIDDEN)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN).entity("Can only set password!")
|
||||
requestContext.abortWith(Response
|
||||
.status(Response.Status.FORBIDDEN)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN)
|
||||
.entity("Can only set password!")
|
||||
.build());
|
||||
return null;
|
||||
}
|
||||
|
@ -269,27 +283,4 @@ public class AuthenticationRequestFilter implements ContainerRequestFilter {
|
|||
requestContext.setProperty(STROLCH_REQUEST_SOURCE, remoteIp);
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public static String getRemoteIp(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
logger.error("HttpServletRequest NOT AVAILABLE! Probably running in TEST!");
|
||||
return "(null)";
|
||||
}
|
||||
|
||||
String remoteHost = request.getRemoteHost();
|
||||
String remoteAddr = request.getRemoteAddr();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (remoteHost.equals(remoteAddr))
|
||||
sb.append(remoteAddr);
|
||||
else {
|
||||
sb.append(remoteHost).append(": (").append(remoteAddr).append(")");
|
||||
}
|
||||
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (isNotEmpty(xForwardedFor))
|
||||
sb.append(" (fwd)=> ").append(xForwardedFor);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package li.strolch.rest.filters;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerRequestFilter;
|
||||
import jakarta.ws.rs.container.PreMatching;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import li.strolch.rest.helper.RestfulHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_REMOTE_IP;
|
||||
import static li.strolch.rest.StrolchRestfulConstants.STROLCH_REQUEST_URL;
|
||||
import static li.strolch.rest.helper.ServletRequestHelper.logRequest;
|
||||
|
||||
@PreMatching
|
||||
public class LogRequestFilter implements ContainerRequestFilter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LogRequestFilter.class);
|
||||
|
||||
@Context
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext) throws IOException {
|
||||
String remoteIp = RestfulHelper.getRemoteIp(this.request);
|
||||
logger.info("Remote IP: {}: {} {}", remoteIp, requestContext.getMethod(),
|
||||
requestContext.getUriInfo().getRequestUri());
|
||||
|
||||
this.request.setAttribute(STROLCH_REMOTE_IP, remoteIp);
|
||||
this.request.setAttribute(STROLCH_REQUEST_URL,
|
||||
requestContext.getMethod() + " " + requestContext.getUriInfo().getRequestUri());
|
||||
|
||||
logRequest(this.request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package li.strolch.rest.helper;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import li.strolch.exception.StrolchAccessDeniedException;
|
||||
import li.strolch.exception.StrolchNotAuthenticatedException;
|
||||
import li.strolch.privilege.base.InvalidCredentialsException;
|
||||
import li.strolch.privilege.model.Certificate;
|
||||
import li.strolch.privilege.model.Usage;
|
||||
import li.strolch.runtime.sessions.StrolchSessionHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCause;
|
||||
import static li.strolch.utils.helper.ExceptionHelper.getRootCauseExceptionMessage;
|
||||
import static li.strolch.utils.helper.StringHelper.isEmpty;
|
||||
|
||||
public class BasicAuth {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BasicAuth.class);
|
||||
|
||||
private final StrolchSessionHandler sessionHandler;
|
||||
|
||||
public BasicAuth(StrolchSessionHandler sessionHandler) {
|
||||
this.sessionHandler = sessionHandler;
|
||||
}
|
||||
|
||||
public Certificate doBasicAuth(String authorization, String remoteIp) throws BasicAuthFailure {
|
||||
if (isEmpty(authorization))
|
||||
throw new BasicAuthFailure(Response.Status.UNAUTHORIZED, "No authentication!");
|
||||
if (!authorization.startsWith("Basic "))
|
||||
throw new BasicAuthFailure(Response.Status.BAD_REQUEST, "Invalid basic auth request!");
|
||||
|
||||
try {
|
||||
String auth = new String(Base64.getDecoder().decode(authorization.substring(6)), UTF_8);
|
||||
int splitIndex = auth.indexOf(':');
|
||||
String username = auth.substring(0, splitIndex);
|
||||
String password = auth.substring(splitIndex + 1);
|
||||
|
||||
return this.sessionHandler.authenticate(username, password.toCharArray(), remoteIp, Usage.SINGLE, false);
|
||||
|
||||
} catch (StrolchNotAuthenticatedException | InvalidCredentialsException e) {
|
||||
logger.error(e.getMessage());
|
||||
throw new BasicAuthFailure(Response.Status.UNAUTHORIZED, "Authentication failed", e);
|
||||
} catch (StrolchAccessDeniedException e) {
|
||||
logger.error(e.getMessage());
|
||||
throw new BasicAuthFailure(Response.Status.FORBIDDEN, "User is not authorized!", e);
|
||||
} catch (Exception e) {
|
||||
Response.Status status;
|
||||
String msg;
|
||||
Throwable rootCause = getRootCause(e);
|
||||
if (rootCause instanceof StrolchNotAuthenticatedException
|
||||
|| rootCause instanceof InvalidCredentialsException) {
|
||||
status = Response.Status.UNAUTHORIZED;
|
||||
msg = "Authentication failed";
|
||||
} else if (rootCause instanceof StrolchAccessDeniedException) {
|
||||
status = Response.Status.FORBIDDEN;
|
||||
msg = "User is not authorized!";
|
||||
} else {
|
||||
status = Response.Status.INTERNAL_SERVER_ERROR;
|
||||
msg = "Internal error";
|
||||
}
|
||||
|
||||
if (status == Response.Status.INTERNAL_SERVER_ERROR)
|
||||
logger.error(e.getMessage(), e);
|
||||
else
|
||||
logger.error("Basic Auth failed: {}", getRootCauseExceptionMessage(e));
|
||||
throw new BasicAuthFailure(status, msg, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package li.strolch.rest.helper;
|
||||
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
public class BasicAuthFailure extends Exception {
|
||||
|
||||
private final Response.Status status;
|
||||
private final String reason;
|
||||
|
||||
public BasicAuthFailure(Response.Status status, String reason) {
|
||||
this.status = status;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public BasicAuthFailure(Response.Status status, String reason, Exception cause) {
|
||||
super(cause);
|
||||
this.status = status;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public Response.Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import jakarta.ws.rs.core.MediaType;
|
|||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.Status;
|
||||
import li.strolch.exception.StrolchElementNotFoundException;
|
||||
import li.strolch.exception.StrolchException;
|
||||
import li.strolch.exception.StrolchNotAuthenticatedException;
|
||||
import li.strolch.exception.StrolchUserMessageException;
|
||||
import li.strolch.model.i18n.I18nMessageToJsonVisitor;
|
||||
|
@ -139,34 +140,24 @@ public class ResponseUtil {
|
|||
if (svcResult.isOk())
|
||||
return Response.ok().entity(json).type(MediaType.APPLICATION_JSON).build();
|
||||
|
||||
Status status;
|
||||
if (t instanceof AccessDeniedException) {
|
||||
status = Status.FORBIDDEN;
|
||||
} else if (t instanceof PrivilegeModelException) {
|
||||
status = Status.INTERNAL_SERVER_ERROR;
|
||||
} else if (t instanceof PrivilegeException) {
|
||||
status = Status.UNAUTHORIZED;
|
||||
} else if (t instanceof StrolchElementNotFoundException) {
|
||||
status = Status.NOT_FOUND;
|
||||
} else {
|
||||
status = Status.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
Status status = switch (t) {
|
||||
case AccessDeniedException ignored -> Status.FORBIDDEN;
|
||||
case PrivilegeException ignored -> Status.UNAUTHORIZED;
|
||||
case StrolchElementNotFoundException ignored -> Status.NOT_FOUND;
|
||||
case null, default -> Status.INTERNAL_SERVER_ERROR;
|
||||
};
|
||||
|
||||
return Response.status(status).entity(json).type(MediaType.APPLICATION_JSON).build();
|
||||
}
|
||||
|
||||
public static Response toResponse(Throwable t) {
|
||||
if (t instanceof StrolchNotAuthenticatedException)
|
||||
return ResponseUtil.toResponse(Status.UNAUTHORIZED, t);
|
||||
if (t instanceof AccessDeniedException)
|
||||
return ResponseUtil.toResponse(Status.FORBIDDEN, t);
|
||||
if (t instanceof StrolchElementNotFoundException)
|
||||
return ResponseUtil.toResponse(Status.NOT_FOUND, t);
|
||||
if (t instanceof PrivilegeModelException)
|
||||
return ResponseUtil.toResponse(Status.INTERNAL_SERVER_ERROR, t);
|
||||
if (t instanceof PrivilegeException)
|
||||
return ResponseUtil.toResponse(Status.FORBIDDEN, t);
|
||||
return toResponse(Status.INTERNAL_SERVER_ERROR, t);
|
||||
return switch (t) {
|
||||
case StrolchNotAuthenticatedException ignored -> toResponse(Status.UNAUTHORIZED, t);
|
||||
case AccessDeniedException ignored -> toResponse(Status.FORBIDDEN, t);
|
||||
case StrolchElementNotFoundException ignored -> toResponse(Status.NOT_FOUND, t);
|
||||
case PrivilegeException ignored -> toResponse(Status.FORBIDDEN, t);
|
||||
case null, default -> toResponse(Status.INTERNAL_SERVER_ERROR, t);
|
||||
};
|
||||
}
|
||||
|
||||
public static Response toResponse(Status status, String msg) {
|
||||
|
@ -180,13 +171,13 @@ public class ResponseUtil {
|
|||
public static Response toResponse(Status status, Throwable t) {
|
||||
JsonObject response = new JsonObject();
|
||||
|
||||
if (t instanceof StrolchUserMessageException ex && ((StrolchUserMessageException) t).hasI18n()) {
|
||||
response.add("i18n", ex.getI18n().accept(new I18nMessageToJsonVisitor()));
|
||||
} else {
|
||||
Throwable rootCause = getRootCause(t);
|
||||
if (rootCause instanceof StrolchUserMessageException ex &&
|
||||
((StrolchUserMessageException) rootCause).hasI18n()) {
|
||||
response.add("i18n", ex.getI18n().accept(new I18nMessageToJsonVisitor()));
|
||||
switch (t) {
|
||||
case StrolchException ex when ex.hasI18n() ->
|
||||
response.add("i18n", ex.getI18n().accept(new I18nMessageToJsonVisitor()));
|
||||
case null, default -> {
|
||||
Throwable rootCause = getRootCause(t);
|
||||
if (rootCause instanceof StrolchUserMessageException ex && ex.hasI18n())
|
||||
response.add("i18n", ex.getI18n().accept(new I18nMessageToJsonVisitor()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,17 +15,10 @@
|
|||
*/
|
||||
package li.strolch.rest.helper;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import li.strolch.model.StrolchRootElement;
|
||||
import li.strolch.model.visitor.StrolchRootElementVisitor;
|
||||
import li.strolch.privilege.model.Certificate;
|
||||
|
@ -36,16 +29,28 @@ import li.strolch.search.SearchResult;
|
|||
import li.strolch.utils.collections.Paging;
|
||||
import li.strolch.utils.dbc.DBC;
|
||||
import li.strolch.utils.helper.StringHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static li.strolch.utils.helper.StringHelper.isNotEmpty;
|
||||
|
||||
/**
|
||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||
*/
|
||||
public class RestfulHelper {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RestfulHelper.class);
|
||||
|
||||
public static Locale getLocale(HttpHeaders headers) {
|
||||
if (headers == null || StringHelper.isEmpty(headers.getHeaderString(HttpHeaders.ACCEPT_LANGUAGE)))
|
||||
return null;
|
||||
return headers.getAcceptableLanguages().get(0);
|
||||
return headers.getAcceptableLanguages().getFirst();
|
||||
}
|
||||
|
||||
public static Certificate getCert(HttpServletRequest request) {
|
||||
|
@ -105,4 +110,27 @@ public class RestfulHelper {
|
|||
root.add("data", data);
|
||||
return root;
|
||||
}
|
||||
|
||||
public static String getRemoteIp(HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
logger.error("HttpServletRequest NOT AVAILABLE! Probably running in TEST!");
|
||||
return "(null)";
|
||||
}
|
||||
|
||||
String remoteHost = request.getRemoteHost();
|
||||
String remoteAddr = request.getRemoteAddr();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (remoteHost.equals(remoteAddr))
|
||||
sb.append(remoteAddr);
|
||||
else {
|
||||
sb.append(remoteHost).append(": (").append(remoteAddr).append(")");
|
||||
}
|
||||
|
||||
String xForwardedFor = request.getHeader("X-Forwarded-For");
|
||||
if (isNotEmpty(xForwardedFor))
|
||||
sb.append(" (fwd)=> ").append(xForwardedFor);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
package li.strolch.rest.helper;
|
||||
|
||||
import jakarta.servlet.http.Cookie;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import li.strolch.rest.RestfulStrolchComponent;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Enumeration;
|
||||
|
||||
import static li.strolch.utils.helper.StringHelper.*;
|
||||
|
||||
public class ServletRequestHelper {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ServletRequestHelper.class);
|
||||
|
||||
private static final int PADDING = 30;
|
||||
private static final int PADDING_SHORT = 27;
|
||||
|
||||
public static void logRequest(HttpServletRequest request) {
|
||||
if (!RestfulStrolchComponent.getInstance().isRestLogging())
|
||||
return;
|
||||
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb
|
||||
.append("\n")
|
||||
.append(pad("REQUEST URL:"))
|
||||
.append(request.getMethod())
|
||||
.append(" ")
|
||||
.append(request.getRequestURL())
|
||||
.append("\n");
|
||||
sb
|
||||
.append(pad("REQUEST:"))
|
||||
.append(request.getMethod())
|
||||
.append(" ")
|
||||
.append(request.getRequestURI())
|
||||
.append("\n");
|
||||
|
||||
sb.append(pad("QUERY:")).append('?').append(string(request.getQueryString())).append("\n");
|
||||
|
||||
String from = request.getRemoteAddr();
|
||||
sb.append(pad("REMOTE:")).append(from);
|
||||
if (!from.equals(request.getRemoteHost()))
|
||||
sb.append("/").append(request.getRemoteHost());
|
||||
sb.append(":").append(request.getRemotePort()).append("\n");
|
||||
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
if (!headerNames.hasMoreElements()) {
|
||||
sb.append("HEADERS: (none)!\n");
|
||||
} else {
|
||||
sb.append("HEADERS: \n");
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String headerName = headerNames.nextElement();
|
||||
String headerValue = request.getHeader(headerName);
|
||||
sb.append(" ").append(padShort(headerName)).append(" = ").append(headerValue).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if (cookies == null) {
|
||||
sb.append("COOKIES: (none)!\n");
|
||||
} else {
|
||||
sb.append("COOKIES: \n");
|
||||
for (Cookie cookie : cookies) {
|
||||
sb
|
||||
.append(" ")
|
||||
.append(padShort(cookie.getName()))
|
||||
.append(" = ")
|
||||
.append(cookie.getValue())
|
||||
.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(pad("AuthType:")).append(string(request.getAuthType())).append("\n");
|
||||
sb.append(pad("User-Principal:")).append(string(request.getUserPrincipal())).append("\n");
|
||||
sb.append(pad("Remote User:")).append(string(request.getRemoteUser())).append("\n");
|
||||
sb.append(pad("Requested SessionID:")).append(string(request.getRequestedSessionId())).append("\n");
|
||||
sb.append(pad("Protocol:")).append(string(request.getProtocol())).append("\n");
|
||||
sb.append(pad("RequestId:")).append(string(request.getRequestId())).append("\n");
|
||||
sb.append(pad("DispatcherType:")).append(string(request.getDispatcherType())).append("\n");
|
||||
sb.append(pad("CharacterEncoding:")).append(string(request.getCharacterEncoding())).append("\n");
|
||||
sb.append(pad("ContentType:")).append(string(request.getContentType())).append("\n");
|
||||
sb.append(pad("ContentLength:")).append(request.getContentLengthLong()).append("\n");
|
||||
|
||||
logger.info(sb.toString());
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to log request", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String pad(String string) {
|
||||
return normalizeLength(string, PADDING, false, ' ');
|
||||
}
|
||||
|
||||
private static String padShort(String string) {
|
||||
return normalizeLength(string, PADDING_SHORT, false, ' ');
|
||||
}
|
||||
|
||||
private static String string(String string) {
|
||||
return isEmpty(string) ? "(none)" : string;
|
||||
}
|
||||
|
||||
private static String string(Enum<?> e) {
|
||||
return e == null ? "(none)" : e.name();
|
||||
}
|
||||
|
||||
private static String string(Object o) {
|
||||
return o == null ? "(none)" : o.toString();
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package li.strolch.websocket;
|
||||
|
||||
import static li.strolch.rest.filters.AuthenticationRequestFilter.getRemoteIp;
|
||||
import static li.strolch.rest.helper.RestfulHelper.getRemoteIp;
|
||||
|
||||
import jakarta.servlet.annotation.WebFilter;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch</artifactId>
|
||||
<version>2.2.0-SNAPSHOT</version>
|
||||
<version>2.3.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
Loading…
Reference in New Issue