strolch-website/docs/index.json

1 line
204 KiB
JSON

[{"uri":"https://strolch.li/api/","title":"API","tags":[],"description":"","content":"Overview The Strolch API revolves around the StrolchTransaction object. The main concept is to implement your use cases in Service implementations. You open a transaction using the openTx(String)-method and then perform the use case by adding your Command instances to the transaction.\nTransactions are opened on a StrolchRealm. The realms are used to separate mandates in a single runtime instance of Strolch. Each realm has its own ResourceMap, OrderMap, ActivityMap instances from which the TX retrieves the elements.\nModel The Strolch model is implemented in the project li.strolch.model.\nThe Strolch model consists of three root level elements: Resource, Order and Activity. Each element has at least the following attributes:\n Id → the element\u0026rsquo;s id Name → the element\u0026rsquo;s name Type → the element\u0026rsquo;s type Each root element can have any number of ParameterBag instances on it, which in turn can have any number of Parameters on it. Accessing these objects is always done by their IDs. Strolch root elements are always stored in the respective ElementMaps in their Strolch realm. Thus accessing a certain parameter from a Resource would look like this:\ntry (StrolchTransaction tx = openTx(realmName)) { Resource resource = tx.getResourceBy(\u0026#34;TestType\u0026#34;, \u0026#34;MyTestResource\u0026#34;); Date date = resource.getDate(\u0026#34;@bag01\u0026#34;, \u0026#34;@param6\u0026#34;); logger.info(\u0026#34;@param6 date is \u0026#34; + date); } XML Presentation of Strolch\u0026rsquo;s top level elements of Resources:\n\u0026lt;!-- Resource instance --\u0026gt; \u0026lt;Resource Id=\u0026#34;MyTestResource\u0026#34; Name=\u0026#34;Test Name\u0026#34; Type=\u0026#34;TestType\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param7\u0026#34; Name=\u0026#34;StringList Param\u0026#34; Type=\u0026#34;StringList\u0026#34; Value=\u0026#34;Hello;World\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param6\u0026#34; Name=\u0026#34;Date Param\u0026#34; Type=\u0026#34;Date\u0026#34; Value=\u0026#34;2012-11-30T18:12:05.628+01:00\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param5\u0026#34; Name=\u0026#34;String Param\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;Strolch\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag02\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param4\u0026#34; Name=\u0026#34;Long Param\u0026#34; Type=\u0026#34;Long\u0026#34; Value=\u0026#34;4453234566\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param3\u0026#34; Name=\u0026#34;Integer Param\u0026#34; Type=\u0026#34;Integer\u0026#34; Value=\u0026#34;77\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param2\u0026#34; Name=\u0026#34;Float Param\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;44.3\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;TimedState Id=\u0026#34;@integerState\u0026#34; Name=\u0026#34;Integer State\u0026#34; Type=\u0026#34;IntegerState\u0026#34;\u0026gt; \u0026lt;Value Time=\u0026#34;0\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Value Time=\u0026#34;1\u0026#34; Value=\u0026#34;2\u0026#34; /\u0026gt; \u0026lt;Value Time=\u0026#34;2\u0026#34; Value=\u0026#34;3\u0026#34; /\u0026gt; \u0026lt;/TimedState\u0026gt; \u0026lt;/Resource\u0026gt; XML Presentation of Strolch\u0026rsquo;s top level elements of Orders:\n\u0026lt;!-- Order instance --\u0026gt; \u0026lt;Order Id=\u0026#34;MyTestOrder\u0026#34; Name=\u0026#34;Test Name\u0026#34; Type=\u0026#34;TestType\u0026#34; Date=\u0026#34;2013-11-20T07:42:57.699Z\u0026#34; State=\u0026#34;CREATED\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param7\u0026#34; Name=\u0026#34;StringList Param\u0026#34; Type=\u0026#34;StringList\u0026#34; Value=\u0026#34;Hello;World\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param6\u0026#34; Name=\u0026#34;Date Param\u0026#34; Type=\u0026#34;Date\u0026#34; Value=\u0026#34;2012-11-30T18:12:05.628+01:00\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param5\u0026#34; Name=\u0026#34;String Param\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;Strolch\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag02\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param4\u0026#34; Name=\u0026#34;Long Param\u0026#34; Type=\u0026#34;Long\u0026#34; Value=\u0026#34;4453234566\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param3\u0026#34; Name=\u0026#34;Integer Param\u0026#34; Type=\u0026#34;Integer\u0026#34; Value=\u0026#34;77\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param2\u0026#34; Name=\u0026#34;Float Param\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;44.3\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Order\u0026gt; XML Presentation of Strolch\u0026rsquo;s top level elements of Activities:\n\u0026lt;!-- Activity instance --\u0026gt; \u0026lt;Activity Id=\u0026#34;bicycleProduction\u0026#34; Name=\u0026#34;Bicycle Production\u0026#34; Type=\u0026#34;Series\u0026#34;\u0026gt; \u0026lt;Activity Id=\u0026#34;componentProduction\u0026#34; Name=\u0026#34;Production of components\u0026#34; Type=\u0026#34;Series\u0026#34;\u0026gt; \u0026lt;Action Id=\u0026#34;consumeGears\u0026#34; Name=\u0026#34;Gears\u0026#34; ResourceId=\u0026#34;gears\u0026#34; ResourceType=\u0026#34;Article\u0026#34; Type=\u0026#34;Consume\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;objectives\u0026#34; Name=\u0026#34;Production goals\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;duration\u0026#34; Name=\u0026#34;Duration\u0026#34; Type=\u0026#34;Duration\u0026#34; Value=\u0026#34;PT0S\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;Activity Id=\u0026#34;frameProduction\u0026#34; Name=\u0026#34;Production frame\u0026#34; Type=\u0026#34;Series\u0026#34;\u0026gt; \u0026lt;Action Id=\u0026#34;produce\u0026#34; Name=\u0026#34;Production frame\u0026#34; ResourceId=\u0026#34;frameProduction\u0026#34; ResourceType=\u0026#34;Machine\u0026#34; Type=\u0026#34;Use\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;objectives\u0026#34; Name=\u0026#34;Production goals\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;duration\u0026#34; Name=\u0026#34;Duration\u0026#34; Type=\u0026#34;Duration\u0026#34; Value=\u0026#34;PT5M\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;Action Id=\u0026#34;toStock\u0026#34; Name=\u0026#34;Frame ToStock\u0026#34; ResourceId=\u0026#34;frame\u0026#34; ResourceType=\u0026#34;Article\u0026#34; Type=\u0026#34;Produce\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;objectives\u0026#34; Name=\u0026#34;Production goals\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;duration\u0026#34; Name=\u0026#34;Duration\u0026#34; Type=\u0026#34;Duration\u0026#34; Value=\u0026#34;PT1M\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;/Activity\u0026gt; \u0026lt;Activity Id=\u0026#34;brakeProduction\u0026#34; Type=\u0026#34;Series\u0026#34; Name=\u0026#34;Herstellen Bremsen\u0026#34; TimeOrdering=\u0026#34;Series\u0026#34;\u0026gt; \u0026lt;Action Id=\u0026#34;produce\u0026#34; Name=\u0026#34;Production saddle\u0026#34; ResourceId=\u0026#34;saddleProduction\u0026#34; ResourceType=\u0026#34;Machine\u0026#34; Type=\u0026#34;Use\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;objectives\u0026#34; Name=\u0026#34;Production goals\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;duration\u0026#34; Name=\u0026#34;Duration\u0026#34; Type=\u0026#34;Duration\u0026#34; Value=\u0026#34;PT5M\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;Action Id=\u0026#34;toStock\u0026#34; Name=\u0026#34;Saddle ToStock\u0026#34; ResourceId=\u0026#34;frame\u0026#34; ResourceType=\u0026#34;Article\u0026#34; Type=\u0026#34;Produce\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;objectives\u0026#34; Name=\u0026#34;Production goals\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;duration\u0026#34; Name=\u0026#34;Duration\u0026#34; Type=\u0026#34;Duration\u0026#34; Value=\u0026#34;PT1M\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;/Activity\u0026gt; \u0026lt;/Activity\u0026gt; \u0026lt;Action Id=\u0026#34;assembly\u0026#34; Name=\u0026#34;Bicycle assemble\u0026#34; ResourceId=\u0026#34;bicycleAssembly\u0026#34; ResourceType=\u0026#34;Assembly\u0026#34; Type=\u0026#34;Use\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;objectives\u0026#34; Name=\u0026#34;Production goals\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;duration\u0026#34; Name=\u0026#34;Duration\u0026#34; Type=\u0026#34;Duration\u0026#34; Value=\u0026#34;PT5M\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;Action Id=\u0026#34;toStock\u0026#34; Name=\u0026#34;Bicycle to stock\u0026#34; ResourceId=\u0026#34;bicycle\u0026#34; ResourceType=\u0026#34;Product\u0026#34; Type=\u0026#34;Produce\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;objectives\u0026#34; Name=\u0026#34;Production goals\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;1\u0026#34; /\u0026gt; \u0026lt;Parameter Id=\u0026#34;duration\u0026#34; Name=\u0026#34;Duration\u0026#34; Type=\u0026#34;Duration\u0026#34; Value=\u0026#34;PT1M\u0026#34; /\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;/Activity\u0026gt; Realms Strolch realms implement the multi-client capability which is thus baked right into the Strolch runtime. When configuring a Strolch runtime, realms are configured and for each realm the data store mode is set. Each realm has its own persistence configuration and can thus run in one of the 4 modes that the Strolch agent implements:\n EMPTY This is a transient data store mode, where no model changes are persisted, but they are only kept in memory. When the Strolch agent is started, this realm stays empty as no data is loaded. TRANSIENT This is the same as EMPTY, but with the difference that when the Strolch agent is started, an XML file is parsed and the in memory realm is populated with the elements parsed from that XML file. CACHED In this mode, all data is stored in memory, and any changes made are written back to the persistence layer. This allows for fast in-memory quries, but makes sure no data is lost when the agent is restarted. Strolch Realms are also responsible for opening Transactions, as these are bound to the persistence layer configured for this realm. At runtime, a realm is then accessed from the ComponentContainer:\nComponentContainer container = getAgent().getContainer(); StrolchRealm realm = container.getRealm(StrolchConstants.DEFAULT_REALM); try(StrolchTransaction tx = realm.openTx()) { Resource resource = tx.getResourceBy(\u0026#34;TestType\u0026#34;, \u0026#34;MyTestResource\u0026#34;); ... } "},{"uri":"https://strolch.li/documentation/architecture/","title":"Architecture","tags":[],"description":"","content":"Architecture Birds View A Strolch agent\u0026rsquo;s architecture can be seen as a simple three-tier architecture. The presentation layer is mostly a web frontend, where the communication with the agent is done via REST API calls.\nThe agent itself implements the business logic using Services, Commands, Queries etc.\nThe agent can communicate with other 3rd systems using any API, where it is preferred to use JSON over REST.\nThe agent can use a standard RDBMS as a storage system, where currently DAOs have been implemented only for PostgreSQL. Should it be required, then any JDBC cabable RDBMS can be used, as no PostgreSQL specific SQL commands are used.\nThe following diagram helps visualize this:\nSquirrel View The following diagram shows a more detailed view of the architecture of a Strolch Agent:\nA Strolch agent consists of the following main parts:\n REST Endpoints → expose an API to access the Strolch agent outside of the Java VM Services and Commands → implements business logic Searches → implements specific queries against the Strolch model Components → Implements additional logic, which is best implement as a component. E.g. active components which have threads, etc. Realms → implements multi-tenant capabilities In addition to the main parts, Strolch contains inherit capabilities, which gives Strolch unique features when compared to other Java Frameworks:\n Policies → Policies allow injecting different algorithms. All root elements can store Policy definitions, so that a service can delegate to a Policy and thus behave differently, depending on the element being accessed. Transactions → Transactions handle locking/unlocking of objects, updating the model and are the central API for the developer. Privileges → Strolch is user agnostic and any action, i.e. Service, Query, etc. is authorized against the authenticated user. Observers → modifications to the model are propagated to listeners using the observer pattern. Audits → Every access (read, modify) of the model are audited Versioning → modifications to objects are versioned and thus can be rolled back at a later time. "},{"uri":"https://strolch.li/tutorial/configuration/","title":"Configuration","tags":[],"description":"","content":"Configuration Let\u0026rsquo;s start by creating a new Apache Maven project. We\u0026rsquo;ll need a POM with the proper dependencies. We expect you to be familiar with Apache Maven, so we\u0026rsquo;ll just show you a working POM file:\npom.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34;?\u0026gt; \u0026lt;project xmlns=\u0026#34;http://maven.apache.org/POM/4.0.0\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\u0026#34;\u0026gt; \u0026lt;modelVersion\u0026gt;4.0.0\u0026lt;/modelVersion\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;strolch-bookshop\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;0.1.0-SNAPSHOT\u0026lt;/version\u0026gt; \u0026lt;packaging\u0026gt;war\u0026lt;/packaging\u0026gt; \u0026lt;name\u0026gt;strolch-bookshop\u0026lt;/name\u0026gt; \u0026lt;description\u0026gt;Bookshop built on Strolch\u0026lt;/description\u0026gt; \u0026lt;inceptionYear\u0026gt;2017\u0026lt;/inceptionYear\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;project.build.sourceEncoding\u0026gt;UTF-8\u0026lt;/project.build.sourceEncoding\u0026gt; \u0026lt;maven.build.timestamp.format\u0026gt;yyyy-MM-dd HH:mm:ss\u0026lt;/maven.build.timestamp.format\u0026gt; \u0026lt;buildTimestamp\u0026gt;${maven.build.timestamp}\u0026lt;/buildTimestamp\u0026gt; \u0026lt;jdk.version\u0026gt;1.8\u0026lt;/jdk.version\u0026gt; \u0026lt;jersey.version\u0026gt;2.25.1\u0026lt;/jersey.version\u0026gt; \u0026lt;slf4j.version\u0026gt;1.7.25\u0026lt;/slf4j.version\u0026gt; \u0026lt;logback.version\u0026gt;1.2.3\u0026lt;/logback.version\u0026gt; \u0026lt;petitparser.version\u0026gt;2.1.0\u0026lt;/petitparser.version\u0026gt; \u0026lt;hikaricp.version\u0026gt;2.7.1\u0026lt;/hikaricp.version\u0026gt; \u0026lt;postgresql.version\u0026gt;42.1.4\u0026lt;/postgresql.version\u0026gt; \u0026lt;gson.version\u0026gt;2.8.2\u0026lt;/gson.version\u0026gt; \u0026lt;annotation.version\u0026gt;1.3.1\u0026lt;/annotation.version\u0026gt; \u0026lt;javaxmail.version\u0026gt;1.6.0\u0026lt;/javaxmail.version\u0026gt; \u0026lt;serverlet.version\u0026gt;3.1.0\u0026lt;/serverlet.version\u0026gt; \u0026lt;jaxrs.api.version\u0026gt;2.1\u0026lt;/jaxrs.api.version\u0026gt; \u0026lt;junit.version\u0026gt;4.12\u0026lt;/junit.version\u0026gt; \u0026lt;hamcrest.version\u0026gt;1.3\u0026lt;/hamcrest.version\u0026gt; \u0026lt;mockito.version\u0026gt;2.0.8-beta\u0026lt;/mockito.version\u0026gt; \u0026lt;maven-compiler-plugin.version\u0026gt;3.7.0\u0026lt;/maven-compiler-plugin.version\u0026gt; \u0026lt;maven-source-plugin.version\u0026gt;3.0.1\u0026lt;/maven-source-plugin.version\u0026gt; \u0026lt;maven-jar-plugin.version\u0026gt;3.0.2\u0026lt;/maven-jar-plugin.version\u0026gt; \u0026lt;maven-war-plugin.version\u0026gt;3.1.0\u0026lt;/maven-war-plugin.version\u0026gt; \u0026lt;strolch.version\u0026gt;1.6.0-SNAPSHOT\u0026lt;/strolch.version\u0026gt; \u0026lt;warFinalName\u0026gt;bookshop\u0026lt;/warFinalName\u0026gt; \u0026lt;m2eclipse.wtp.contextRoot\u0026gt;${warFinalName}\u0026lt;/m2eclipse.wtp.contextRoot\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;dependencies\u0026gt; \u0026lt;!-- base --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.slf4j\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;slf4j-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${slf4j.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;ch.qos.logback\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;logback-classic\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${logback.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;runtime\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- strolch --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.utils\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.privilege\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.model\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.agent\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.rest\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.service\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.testbase\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;test\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- utils --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;com.google.code.gson\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;gson\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${gson.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- web --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.servlet\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;javax.servlet-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${serverlet.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;provided\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;javax.ws.rs\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;javax.ws.rs-api\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${jaxrs.api.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.glassfish.jersey.core\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jersey-common\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${jersey.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.glassfish.jersey.core\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jersey-server\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${jersey.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.glassfish.jersey.containers\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;jersey-container-servlet\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${jersey.version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;!-- testing --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;junit\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;junit\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${junit.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;test\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.hamcrest\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;hamcrest-core\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${hamcrest.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;test\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.hamcrest\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;hamcrest-library\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${hamcrest.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;test\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;/dependencies\u0026gt; \u0026lt;build\u0026gt; \u0026lt;resources\u0026gt; \u0026lt;!-- filter properties files, and copy the rest --\u0026gt; \u0026lt;resource\u0026gt; \u0026lt;directory\u0026gt;src/main/resources\u0026lt;/directory\u0026gt; \u0026lt;filtering\u0026gt;true\u0026lt;/filtering\u0026gt; \u0026lt;includes\u0026gt; \u0026lt;include\u0026gt;**/*.properties\u0026lt;/include\u0026gt; \u0026lt;/includes\u0026gt; \u0026lt;/resource\u0026gt; \u0026lt;resource\u0026gt; \u0026lt;directory\u0026gt;src/main/resources\u0026lt;/directory\u0026gt; \u0026lt;filtering\u0026gt;false\u0026lt;/filtering\u0026gt; \u0026lt;excludes\u0026gt; \u0026lt;exclude\u0026gt;**/*.properties\u0026lt;/exclude\u0026gt; \u0026lt;/excludes\u0026gt; \u0026lt;/resource\u0026gt; \u0026lt;/resources\u0026gt; \u0026lt;plugins\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.apache.maven.plugins\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;maven-compiler-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${maven-compiler-plugin.version}\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;source\u0026gt;${jdk.version}\u0026lt;/source\u0026gt; \u0026lt;target\u0026gt;${jdk.version}\u0026lt;/target\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.apache.maven.plugins\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;maven-war-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${maven-war-plugin.version}\u0026lt;/version\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;failOnMissingWebXml\u0026gt;false\u0026lt;/failOnMissingWebXml\u0026gt; \u0026lt;warName\u0026gt;${warFinalName}\u0026lt;/warName\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;/plugins\u0026gt; \u0026lt;/build\u0026gt; \u0026lt;profiles\u0026gt; \u0026lt;!-- active when building on eitch\u0026#39;s machines --\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;m2e.eitchpc\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;property\u0026gt; \u0026lt;name\u0026gt;user.name\u0026lt;/name\u0026gt; \u0026lt;value\u0026gt;eitch\u0026lt;/value\u0026gt; \u0026lt;/property\u0026gt; \u0026lt;os\u0026gt; \u0026lt;family\u0026gt;unix\u0026lt;/family\u0026gt; \u0026lt;/os\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;strolch.env\u0026gt;dev.eitchpc\u0026lt;/strolch.env\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; \u0026lt;/profiles\u0026gt; \u0026lt;/project\u0026gt; Now we need the rest of the directory structure:\n../strolch-bookshop/ - src/main/java/ - li/strolch/bookshop/ - \u0026lt;!-- java classes --\u0026gt; - src/main/resources/ - ENV.properties - appVersion.properties - logback.xml - src/main/webapp/WEB-INF/ - StrolchBootstrap.xml - runtime - config/ - PrivilegeConfig.xml - PrivilegeRoles.xml - PrivilegeUsers.xml - StrolchConfiguration.xml - StrolchPolicies.xml - data/ - StrolchModel.xsd - defaultModel.xml - templates.xml - temp/ A few notes to the resource files:\n The ENV.properties file is filtered by maven and the environment to load is written in it using the environment variable strolch.env. The appVersion.properties file is also filtered by maven and allows to reflect on the version of this app at runtime. The logback.xml file configures logging using SLF4j and Logback. The StrolchBootstrap.xml file is used to configure Strolch\u0026rsquo;s environment and root directory. For a webapp it can be annoying to store Strolch\u0026rsquo;s configuration inside the webapp, which is why we can define an absolute path where the configuration is kept. In the following example we keep it in the root of the sources:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;StrolchBootstrap\u0026gt; \u0026lt;env id=\u0026#34;dev.eitchpc\u0026#34; default=\u0026#34;true\u0026#34;\u0026gt; \u0026lt;root\u0026gt;/home/eitch/src/git/strolch-bookshop/runtime\u0026lt;/root\u0026gt; \u0026lt;environment\u0026gt;dev\u0026lt;/environment\u0026gt; \u0026lt;/env\u0026gt; \u0026lt;/StrolchBootstrap\u0026gt; Here we define two environments, but the both redefine the environment to dev. This is because we want this app to start on two different machines with different user home directories. See the profiles in the POM as to how these environments are activated using a environment property strolch.env.\nIn this next step we\u0026rsquo;ll create Strolch\u0026rsquo;s configuration at the location we defined in the StrolchBootstrap.xml file. Strolch\u0026rsquo;s configuration contains of three directories: config, data and temp. config contains static files which usually aren\u0026rsquo;t changed, data contains model files in XML format and temp is used at runtime for any temporary files, e.g. storing active sessions.\nThe configuration as well as the model has been described on Strolch\u0026rsquo;s documentation web page, we\u0026rsquo;ll just provide you with the files for the bookshop:\nPrivilegeConfig.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;Privilege\u0026gt; \u0026lt;Container\u0026gt; \u0026lt;Parameters\u0026gt; \u0026lt;!-- parameters for the container itself --\u0026gt; \u0026lt;Parameter name=\u0026#34;secretKey\u0026#34; value=\u0026#34;45f251ce-d51f-4624-990a-8dcd5b181f0e\u0026#34;/\u0026gt; \u0026lt;Parameter name=\u0026#34;secretSalt\u0026#34; value=\u0026#34;4770a32d-1512-4891-9a63-362504932500\u0026#34;/\u0026gt; \u0026lt;Parameter name=\u0026#34;persistSessions\u0026#34; value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;Parameter name=\u0026#34;autoPersistOnUserChangesData\u0026#34; value=\u0026#34;false\u0026#34;/\u0026gt; \u0026lt;Parameter name=\u0026#34;privilegeConflictResolution\u0026#34; value=\u0026#34;MERGE\u0026#34;/\u0026gt; \u0026lt;/Parameters\u0026gt; \u0026lt;EncryptionHandler class=\u0026#34;li.strolch.privilege.handler.DefaultEncryptionHandler\u0026#34;\u0026gt; \u0026lt;Parameters\u0026gt; \u0026lt;Parameter name=\u0026#34;hashAlgorithm\u0026#34; value=\u0026#34;PBKDF2WithHmacSHA512\u0026#34;/\u0026gt; \u0026lt;Parameter name=\u0026#34;hashIterations\u0026#34; value=\u0026#34;10000\u0026#34;/\u0026gt; \u0026lt;Parameter name=\u0026#34;hashKeyLength\u0026#34; value=\u0026#34;256\u0026#34;/\u0026gt; \u0026lt;/Parameters\u0026gt; \u0026lt;/EncryptionHandler\u0026gt; \u0026lt;PersistenceHandler class=\u0026#34;li.strolch.privilege.handler.XmlPersistenceHandler\u0026#34;\u0026gt; \u0026lt;Parameters\u0026gt; \u0026lt;Parameter name=\u0026#34;usersXmlFile\u0026#34; value=\u0026#34;PrivilegeUsers.xml\u0026#34;/\u0026gt; \u0026lt;Parameter name=\u0026#34;rolesXmlFile\u0026#34; value=\u0026#34;PrivilegeRoles.xml\u0026#34;/\u0026gt; \u0026lt;/Parameters\u0026gt; \u0026lt;/PersistenceHandler\u0026gt; \u0026lt;UserChallengeHandler class=\u0026#34;li.strolch.privilege.handler.MailUserChallengeHandler\u0026#34;\u0026gt; \u0026lt;/UserChallengeHandler\u0026gt; \u0026lt;/Container\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy name=\u0026#34;DefaultPrivilege\u0026#34; class=\u0026#34;li.strolch.privilege.policy.DefaultPrivilege\u0026#34;/\u0026gt; \u0026lt;Policy name=\u0026#34;RoleAccessPrivilege\u0026#34; class=\u0026#34;li.strolch.privilege.policy.RoleAccessPrivilege\u0026#34;/\u0026gt; \u0026lt;Policy name=\u0026#34;UserAccessPrivilege\u0026#34; class=\u0026#34;li.strolch.privilege.policy.UserAccessPrivilege\u0026#34;/\u0026gt; \u0026lt;Policy name=\u0026#34;UserSessionAccessPrivilege\u0026#34; class=\u0026#34;li.strolch.privilege.policy.UsernameFromCertificatePrivilege\u0026#34;/\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Privilege\u0026gt; PrivilegeRoles.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;Roles\u0026gt; \u0026lt;Role name=\u0026#34;User\u0026#34;\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.service.api.Service\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.search.StrolchSearch\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;Allow\u0026gt;li.strolch.bookshop.search.BookSearch\u0026lt;/Allow\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;/Role\u0026gt; \u0026lt;Role name=\u0026#34;UserPrivileges\u0026#34;\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeSetUserLocale\u0026#34; policy=\u0026#34;UserAccessPrivilege\u0026#34; /\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeSetUserPassword\u0026#34; policy=\u0026#34;UserAccessPrivilege\u0026#34; /\u0026gt; \u0026lt;/Role\u0026gt; \u0026lt;!-- Internal --\u0026gt; \u0026lt;Role name=\u0026#34;StrolchAdmin\u0026#34;\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.service.api.Service\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.search.StrolchSearch\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeAddUser\u0026#34; policy=\u0026#34;UserAccessPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeSetUserPassword\u0026#34; policy=\u0026#34;UserAccessPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;/Role\u0026gt; \u0026lt;Role name=\u0026#34;agent\u0026#34;\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.privilege.handler.SystemAction\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;Allow\u0026gt;li.strolch.runtime.privilege.StrolchSystemAction\u0026lt;/Allow\u0026gt; \u0026lt;Allow\u0026gt;li.strolch.runtime.privilege.StrolchSystemActionWithResult\u0026lt;/Allow\u0026gt; \u0026lt;Allow\u0026gt;li.strolch.persistence.postgresql.PostgreSqlSchemaInitializer\u0026lt;/Allow\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.service.api.Service\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.model.query.StrolchQuery\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.search.StrolchSearch\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;GetActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;AddActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;UpdateActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveResource\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveOrder\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;RemoveActivity\u0026#34; policy=\u0026#34;ModelPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeAction\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;Allow\u0026gt;Persist\u0026lt;/Allow\u0026gt; \u0026lt;Allow\u0026gt;PersistSessions\u0026lt;/Allow\u0026gt; \u0026lt;Allow\u0026gt;GetCertificates\u0026lt;/Allow\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeAddUser\u0026#34; policy=\u0026#34;UserAccessPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeModifyUser\u0026#34; policy=\u0026#34;UserAccessPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;PrivilegeGetUser\u0026#34; policy=\u0026#34;UserAccessPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;/Role\u0026gt; \u0026lt;/Roles\u0026gt; PrivilegeUsers.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;Users\u0026gt; \u0026lt;User userId=\u0026#34;U10\u0026#34; username=\u0026#34;jill\u0026#34; password=\u0026#34;8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918\u0026#34;\u0026gt; \u0026lt;Firstname\u0026gt;Jill\u0026lt;/Firstname\u0026gt; \u0026lt;Lastname\u0026gt;Someone\u0026lt;/Lastname\u0026gt; \u0026lt;State\u0026gt;ENABLED\u0026lt;/State\u0026gt; \u0026lt;Locale\u0026gt;en-GB\u0026lt;/Locale\u0026gt; \u0026lt;Roles\u0026gt; \u0026lt;Role\u0026gt;User\u0026lt;/Role\u0026gt; \u0026lt;Role\u0026gt;UserPrivileges\u0026lt;/Role\u0026gt; \u0026lt;/Roles\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;Property name=\u0026#34;email\u0026#34; value=\u0026#34;eitch+jill@eitchnet.ch\u0026#34; /\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/User\u0026gt; \u0026lt;User userId=\u0026#34;U01\u0026#34; username=\u0026#34;admin\u0026#34; password=\u0026#34;8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918\u0026#34;\u0026gt; \u0026lt;Firstname\u0026gt;Jill\u0026lt;/Firstname\u0026gt; \u0026lt;Lastname\u0026gt;Someone\u0026lt;/Lastname\u0026gt; \u0026lt;State\u0026gt;ENABLED\u0026lt;/State\u0026gt; \u0026lt;Locale\u0026gt;en-GB\u0026lt;/Locale\u0026gt; \u0026lt;Roles\u0026gt; \u0026lt;Role\u0026gt;StrolchAdmin\u0026lt;/Role\u0026gt; \u0026lt;Role\u0026gt;UserPrivileges\u0026lt;/Role\u0026gt; \u0026lt;/Roles\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;Property name=\u0026#34;email\u0026#34; value=\u0026#34;eitch+admin@eitchnet.ch\u0026#34; /\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/User\u0026gt; \u0026lt;!-- Internal --\u0026gt; \u0026lt;User userId=\u0026#34;S01\u0026#34; username=\u0026#34;agent\u0026#34;\u0026gt; \u0026lt;State\u0026gt;SYSTEM\u0026lt;/State\u0026gt; \u0026lt;Roles\u0026gt; \u0026lt;Role\u0026gt;agent\u0026lt;/Role\u0026gt; \u0026lt;/Roles\u0026gt; \u0026lt;/User\u0026gt; \u0026lt;/Users\u0026gt; StrolchConfiguration.xml\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;global\u0026#34;\u0026gt; \u0026lt;Runtime\u0026gt; \u0026lt;applicationName\u0026gt;Bookshop\u0026lt;/applicationName\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;locale\u0026gt;en\u0026lt;/locale\u0026gt; \u0026lt;verbose\u0026gt;true\u0026lt;/verbose\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Runtime\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;PrivilegeHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.runtime.privilege.PrivilegeHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.runtime.privilege.DefaultStrolchPrivilegeHandler\u0026lt;/impl\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;privilegeConfigFile\u0026gt;PrivilegeConfig.xml\u0026lt;/privilegeConfigFile\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;RealmHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.agent.api.RealmHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.agent.impl.DefaultRealmHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;realms\u0026gt;defaultRealm\u0026lt;/realms\u0026gt; \u0026lt;dataStoreMode\u0026gt;TRANSIENT\u0026lt;/dataStoreMode\u0026gt; \u0026lt;dataStoreFile\u0026gt;defaultModel.xml\u0026lt;/dataStoreFile\u0026gt; \u0026lt;enableObserverUpdates\u0026gt;true\u0026lt;/enableObserverUpdates\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;ServiceHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.service.api.ServiceHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.service.api.DefaultServiceHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;RealmHandler\u0026lt;/depends\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;verbose\u0026gt;true\u0026lt;/verbose\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;PolicyHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.policy.PolicyHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.policy.DefaultPolicyHandler\u0026lt;/impl\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;readPolicyFile\u0026gt;true\u0026lt;/readPolicyFile\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;ExecutionHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.execution.ExecutionHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.execution.EventBasedExecutionHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;RealmHandler\u0026lt;/depends\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;RestfulHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.rest.RestfulStrolchComponent\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.rest.RestfulStrolchComponent\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;SessionHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;secureCookie\u0026gt;false\u0026lt;/secureCookie\u0026gt; \u0026lt;restLogging\u0026gt;false\u0026lt;/restLogging\u0026gt; \u0026lt;restLoggingEntity\u0026gt;false\u0026lt;/restLoggingEntity\u0026gt; \u0026lt;restTracing\u0026gt;ALL\u0026lt;/restTracing\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;SessionHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.rest.StrolchSessionHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.rest.DefaultStrolchSessionHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;session.ttl.minutes\u0026gt;30\u0026lt;/session.ttl.minutes\u0026gt; \u0026lt;session.reload\u0026gt;true\u0026lt;/session.reload\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;MailHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.handler.mail.MailHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.handler.mail.SmtpMailHandler\u0026lt;/impl\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;fromAddr\u0026gt;relayer@eitchnet.ch\u0026lt;/fromAddr\u0026gt; \u0026lt;fromName\u0026gt;Susi\u0026lt;/fromName\u0026gt; \u0026lt;overrideRecipients\u0026gt;\u0026lt;![CDATA[IPSC Test \u0026lt;eitch@eitchnet.ch\u0026gt;]]\u0026gt;\u0026lt;/overrideRecipients\u0026gt; \u0026lt;recipientWhitelist\u0026gt;eitch@eitchnet.ch\u0026lt;/recipientWhitelist\u0026gt; \u0026lt;username\u0026gt;test\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;test\u0026lt;/password\u0026gt; \u0026lt;auth\u0026gt;true\u0026lt;/auth\u0026gt; \u0026lt;startTls\u0026gt;true\u0026lt;/startTls\u0026gt; \u0026lt;host\u0026gt;smtp.gmail.com\u0026lt;/host\u0026gt; \u0026lt;port\u0026gt;587\u0026lt;/port\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;/env\u0026gt; \u0026lt;env id=\u0026#34;dev\u0026#34;\u0026gt; \u0026lt;!-- overrides go here --\u0026gt; \u0026lt;/env\u0026gt; \u0026lt;/StrolchConfiguration\u0026gt; StrolchPolicies.xml\n\u0026lt;StrolchPolicies\u0026gt; \u0026lt;PolicyType Type=\u0026#34;ExecutionPolicy\u0026#34; Api=\u0026#34;li.strolch.execution.policy.ExecutionPolicy\u0026#34;\u0026gt; \u0026lt;Policy Key=\u0026#34;DurationExecution\u0026#34; Class=\u0026#34;li.strolch.execution.policy.DurationExecution\u0026#34; /\u0026gt; \u0026lt;Policy Key=\u0026#34;ReservationExection\u0026#34; Class=\u0026#34;li.strolch.execution.policy.ReservationExecution\u0026#34; /\u0026gt; \u0026lt;/PolicyType\u0026gt; \u0026lt;PolicyType Type=\u0026#34;ConfirmationPolicy\u0026#34; Api=\u0026#34;li.strolch.execution.policy.ConfirmationPolicy\u0026#34;\u0026gt; \u0026lt;Policy Key=\u0026#34;DefaultConfirmation\u0026#34; Class=\u0026#34;li.strolch.execution.policy.ConfirmationPolicy\u0026#34; /\u0026gt; \u0026lt;/PolicyType\u0026gt; \u0026lt;PolicyType Type=\u0026#34;ActivityArchivalPolicy\u0026#34; Api=\u0026#34;li.strolch.execution.policy.ActivityArchivalPolicy\u0026#34;\u0026gt; \u0026lt;Policy Key=\u0026#34;DefaultActivityArchival\u0026#34; Class=\u0026#34;li.strolch.execution.policy.ActivityArchivalPolicy\u0026#34; /\u0026gt; \u0026lt;/PolicyType\u0026gt; \u0026lt;/StrolchPolicies\u0026gt; A few notes on the configuration:\n Note how there are three users. Jill is a user with currently no privileges as it\u0026rsquo;s role definition is empty. Admin can do everything, and the agent user is a system user which can also do everything.\n There is one realm defined in the RealmHandler component which references the defaultModel.xml file in the data directory. This file then includes the currently still empty templates.xml file.\n We have defined a global environment, but are using the dev environment. The dev environment includes the definitions in the global environment.\n In PrivilegeConfig.xml we have enabled persistence of sessions, so you will be needing the unlimited JCE libraries for your JVM.\nWhen you restart the server, you don\u0026rsquo;t need to log back in, if your session is still alive.\n In PrivilegeRoles.xml there seems to be a lot of boilerplate. One thing about a highly configurable system is that sometimes the configuration is bigger. In this case we have opted to have the configuration shown and not use default values which you don\u0026rsquo;t see, so that privilege access is clearly seen.\n Your project is now ready to be imported into your favourite IDE. We have used both IntelliJ and Eclipse so this is up to you.\nNow that we have a configuration, it is time to have Strolch started when the WAR is deployed and started. In your IDE create a new class as follows:\nStartupListener.java\npackage li.strolch.bookshop.web; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; import java.io.InputStream; import li.strolch.agent.api.StrolchAgent; import li.strolch.agent.api.StrolchBootstrapper; import li.strolch.utils.helper.StringHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @WebListener public class StartupListener implements ServletContextListener { private static final Logger logger = LoggerFactory.getLogger(StartupListener.class); private static final String APP_NAME = \u0026#34;Bookshop\u0026#34;; private StrolchAgent agent; @Override public void contextInitialized(ServletContextEvent sce) { logger.info(\u0026#34;Starting \u0026#34; + APP_NAME + \u0026#34;...\u0026#34;); long start = System.currentTimeMillis(); try { String boostrapFileName = \u0026#34;/WEB-INF/\u0026#34; + StrolchBootstrapper.FILE_BOOTSTRAP; InputStream bootstrapFile = sce.getServletContext().getResourceAsStream(boostrapFileName); StrolchBootstrapper bootstrapper = new StrolchBootstrapper(StartupListener.class); this.agent = bootstrapper.setupByBoostrapFile(StartupListener.class, bootstrapFile); this.agent.initialize(); this.agent.start(); } catch (Throwable e) { logger.error(\u0026#34;Failed to start \u0026#34; + APP_NAME + \u0026#34; due to: \u0026#34; + e.getMessage(), e); throw e; } long took = System.currentTimeMillis() - start; logger.info(\u0026#34;Started \u0026#34; + APP_NAME + \u0026#34; in \u0026#34; + (StringHelper.formatMillisecondsDuration(took))); } @Override public void contextDestroyed(ServletContextEvent sce) { if (this.agent != null) { logger.info(\u0026#34;Destroying \u0026#34; + APP_NAME + \u0026#34;...\u0026#34;); try { this.agent.stop(); this.agent.destroy(); } catch (Throwable e) { logger.error(\u0026#34;Failed to stop \u0026#34; + APP_NAME + \u0026#34; due to: \u0026#34; + e.getMessage(), e); throw e; } } logger.info(\u0026#34;Destroyed \u0026#34; + APP_NAME); } } Now configure your IDE to start the web project, and then once it has started, you should see the following in the logs:\nBookshop:dev All 8 Strolch Components started. Took 44ms. Strolch is now ready to be used. Have fun =)) This log tells us the name of the app as defined in the StrolchConfiguration.xml file as well as which environment was loaded. Further we can see that 8 components were configured and started.\nThis concludes the initial setup of a new Strolch project. We can now go ahead and start building the business logic.\n"},{"uri":"https://strolch.li/history/","title":"History","tags":[],"description":"","content":"Overview Strolch is an open source component based software agent written in Java and can be compared, in a light sense, with the Java EE stack: Strolch takes care of persistence, implements Services for use cases, Commands as re-usable algorithms and has a parameterized data model.\nStrolch has an intrinsic understanding for mandates, which are called realms so that a single agent can be used to implement applications with multiple users/customers for instance in SaaS environments.\nThe parameterized data model consists of three top level objects, Resources, Orders and Activities. These objects can have any number of ParameterBags which in turn can have any number of Parameters on them. This allows for a very dynamic modelling of data structures including modification at run time. Multiple ready to use Parameter types are already implemented which handle the primitive types in Java including ListParameters for collections of these primitive types.\nOne of the main features of the Strolch agent, is that persistence is handled transparently and the user must not be worried about databases and the likes. Currently there are two implementations for persisting the Strolch model, a PostgreSQL and an XML file persistence. Currently both persistence layers persist the data by converting to XML and storing it into the database. The XML file persistence stores each object in its own file.\nThe agent itself has a small memory footprint and requires very few components to start. For the agent to be useful it needs additional functionality which is implemented in StrolchComponents. Each component is registered via its Java interface on the agent and is bound to the life cycle of the agent. When the agent is started, these components can be retrieved and used to perform any number of functionalities. This is the preferred way to extend the Strolch agent. There are a number of components already implemented, e.g. the ServiceHandler which executes Services in a controlled fashion and can validate authorized access to these services.\nNo software product is complete without a system for authentication and authorization. Strolch implements this by using the Privilege framework which has been written by Robert von Burg. The standard ServiceHandler detects the existence of the PrivilegeHandler and then validates that the user has authorization to perform the service. This framework is implemented as its own Strolch component, thus can be retrieved at any time during execution to perform fine grained and special authorization validation.\nMotivation A question often asked is why create Strolch. What are its benefits in contrast to using Java SE with an OR-Mapper like Hibernate, or using Java EE on JBoss or Glassfish? Especially since many of the features existing in those stacks needed to be re-created in Strolch.\nThe first answer to this question is that those systems are often overly complicated and bloated. Java SE with Hibernate certainly is a viable option when it comes to being light-weightier but Hibernate, even though it is supposed to, often fails to truly help remove the need to really understand an RDBMS. Often enough Hibernate will just get in the way of the most important part of development: writing the business code. Being an OR-Mapper which is supposed to implement all the nitty-gritty details of an RDBMS system, Hibernate, and JPA for that matter, still often has the developer go back to understanding these details.\nStrolch tries a different approach to persistence. Instead of writing pojos/entities, Strolch\u0026rsquo;s model has the concept that each element\u0026rsquo;s attributes are part of a composition pattern: each attribute is its own object and thus can be dynamically changed at runtime, but also makes persistence of such an element generic. Instead of having fixed attributes for a concrete class, these parameters are stored in a map and are accessed through the parameter\u0026rsquo;s ID.\nAssigning an ID to an attribute for accessing of course brings its own downsides, i.e. the parameter might simply not be there, when being accessed. This is certainly an issue that the developer must handle, when implementing a project using Strolch, but allows the developer to not need to worry about persistence, as this is generically handled.\nSince the persistence is generically handled, and Strolch stays lightweight on its requirements at runtime, the developer can quickly get down to what is important for business value: Writing the business logic and the presentation layer. Here too Strolch tries to help the developer by bringing in concepts which are easy to follow: Use cases are implemented as Services, and re-usable business logic is put into Commands.\nThere will be reasons against using Strolch, as there will be against using the Java EE stack, or an OR-Mapper or even the Java ecosystem for that fact. Important is to note, that the concepts behind Strolch are nothing new, but have been implemented in at least two previous proprietary products. Since those products are not accessible to the public, it was decided that a re-implementation might be of use to the programming community at large.\nCurrently, there is at least one company using Strolch in a commercial project which helps drive Strolch\u0026rsquo;s development and further motivates its existence.\nStrolch is an open source project and licensed under the Apache License 2.0.\n##Technology Strolch is written in Java and is programmed against the JDK 8. Strolch runs on any JRE 8 compliant environment. Strolch is tested on the Oracle JRE 8.\n"},{"uri":"https://strolch.li/documentation/","title":"Documentation","tags":[],"description":"","content":"Overview Strolch\u0026rsquo;s documentation has only just begun, but as more and more details of the implementation in Strolch are fixed, more documentation can be created and will be available here.\nCurrently we have the following topics of discussion:\n Architecture Model Do and Don\u0026#39;t Runtime Configuration Realms Components Services and Commands Searches Queries Transactions Policies Observers Versioning Reports Privileges "},{"uri":"https://strolch.li/documentation/model/","title":"Model","tags":[],"description":"","content":"Model Before we dive into the entire model, let\u0026rsquo;s show an example and how it would be modelled in Strolch and use in Strolch:\nA possible model would look as follows:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34; ?\u0026gt; \u0026lt;StrolchModel xmlns=\u0026#34;https://strolch.li/xsd/StrolchModel-1.6.xsd\u0026#34;\u0026gt; \u0026lt;Resource Id=\u0026#34;Product\u0026#34; Name=\u0026#34;Product Template\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;description\u0026#34; Name=\u0026#34;Description\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;color\u0026#34; Name=\u0026#34;Color\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;form\u0026#34; Name=\u0026#34;Form\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;relations\u0026#34; Name=\u0026#34;Relations\u0026#34; Type=\u0026#34;Relations\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;articles\u0026#34; Name=\u0026#34;Articles\u0026#34; Type=\u0026#34;StringList\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Article\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Resource Id=\u0026#34;Article\u0026#34; Name=\u0026#34;Article Template\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;description\u0026#34; Name=\u0026#34;Description\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;barcode\u0026#34; Name=\u0026#34;Barcode\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;relations\u0026#34; Name=\u0026#34;Relations\u0026#34; Type=\u0026#34;Relations\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;product\u0026#34; Name=\u0026#34;Product\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Product\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Resource Id=\u0026#34;Customer\u0026#34; Name=\u0026#34;Customer Template\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;address\u0026#34; Name=\u0026#34;Address\u0026#34; Type=\u0026#34;Address\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;street\u0026#34; Name=\u0026#34;Street\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;zip\u0026#34; Name=\u0026#34;Zip\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;city\u0026#34; Name=\u0026#34;City\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;country\u0026#34; Name=\u0026#34;Country\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Order Id=\u0026#34;Order\u0026#34; Name=\u0026#34;Order\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;quantities\u0026#34; Name=\u0026#34;Quantities per Article Id\u0026#34; Type=\u0026#34;Quantities\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;0\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;relations\u0026#34; Name=\u0026#34;Relations\u0026#34; Type=\u0026#34;Relations\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;articles\u0026#34; Name=\u0026#34;Articles\u0026#34; Type=\u0026#34;StringList\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Article\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;customer\u0026#34; Name=\u0026#34;Customer\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Customer\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Order\u0026gt; \u0026lt;/StrolchModel\u0026gt; Let\u0026rsquo;s go through this model:\n In the above model we see that the id and name fields are always on the element, and thus aren\u0026rsquo;t added as parameters. Further most elements have a parameters ParameterBag, with one or more parameters, modelling the fields. Note that in this example the Type of all the elements is Template. Strolch has API support to create a clone of these elements, so that they have a unique ID, and the proper type for persistence. The Product element has three parameters: description, color and form. In this case they are all of type String. Further the relations ParameterBag defines the relationships, i.e. the product knows its articles. Note how the relation is first defined in a relations ParameterBag and that the Parameter has Interpretation=\u0026ldquo;Resource-Ref\u0026rdquo; Uom=\u0026ldquo;Product\u0026rdquo; attributes. Strolch has API support for this, making it trivial to retrieve a dependency. The Article element has two parameters description and barcode. Further it has a reference to its Product. The Order element doesn\u0026rsquo;t model the date and state fields as parameters, as these are inherently part of an Order element. The Order does define two references to customer and articles. A special case is the quantities ParameterBag. This bag of parameters is used to store the per article quantity for this order. With ParameterBags, you can eliminate the use of simple aggregate classes, as is commonly used in object-oriented programming. The Customer element models a address ParameterBag to store the address of a customer. Using a separate bag allows for further more direct fields to stored on the default parameters bag. Now that we have a basic understanding of te model, it is of far more interest in how to create and interact with these elements at runtime. The following listing will perform simple operations:\ntry (StrolchTransaction tx = runtimeMock.openUserTx(certificate, false)) { /* * create a new product */ Resource dafalgan = tx.getResourceTemplate(\u0026#34;Product\u0026#34;, true); dafalgan.setName(\u0026#34;Dafalgan 100mg\u0026#34;); dafalgan.getParameter(\u0026#34;description\u0026#34;, true).setValue(\u0026#34;Dafalgan is for pain.\u0026#34;); dafalgan.getParameter(\u0026#34;color\u0026#34;, true).setValue(\u0026#34;Yellow\u0026#34;); dafalgan.getParameter(\u0026#34;form\u0026#34;, true).setValue(\u0026#34;flat\u0026#34;); StringListParameter articlesP = dafalgan.getRelationsParam(\u0026#34;articles\u0026#34;, true); /* * create articles */ Resource dafalgan1 = tx.getResourceTemplate(\u0026#34;Article\u0026#34;, true); dafalgan1.setName(\u0026#34;Dafalgan 100mg 10 pce\u0026#34;); dafalgan1.getParameter(\u0026#34;description\u0026#34;, true).setValue(\u0026#34;This is pack with 10 pieces.\u0026#34;); dafalgan1.getParameter(\u0026#34;barcode\u0026#34;, true).setValue(\u0026#34;654654\u0026#34;); Resource dafalgan2 = tx.getResourceTemplate(\u0026#34;Article\u0026#34;, true); dafalgan2.setName(\u0026#34;Dafalgan 100mg 20 pce\u0026#34;); dafalgan2.getParameter(\u0026#34;description\u0026#34;, true).setValue(\u0026#34;This is pack with 20 pieces.\u0026#34;); dafalgan2.getParameter(\u0026#34;barcode\u0026#34;, true).setValue(\u0026#34;654655\u0026#34;); /* * add reference to product */ dafalgan1.getRelationParam(\u0026#34;product\u0026#34;).setValue(dafalgan.getId()); articlesP.addValue(dafalgan1.getId()); dafalgan2.getRelationParam(\u0026#34;product\u0026#34;).setValue(dafalgan.getId()); articlesP.addValue(dafalgan2.getId()); /* * create a new customer */ Resource customer1 = tx.getResourceTemplate(\u0026#34;Customer\u0026#34;, true); customer1.setName(\u0026#34;John Doe\u0026#34;); // set address ParameterBag addressBag = customer1.getParameterBag(\u0026#34;address\u0026#34;, true); addressBag.getParameter(\u0026#34;street\u0026#34;, true).setValue(\u0026#34;Main Str. 1\u0026#34;); addressBag.getParameter(\u0026#34;zip\u0026#34;, true).setValue(\u0026#34;1234\u0026#34;); addressBag.getParameter(\u0026#34;city\u0026#34;, true).setValue(\u0026#34;Hometown\u0026#34;); addressBag.getParameter(\u0026#34;country\u0026#34;, true).setValue(\u0026#34;Switzerland\u0026#34;); /* * create a new order */ Order order = tx.getOrderTemplate(\u0026#34;Order\u0026#34;, true); order.setName(\u0026#34;Order for \u0026#34; + customer1.getName()); order.setDate(LocalDate.of(2021, 2, 1)); order.setState(State.PLANNED); // store reference to customer order.getRelationParam(\u0026#34;customer\u0026#34;, true).setValue(customer1.getId()); StringListParameter orderArticlesP = order.getRelationsParam(\u0026#34;articles\u0026#34;, true); ParameterBag quantitiesBag = order.getParameterBag(\u0026#34;quantities\u0026#34;, true); FloatParameter quantityT = quantitiesBag.removeParameter(\u0026#34;quantity\u0026#34;); // order quantity of 20 for Dafalgan 1 FloatParameter q1P = quantityT.getClone(); q1P.setId(dafalgan1.getId()); q1P.setValue(20); quantitiesBag.addParameter(q1P); orderArticlesP.addValue(dafalgan1.getId()); // set order quantity of 10 for Dafalgan 2 FloatParameter q2P = quantityT.getClone(); orderArticlesP.addValue(dafalgan2.getId()); q2P.setId(dafalgan2.getId()); q2P.setValue(20); quantitiesBag.addParameter(q2P); // keep IDs for later use dafalganId = dafalgan.getId(); dafalgan1Id = dafalgan1.getId(); dafalgan2Id = dafalgan2.getId(); customerId = customer1.getId(); orderId = order.getId(); /* * persist */ tx.add(dafalgan); tx.add(dafalgan1); tx.add(dafalgan2); tx.add(customer1); tx.add(order); // commit tx.commitOnClose(); } try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true)) { // get order Order order = tx.getOrderBy(\u0026#34;Order\u0026#34;, orderId, true); assertNotNull(orderId); assertEquals(\u0026#34;Order for John Doe\u0026#34;, order.getName()); // get customer Resource customer = tx.getResourceByRelation(order, \u0026#34;customer\u0026#34;, true); assertNotNull(customer); assertEquals(\u0026#34;John Doe\u0026#34;, customer.getName()); // get articles List\u0026lt;Resource\u0026gt; articles = tx.getResourcesByRelation(order, \u0026#34;articles\u0026#34;, true); assertEquals(2, articles.size()); // get products List\u0026lt;Resource\u0026gt; products = articles.stream().map(a -\u0026gt; tx.getResourceByRelation(a, \u0026#34;product\u0026#34;, true)) .distinct().collect(Collectors.toList()); assertEquals(1, products.size()); // search for all orders in state PLANNED and with customer List\u0026lt;Order\u0026gt; orders = new OrderSearch().types(\u0026#34;Order\u0026#34;).stateIsIn(State.PLANNED) .where(ExpressionsSupport.relationParam(\u0026#34;customer\u0026#34;).isEqualTo(customerId)).search(tx).toList(); assertEquals(1, orders.size()); } Note: Checkout example-model.xml and SimpleModelTest.java for these examples.\n There is an XML Schema which defines the model in XML: StrolchModel-1.6.xsd\n Here is an example of all the possible elements in Strolch:\n\u0026lt;StrolchModel xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xmlns=\u0026#34;https://strolch.li/xsd/StrolchModel-1.6.xsd\u0026#34; xsi:schemaLocation=\u0026#34;https://strolch.li/xsd/StrolchModel-1.6.xsd StrolchModel-1.6.xsd\u0026#34;\u0026gt; \u0026lt;IncludeFile file=\u0026#34;Include1.xml\u0026#34;/\u0026gt; \u0026lt;Order Id=\u0026#34;@test1\u0026#34; Name=\u0026#34;Test Order\u0026#34; Type=\u0026#34;Order\u0026#34;\u0026gt; \u0026lt;Version Version=\u0026#34;0\u0026#34; CreatedBy=\u0026#34;test\u0026#34; CreatedAt=\u0026#34;2012-11-30T18:12:05.628+01:00\u0026#34; Deleted=\u0026#34;false\u0026#34;/\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;PlanningPolicy\u0026#34; Value=\u0026#34;key:SimplePlanning\u0026#34;/\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:NoConfirmation\u0026#34;/\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Order\u0026gt; \u0026lt;Resource Id=\u0026#34;MyTestResource\u0026#34; Name=\u0026#34;Test Name\u0026#34; Type=\u0026#34;TestType\u0026#34;\u0026gt; \u0026lt;Version Version=\u0026#34;0\u0026#34; CreatedBy=\u0026#34;test\u0026#34; CreatedAt=\u0026#34;2012-11-30T18:12:05.628+01:00\u0026#34; Deleted=\u0026#34;false\u0026#34;/\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag 01\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag02\u0026#34; Name=\u0026#34;Test Bag 02\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;TimedState Id=\u0026#34;@booleanState\u0026#34; Name=\u0026#34;Boolean State\u0026#34; Type=\u0026#34;Boolean\u0026#34;\u0026gt; \u0026lt;Value Time=\u0026#34;1970-01-01T00:02:00.000+01:00\u0026#34; Value=\u0026#34;false\u0026#34;/\u0026gt; \u0026lt;/TimedState\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;PlanningPolicy\u0026#34; Value=\u0026#34;key:SimplePlanning\u0026#34;/\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:NoConfirmation\u0026#34;/\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Activity Id=\u0026#34;activity_1\u0026#34; Name=\u0026#34;Activity\u0026#34; Type=\u0026#34;parentType\u0026#34; TimeOrdering=\u0026#34;Series\u0026#34;\u0026gt; \u0026lt;Version Version=\u0026#34;0\u0026#34; CreatedBy=\u0026#34;test\u0026#34; CreatedAt=\u0026#34;2012-11-30T18:12:05.628+01:00\u0026#34; Deleted=\u0026#34;false\u0026#34;/\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;PlanningPolicy\u0026#34; Value=\u0026#34;key:SimplePlanning\u0026#34;/\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:NoConfirmation\u0026#34;/\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;Action Id=\u0026#34;action_1\u0026#34; Name=\u0026#34;Action 1\u0026#34; ResourceId=\u0026#34;dummyId\u0026#34; ResourceType=\u0026#34;dummyType\u0026#34; State=\u0026#34;Created\u0026#34; Type=\u0026#34;Use\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;PlanningPolicy\u0026#34; Value=\u0026#34;key:SimplePlanning\u0026#34;/\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:NoConfirmation\u0026#34;/\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;ValueChange StateId=\u0026#34;dummyId\u0026#34; Time=\u0026#34;2012-11-30T18:12:05.628+01:00\u0026#34; Value=\u0026#34;5\u0026#34; Type=\u0026#34;Integer\u0026#34;/\u0026gt; \u0026lt;ValueChange StateId=\u0026#34;dummyId\u0026#34; Time=\u0026#34;2012-11-30T18:12:06.628+01:00\u0026#34; Value=\u0026#34;6\u0026#34; Type=\u0026#34;Integer\u0026#34;/\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;Activity Id=\u0026#34;child_activity\u0026#34; Name=\u0026#34;Child Activity\u0026#34; Type=\u0026#34;childType\u0026#34; TimeOrdering=\u0026#34;Series\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;@bag01\u0026#34; Name=\u0026#34;Test Bag\u0026#34; Type=\u0026#34;TestBag\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;@param1\u0026#34; Name=\u0026#34;Boolean Param\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;PlanningPolicy\u0026#34; Value=\u0026#34;key:SimplePlanning\u0026#34;/\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:NoConfirmation\u0026#34;/\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;Action Id=\u0026#34;action_2\u0026#34; Name=\u0026#34;Action 2\u0026#34; ResourceId=\u0026#34;dummyId\u0026#34; ResourceType=\u0026#34;dummyType\u0026#34; State=\u0026#34;Planned\u0026#34; Type=\u0026#34;Use\u0026#34;\u0026gt; \u0026lt;ValueChange StateId=\u0026#34;dummyId\u0026#34; Time=\u0026#34;2012-11-30T18:12:05.628+01:00\u0026#34; Value=\u0026#34;5\u0026#34; Type=\u0026#34;Integer\u0026#34;/\u0026gt; \u0026lt;ValueChange StateId=\u0026#34;dummyId\u0026#34; Time=\u0026#34;2012-11-30T18:12:06.628+01:00\u0026#34; Value=\u0026#34;6\u0026#34; Type=\u0026#34;Integer\u0026#34;/\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;Action Id=\u0026#34;action_3\u0026#34; Name=\u0026#34;Action 3\u0026#34; ResourceId=\u0026#34;dummyId\u0026#34; ResourceType=\u0026#34;dummyType\u0026#34; State=\u0026#34;Created\u0026#34; Type=\u0026#34;Use\u0026#34;/\u0026gt; \u0026lt;/Activity\u0026gt; \u0026lt;/Activity\u0026gt; \u0026lt;/StrolchModel\u0026gt; "},{"uri":"https://strolch.li/tutorial/model/","title":"Model","tags":[],"description":"","content":"Model Looking back at our functionality, we can list the following entities that need to be modelled (We\u0026rsquo;ll go into detail further down):\n Book books can be orderd UserCart we want to store the cart of the user Account we need to know where to send the orders PurchaseOrder we need to know what was ordered and keep track of its state FromStock we want to use activities to implement the process of an order In Strolch we model entities by defining the element as a template. Thus in the templates.xml file we can add the templates with the following content:\nBook\n\u0026lt;Resource Id=\u0026#34;Book\u0026#34; Name=\u0026#34;Book Template\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;description\u0026#34; Name=\u0026#34;Description\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity in Stock\u0026#34; Type=\u0026#34;Integer\u0026#34; Value=\u0026#34;0\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; Account\n\u0026lt;Resource Id=\u0026#34;Account\u0026#34; Name=\u0026#34;Account Template\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;user\u0026#34; Name=\u0026#34;User\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;firstName\u0026#34; Name=\u0026#34;First Name\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;lastName\u0026#34; Name=\u0026#34;Last Name\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;email\u0026#34; Name=\u0026#34;E-Mail\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Name=\u0026#34;Address\u0026#34; Id=\u0026#34;address\u0026#34; Type=\u0026#34;Address\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;phone\u0026#34; Name=\u0026#34;Telephone Number\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;street\u0026#34; Name=\u0026#34;Street\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;city\u0026#34; Name=\u0026#34;City\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;zip\u0026#34; Name=\u0026#34;Postal Code\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;country\u0026#34; Name=\u0026#34;Country\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; UserCart\n\u0026lt;Resource Id=\u0026#34;UserCart\u0026#34; Name=\u0026#34;UserCart Template\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;books\u0026#34; Name=\u0026#34;Books\u0026#34; Type=\u0026#34;Book\u0026#34;\u0026gt; \u0026lt;!-- Parameter Id=\u0026#34;bookId\u0026#34; Name=\u0026#34;Book reference\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;0\u0026#34; / --\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;relations\u0026#34; Name=\u0026#34;Relations\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;account\u0026#34; Name=\u0026#34;Account\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Account\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; PurchaseOrder\n\u0026lt;Order Id=\u0026#34;PurchaseOrder\u0026#34; Name=\u0026#34;PurchaseOrder Template\u0026#34; Type=\u0026#34;Template\u0026#34; State=\u0026#34;Created\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;books\u0026#34; Name=\u0026#34;Books\u0026#34; Type=\u0026#34;Book\u0026#34;\u0026gt; \u0026lt;!-- Parameter Id=\u0026#34;bookId\u0026#34; Name=\u0026#34;Book reference\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;0\u0026#34; / --\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;relations\u0026#34; Name=\u0026#34;Relations\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;account\u0026#34; Name=\u0026#34;Account\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Account\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Order\u0026gt; FromStock\n\u0026lt;Activity Id=\u0026#34;FromStock\u0026#34; Name=\u0026#34;From Stock Template\u0026#34; Type=\u0026#34;FromStock\u0026#34; TimeOrdering=\u0026#34;Series\u0026#34;\u0026gt; \u0026lt;ParameterBag Name=\u0026#34;objectives\u0026#34; Id=\u0026#34;Objectives\u0026#34; Type=\u0026#34;Objectives\u0026#34;\u0026gt; \u0026lt;Parameter Name=\u0026#34;Duration\u0026#34; Id=\u0026#34;duration\u0026#34; Value=\u0026#34;PT1MS\u0026#34; Type=\u0026#34;Duration\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;Action Id=\u0026#34;validate\u0026#34; Name=\u0026#34;Validation of order\u0026#34; Type=\u0026#34;Use\u0026#34; ResourceType=\u0026#34;Validation\u0026#34; ResourceId=\u0026#34;validation\u0026#34;/\u0026gt; \u0026lt;!-- for each book we do a consume, i.e. reduce the stock quantity --\u0026gt; \u0026lt;Action Id=\u0026#34;Consume\u0026#34; Name=\u0026#34;Consume Template for book\u0026#34; Type=\u0026#34;Template\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;quantity\u0026#34; Name=\u0026#34;Quantity\u0026#34; Type=\u0026#34;Float\u0026#34; Value=\u0026#34;0\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Action\u0026gt; \u0026lt;Action Id=\u0026#34;package\u0026#34; Name=\u0026#34;Packaging of PurchaseOrder\u0026#34; Type=\u0026#34;Use\u0026#34; ResourceType=\u0026#34;Packaging\u0026#34; ResourceId=\u0026#34;packaging\u0026#34;/\u0026gt; \u0026lt;Action Id=\u0026#34;send\u0026#34; Name=\u0026#34;Sending of package\u0026#34; Type=\u0026#34;Use\u0026#34; ResourceType=\u0026#34;Sending\u0026#34; ResourceId=\u0026#34;sending\u0026#34;/\u0026gt; \u0026lt;/Activity\u0026gt; Let\u0026rsquo;s explain a few things:\n The Book entity is a Resource object and only contains the description and the current quantity in stock.\n The Account entity is a Resource and contains the address and further details of the user, and with the user parameter the username is defined, thus referencing the real user.\n The UserCart entity is a Resource and has a reference to the account Resource.\nNote how the reference is done using a StringParameter, where Interpretation, UOM and the value is set in a specific manner.\n The UserCart entity is a Resource and references books using a special ParameterBag with the type set to Book, the actual type of the book entity. Each Parameter is of type Float and the ID of the parameter is the ID of the book, and the value is the quantity that the user would like to purchase.\nThere will only be one cart per user/account.\n The PurchaseOrder entity is an Order object, and is basically a copy of the UserCart entity. This is the confirmed purchase order for the contents of a cart, and can then be used for reports on how much of which book was sold.\n The FromStock entity is an Activity object and defines the process we will go through when delivering a purchase to a user.\nNote how the activity has a ParameterBag objectives with a duration parameter. This defines for this activity how long each Action should execute. This can be overridden in each Action and can help to plan how much effort goes into the delivering of each PurchaseOrder.\n Further note how the activity has three special actions (validate, package and send) on which a ResourceType and ResourceId are defined. Actions are always performed on a Resource, as the referenced Resource defines the behaviour of the action through defined Policy objects.\n For each book which will be purchased, an Action will be created of type Consume. In the template this is defined by a template Action with the id Consume and will later be changed accordingly.\n Since we are referencing resources from actions in the activity, we need to add these as well, but not as templates. They can be added to the defaultModel.xml file:\n\u0026lt;Resource Id=\u0026#34;validation\u0026#34; Name=\u0026#34;Validation Resource\u0026#34; Type=\u0026#34;Validation\u0026#34;\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;ExecutionPolicy\u0026#34; Value=\u0026#34;key:ValidationExecution\u0026#34; /\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:DefaultConfirmation\u0026#34; /\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Resource Id=\u0026#34;packaging\u0026#34; Name=\u0026#34;Packaging Resource\u0026#34; Type=\u0026#34;Packaging\u0026#34;\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;ExecutionPolicy\u0026#34; Value=\u0026#34;key:PackagingExecution\u0026#34; /\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:DefaultConfirmation\u0026#34; /\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Resource Id=\u0026#34;sending\u0026#34; Name=\u0026#34;Sending Resource\u0026#34; Type=\u0026#34;Sending\u0026#34;\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;ExecutionPolicy\u0026#34; Value=\u0026#34;key:SendingExecution\u0026#34; /\u0026gt; \u0026lt;Policy Type=\u0026#34;ConfirmationPolicy\u0026#34; Value=\u0026#34;key:DefaultConfirmation\u0026#34; /\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Resource\u0026gt; What should now be noted by these three new Resources is that they have Policy definitions:\n ExecutionPolicy defines how an action on this resource is executed by referencing an ExecutionPolicy implementation. ConfirmationPolicy defines behaviour to be performed on every state change of an action being performed on this resource by referencing an ConfirmationPolicy implementation. Currently these resources reference policies which don\u0026rsquo;t exist. We will resolve this issue later, when we implement the execution of the activity.\nThis concludes the model definition. In the next step we\u0026rsquo;ll start creating services and commands for our model.\n"},{"uri":"https://strolch.li/plc/","title":"PLC","tags":[],"description":"","content":"Overview Using Strolch as a PLC has certain advantages and disadvantages. The following is a list of advantages:\n Same programming model and language for server and PLC PLC has the same privilege handling as in Strolch Simulating down to the PLC level is easily possible for easier testing of server logic Of course using the Java language as a PLC has its limitations, we have manage to use it for customers and are satisfied with the result. What follows is a description in how to set up your own Strolch based PLC.\nCheckout the code at GitHub\nCurrently, we have the following topics of discussion:\n Architecture Example Set-Up "},{"uri":"https://strolch.li/tutorial/crud-book/","title":"CRUD Book","tags":[],"description":"","content":"Preparation Since Books are central to the bookshop, we\u0026rsquo;ll first create the CRUD REST API for them. The API will be as follows:\nGET ../rest/books?query=,offset=,limit= GET ../rest/books/{id} POST ../rest/books PUT ../rest/books/{id} DELETE ../rest/books/{id} Thus corresponding with querying, getting, creating, updating and removing of books. So let\u0026rsquo;s go ahead and add these REST APIs to our project.\nOur project is using JAX-RS 2.0 as the API and Jersey 2.x as the implementation, thus first we need to configure JAX-RS. Thus create the following class:\n@ApplicationPath(\u0026#34;rest\u0026#34;) public class RestfulApplication extends ResourceConfig { public RestfulApplication() { // add strolch resources register(AuthenticationService.class); register(ModelQuery.class); register(Inspector.class); // add project resources by package name packages(BooksResource.class.getPackage().getName()); // filters register(AuthenticationRequestFilter.class, Priorities.AUTHENTICATION); register(AccessControlResponseFilter.class); register(AuthenticationResponseFilter.class); register(HttpCacheResponseFilter.class); // log exceptions and return them as plain text to the caller register(StrolchRestfulExceptionMapper.class); // the JSON generated is in UTF-8 register(CharsetResponseFilter.class); RestfulStrolchComponent restfulComponent = RestfulStrolchComponent.getInstance(); if (restfulComponent.isRestLogging()) { register(new LoggingFeature(java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), Level.SEVERE, LoggingFeature.Verbosity.PAYLOAD_ANY, Integer.MAX_VALUE)); property(ServerProperties.TRACING, \u0026#34;ALL\u0026#34;); property(ServerProperties.TRACING_THRESHOLD, \u0026#34;TRACE\u0026#34;); } } } As we add new resources they will be automatically since we register the entire package.\nNow add the books resource class:\n@Path(\u0026#34;books\u0026#34;) public class BooksResource { } Search The first service we\u0026rsquo;ll add is to query, or search for the existing books. The API defines three parameters, with which the result can be controlled. The method can be defined as follows:\n@Path(\u0026#34;books\u0026#34;) public class BooksResource { @GET @Produces(MediaType.APPLICATION_JSON) public Response query(@Context HttpServletRequest request, @QueryParam(\u0026#34;query\u0026#34;) String queryS, @QueryParam(\u0026#34;offset\u0026#34;) String offsetS, @QueryParam(\u0026#34;limit\u0026#34;) String limitS) { // TODO } } To fill this method we need a few things. First let\u0026rsquo;s define a constants class where we keep String constants which we used in the model file:\npublic class BookShopConstants { public static final String TYPE_BOOK = \u0026#34;Book\u0026#34;; } As this tutorial progresses, more and more constants will be added here. This class helps with two issues: Through the constants we can easily reason over where certain fields, and types are used and of course String literals in code are a rather bad thing.\nIn Strolch there are multiple way to access objects. The old way was using Queries, the new search API is much more fluent and easier to read and write. The search API, as well as the deprecated query API allows us to implement privilege validation and thus one should create corresponding classes for each type of search. Book entities are Resources, thus we will be creating a ResourceSearch. The search is for Resources of type Book thus the resulting search looks as follows:\npublic class BooksSearch\u0026lt;U\u0026gt; extends ResourceSearch\u0026lt;U\u0026gt; { public BookSearch() { types(TYPE_BOOK); } public BookSearch stringQuery(String value) { if (isEmpty(value)) return this; // split by spaces value = value.trim(); String[] values = value.split(\u0026#34; \u0026#34;); // add where clauses for id, name and description where(id().containsIgnoreCase(values) // .or(name().containsIgnoreCase(values)) // .or(param(BAG_PARAMETERS, PARAM_DESCRIPTION).containsIgnoreCase(values))); return this; } } Note how we added a special stringQuery(String)-method. This method defines where a search string entered by the user will be used to match a book. In this case for id, name and the description parameter.\nSo that our users can call this query, we must give them this as a privilege. This is done by adding the full class name to the PrivilegeRoles.xml file as follows:\n... \u0026lt;Role name=\u0026#34;User\u0026#34;\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.search.StrolchSearch\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;Allow\u0026gt;internal\u0026lt;/Allow\u0026gt; \u0026lt;Allow\u0026gt;li.strolch.bookshop.search.BookSearch\u0026lt;/Allow\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;/Role\u0026gt; ... Note: The internal allow value is a special privilege which is used internally when a service or something performs internal queries. This means that a service can perform a query for object to which the user might not have access, but without which the service could not be completed. We will use this in a later stage.\n Now we have all parts we need to implement the query method. The method will include opening a transaction, instantiating the search, executing the search, and returning the result:\n@Path(\u0026#34;books\u0026#34;) public class BooksResource { @GET @Produces(MediaType.APPLICATION_JSON) public Response query(@Context HttpServletRequest request, @QueryParam(\u0026#34;query\u0026#34;) String queryS, @QueryParam(\u0026#34;offset\u0026#34;) String offsetS, @QueryParam(\u0026#34;limit\u0026#34;) String limitS) { // this is an authenticated method call, thus we can get the certificate from the request: Certificate cert = (Certificate) request .getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE); int offset = StringHelper.isNotEmpty(offsetS) ? Integer.parseInt(offsetS) : 0; int limit = StringHelper.isNotEmpty(limitS) ? Integer.parseInt(limitS) : 0; // open the TX with the certificate, using this class as context Paging\u0026lt;Resource\u0026gt; paging; try (StrolchTransaction tx = RestfulStrolchComponent.getInstance() .openTx(cert, getClass())) { // perform a book search paging = new BookSearch() // .stringQuery(queryS) // .search(tx) // .orderByName(false) // .toPaging(offset, limit); } ResourceVisitor\u0026lt;JsonObject\u0026gt; visitor = new StrolchRootElementToJsonVisitor() .flat().asResourceVisitor(); return ResponseUtil.toResponse(paging, e -\u0026gt; e.accept(visitor)); } } Note: We automatically transform the Resource objects to JSON using the StrolchElementToJsonVisitor. By calling the method .flat()-method we have a more compact JSON format. Paging is handled by a util class.\n The helper class ResponseUtil takes care of creating the JsonObject and the proper page. As a rule we use the format where we return two fields: msg is a dash if all is ok, otherwise an error message will be present. Data is always in the data field. This is just a personal taste, and can be changed to one\u0026rsquo;s own taste.\nGet We have all we need now to implement the GET method:\n@GET @Path(\u0026#34;{id}\u0026#34;) @Produces(MediaType.APPLICATION_JSON) public Response get(@Context HttpServletRequest request, @PathParam(\u0026#34;id\u0026#34;) String id) { // this is an authenticated method call, thus we can get the certificate from the request: Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE); // open the TX with the certificate, using this class as context try (StrolchTransaction tx = RestfulStrolchComponent.getInstance().openTx(cert, getClass())) { // get the book Resource book = tx.getResourceBy(BookShopConstants.TYPE_BOOK, id); if (book == null) return ResponseUtil.toResponse(Status.NOT_FOUND, \u0026#34;Book \u0026#34; + id + \u0026#34; does not exist!\u0026#34;); // transform to JSON JsonObject bookJ = book.accept(new StrolchRootElementToJsonVisitor().flat()); // return return ResponseUtil.toResponse(StrolchRestfulConstants.DATA, bookJ); } } Note how we simply retrieve the book as a Resource from the TX. This is a good moment to familiarize yourself with the API of the StrolchTransaction. There are methods to retrieve elements, and also perform searches. We will use more of these methods later.\n Further it can be noted that a simple retrieval isn\u0026rsquo;t validated against the user\u0026rsquo;s privileges, the user is authenticated, which is enough for the moment.\nCreate To create a new book we need to implement a Service. This service will be called CreateBookService. A Service always has a ServiceArgument and a ServiceResult. Our service will use the JsonServiceArgument and the JsonServiceResult. The implementation of the POST method is as follows:\n@POST @Produces(MediaType.APPLICATION_JSON) public Response create(@Context HttpServletRequest request, String data) { // this is an authenticated method call, thus we can get the certificate from the request: Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE); // parse data to JSON JsonObject jsonData = JsonParser.parseString(data).getAsJsonObject(); // instantiate the service with the argument CreateBookService svc = new CreateBookService(); JsonServiceArgument arg = svc.getArgumentInstance(); arg.jsonElement = jsonData; // perform the service ServiceHandler serviceHandler = RestfulStrolchComponent.getInstance().getServiceHandler(); JsonServiceResult result = serviceHandler.doService(cert, svc, arg); // return depending on the result state if (result.isOk()) return ResponseUtil.toResponse(StrolchRestfulConstants.DATA, result.getResult()); return ResponseUtil.toResponse(result); } Note: We return the created object again as JSON in its own data field.\n The service is implemented as follows:\npublic class CreateBookService extends AbstractService\u0026lt;JsonServiceArgument, JsonServiceResult\u0026gt; { @Override protected JsonServiceResult getResultInstance() { return new JsonServiceResult(); } @Override public JsonServiceArgument getArgumentInstance() { return new JsonServiceArgument(); } @Override protected JsonServiceResult internalDoService(JsonServiceArgument arg) throws Exception { // open a new transaction, using the realm from the argument, or the certificate Resource book; try (StrolchTransaction tx = openArgOrUserTx(arg)) { // get a new book \u0026#34;instance\u0026#34; from the template book = tx.getResourceTemplate(BookShopConstants.TYPE_BOOK); // map all values from the JSON object into the new book element book.accept(new FromFlatJsonVisitor(arg.jsonElement.getAsJsonObject()).ignoreBag(BAG_RELATIONS)); // save changes tx.add(book); // notify the TX that it should commit on close tx.commitOnClose(); } // map the return value to JSON JsonObject result = book.accept(new StrolchElementToJsonVisitor().flat()); // and return the result return new JsonServiceResult(result); } } Note: For the authenticated user to be able to perform this service, we must add it to their privileges:\n ... \u0026lt;Role name=\u0026#34;User\u0026#34;\u0026gt; ... \u0026lt;Privilege name=\u0026#34;li.strolch.service.api.Service\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;Allow\u0026gt;li.strolch.bookshop.service.CreateBookService\u0026lt;/Allow\u0026gt; \u0026lt;/Privilege\u0026gt; ... \u0026lt;/Role\u0026gt; ... Update Updating of a book is basically the same as the creation, we just use PUT, verify that the book exists and give the user the privilege.\nPUT Method:\n@PUT @Path(\u0026#34;{id}\u0026#34;) @Produces(MediaType.APPLICATION_JSON) public Response update(@Context HttpServletRequest request, @PathParam(\u0026#34;id\u0026#34;) String id, String data) { // this is an authenticated method call, thus we can get the certificate from the request: Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE); // parse data to JSON JsonObject jsonData = JsonParser.parseString(data).getAsJsonObject(); // instantiate the service with the argument UpdateBookService svc = new UpdateBookService(); JsonServiceArgument arg = svc.getArgumentInstance(); arg.objectId = id; arg.jsonElement = jsonData; // perform the service ServiceHandler serviceHandler = RestfulStrolchComponent.getInstance().getServiceHandler(); JsonServiceResult result = serviceHandler.doService(cert, svc, arg); // return depending on the result state if (result.isOk()) return ResponseUtil.toResponse(StrolchRestfulConstants.DATA, result.getResult()); return ResponseUtil.toResponse(result); } Update Service:\npublic class UpdateBookService extends AbstractService\u0026lt;JsonServiceArgument, JsonServiceResult\u0026gt; { @Override protected JsonServiceResult getResultInstance() { return new JsonServiceResult(); } @Override public JsonServiceArgument getArgumentInstance() { return new JsonServiceArgument(); } @Override protected JsonServiceResult internalDoService(JsonServiceArgument arg) throws Exception { // verify same book DBC.PRE.assertEquals(\u0026#34;ObjectId and given Id must be same!\u0026#34;, arg.objectId, arg.jsonElement.getAsJsonObject().get(Json.ID).getAsString()); // open a new transaction, using the realm from the argument, or the certificate Resource book; try (StrolchTransaction tx = openArgOrUserTx(arg)) { // get the existing book book = tx.getResourceBy(BookShopConstants.TYPE_BOOK, arg.objectId, true); // map all values from the JSON object into the new book element book.accept(new FromFlatJsonVisitor(arg.jsonElement.getAsJsonObject()).ignoreBag(BAG_RELATIONS)); // save changes tx.update(book); // notify the TX that it should commit on close tx.commitOnClose(); } // map the return value to JSON JsonObject result = book.accept(new StrolchElementToJsonVisitor().flat()); // and return the result return new JsonServiceResult(result); } } Privilege\n... \u0026lt;Role name=\u0026#34;User\u0026#34;\u0026gt; ... \u0026lt;Privilege name=\u0026#34;li.strolch.service.api.Service\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; ... \u0026lt;Allow\u0026gt;li.strolch.bookshop.service.UpdateBookService\u0026lt;/Allow\u0026gt; ... \u0026lt;/Privilege\u0026gt; ... \u0026lt;/Role\u0026gt; ... Remove To remove a book, we need a DELETE method, a remove service and the associated privilege.\nDELETE Method:\n@DELETE @Path(\u0026#34;{id}\u0026#34;) @Produces(MediaType.APPLICATION_JSON) public Response update(@Context HttpServletRequest request, @PathParam(\u0026#34;id\u0026#34;) String id) { // this is an authenticated method call, thus we can get the certificate from the request: Certificate cert = (Certificate) request.getAttribute(StrolchRestfulConstants.STROLCH_CERTIFICATE); // instantiate the service with the argument RemoveBookService svc = new RemoveBookService(); StringServiceArgument arg = svc.getArgumentInstance(); arg.value = id; // perform the service ServiceHandler serviceHandler = RestfulStrolchComponent.getInstance().getServiceHandler(); ServiceResult result = serviceHandler.doService(cert, svc, arg); // return depending on the result state return ResponseUtil.toResponse(result); } Remove Service:\npublic class RemoveBookService extends AbstractService\u0026lt;StringServiceArgument, ServiceResult\u0026gt; { @Override protected ServiceResult getResultInstance() { return new ServiceResult(); } @Override public StringServiceArgument getArgumentInstance() { return new StringServiceArgument(); } @Override protected ServiceResult internalDoService(StringServiceArgument arg) throws Exception { // open a new transaction, using the realm from the argument, or the certificate try (StrolchTransaction tx = openArgOrUserTx(arg)) { // get the existing book Resource book = tx.getResourceBy(BookShopConstants.TYPE_BOOK, arg.value, true); // save changes tx.remove(book); // notify the TX that it should commit on close tx.commitOnClose(); } // and return the result return ServiceResult.success(); } } Privilege:\n... \u0026lt;Role name=\u0026#34;User\u0026#34;\u0026gt; ... \u0026lt;Privilege name=\u0026#34;li.strolch.service.api.Service\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; ... \u0026lt;Allow\u0026gt;li.strolch.bookshop.service.RemoveBookService\u0026lt;/Allow\u0026gt; ... \u0026lt;/Privilege\u0026gt; ... \u0026lt;/Role\u0026gt; ... Notes One should now see a pattern emerge:\n The REST API delegates to the Services, or Searches, with the exception of the retrieval of a single object by id. Services should do initial validation of the input. Not much validation was done here, but more could be done. Commands are reusable objects to perform recurring work. Searches and Services are privileged actions for which a user must have the privilege to perform the action. The book services are quite simple, but as more requirements arise, it should be easy to implement them in the service layer. Thus should a service be required to be performed by an integration layer, then they can simply call the services, since the input is defined and validation is done there (i.e. NOT in the REST API).\nThis concludes the CRUD of books.\n"},{"uri":"https://strolch.li/documentation/do-and-donts/","title":"Do and Don't","tags":[],"description":"","content":"This page discusses things you should and shouldn\u0026rsquo;t do when using Strolch The following is a simple list of do\u0026rsquo;s and don\u0026rsquo;ts:\n 1 Service per use-case, should mostly delegate to Commands. Commands implement use-cases or parts of it, and are thus reusable. Subclass ResourceSearch, OrderSearch and ActivitySearch when implementing use-case specific search - this allows privilege checking. One Transaction at a time - no TX inside of another TX. Commands are added to TXs and performed on close: tx.addCommand(Command) and then tx.commitOnClose() Use tx.flush() if you need to perform first some work and then as late as possible call tx.commitOnClose() Only access ElementMaps if really no other way, mostly use tx.get*By(), tx.findBy() and searches - if a specific get is missing, then add the method to StrolchTransaction and send a pull request! Use tx.stream*() methods to iterate over all elements, if you don\u0026rsquo;t want to use a search. Don\u0026rsquo;t write logic in REST API beans. Delegate to other services, making your code reusable! Transform to JSON using the StrolchElementToJsonVisitor. References between objects is done by adding a ParameterBag with the id relations to the object and then StringParameters with the value being the ID, the UOM set to the type of element being referenced and the Interpretation set to the class type being referenced. "},{"uri":"https://strolch.li/documentation/runtime-configuration/","title":"Runtime Configuration","tags":[],"description":"","content":"Runtime Configuration A Strolch runtime configuration comprises two parts: a configuration part, and a model part. The configuration are files located in the ..config/ folder, and the model are files located in the ../data folder.\nIn an absolute minimal configuration, the Strolch runtime requires the following folder structure:\n ../config/ ../StrolchConfiguration.xml configures the Strolch agent ../PrivilegeConfig.xml configures user management ../PrivilegeUsers.xml contains the users in an XML based user management file ../PrivilegeRoles.xml contains the roles and privileges in an XML based user management StrolchConfiguration.xml The StrolchConfiguration.xml file configures the Strolch agent. The StrolchConfiguration.xml defines the following:\n \u0026lt;StrolchConfiguration\u0026gt; root element \u0026lt;env id=\u0026quot;xxx\u0026quot;\u0026gt; different environments with the possibility of having a global environment for configuration valid in multiple environments. \u0026lt;Runtime\u0026gt; element which defines the agents name and a few other properties e.g. locale and verbose: \u0026lt;applicationName\u0026gt; the agent\u0026rsquo;s name \u0026lt;Properties\u0026gt; \u0026lt;locale\u0026gt; the agent\u0026rsquo;s internal locale for log messages etc. \u0026lt;verbose\u0026gt; the logging level for some internal logging. (Logging is mostly done using log4j over slf4j) \u0026lt;Component\u0026gt; elements for each component used in the agent. A component is configured by defining the following child elements: \u0026lt;name\u0026gt; the name of the component, use when defining dependencies between components. The name is mostly set to the simple name of the interface of the component\n \u0026lt;api\u0026gt; the full class name to the interface of the component. During runtime this interface will be used to access the component e.g.:\nServiceHandler svcHandler = agent.getContainer().getComponent(ServiceHandler.class);\n \u0026lt;impl\u0026gt; the full class name of the concrete implementation of the component. During initialization this class will be instantiated and registered under the component name and interface. This class must extend the class li.strolch.agent.api.StrolchComponent\n \u0026lt;depends\u0026gt; any number of these elements, where the content is the name of another component, on which this component depends. Depending components are initialized and started after the component they depend on and are stopped and destroyed before\n \u0026lt;Properties\u0026gt;\n \u0026lt;...\u0026gt; any number of properties which the component requires. The element\u0026rsquo;s name will be the key with which the value can be accessed at runtime.\n When a property is missing, and the component has a hard coded default value, then when the component is initialized, the use of the default value and its key is logged. This makes it easy to see which new properties can be configured. Should the component not define a default value, then the component will thrown an exception on initialization. In this case it can be a good moment to read the JavaDoc (or source code) for the component in question to see how it is configured.\n Privilege Configuration In Strolch authentication and authorization is baked in. To open a transaction, and thus access the Strolch model, a Certificate object is required, which means the user has been authenticated and possibly authorized.\nThe PrivilegeConfig.xml defines the following:\n \u0026lt;Privilege\u0026gt; root element \u0026lt;Container\u0026gt; configures the individual Privilege components \u0026lt;Parameters\u0026gt; base configuration properties for Privilege \u0026lt;EncryptionHandler\u0026gt; configures the hashing algorithms and other encryption specific configuration \u0026lt;PersistenceHandler\u0026gt; configures the persistence of the roles and users \u0026lt;UserChallengeHandler\u0026gt; configures a challenge handler so that a user can reset their password. The default challenge handler is the li.strolch.privilege.handler.MailUserChallengeHandler which sends a challenge to the user\u0026rsquo;s defined e-mail address. \u0026lt;SsoHandler\u0026gt; the SSO Handler is used to implement a SingleSignOn and can be used to start a session using a LDAP token, etc. There is no default implementation as this is project specific. \u0026lt;Policies\u0026gt; configures the available privilege policies at runtime, the name is referenced from the model file The PrivilegeUsers.xml and PrivilegeRoles.xml define the users and roles and is used when in PrvilegeConfig.xml the PersistenceHandler is set to ch.eitchnet.privilege.handler.XmlPersistenceHandler:\n \u0026lt;Users\u0026gt; configures all users \u0026lt;User\u0026gt; configures a specific user \u0026lt;Firstname\u0026gt; configures a user\u0026rsquo;s first name \u0026lt;Lastname\u0026gt; configure a user\u0026rsquo;s last name \u0026lt;State\u0026gt; configures the user\u0026rsquo;s state, see li.strolch.privilege.model.UserState \u0026lt;Locale\u0026gt; configure the user\u0026rsquo;s locale \u0026lt;Roles\u0026gt; configures the user\u0026rsquo;s roles \u0026lt;Role\u0026gt; adds a role to the user \u0026lt;Properties\u0026gt; configures user specific properties. What properties are used is not specified and is dependent on the concrete agent \u0026lt;Property\u0026gt; defines a single property \u0026lt;Roles\u0026gt; configures all roles \u0026lt;Role\u0026gt; configures a specific role \u0026lt;Privilege\u0026gt; configures a specific privilege for this role \u0026lt;AllAllowed\u0026gt; if set to true, then defines that all values associated with this privilege are allowed \u0026lt;Allow\u0026gt; defines one allowed value for this privilege \u0026lt;Deny\u0026gt; defines one denied value for this privilege Implementing a StrolchComponent Implementing a strolch component requires an interface, which defines the component\u0026rsquo;s API and a concrete class which implements the interface and extends the class StrolchComponent.\nThe StrolchComponent class adds the state model to the class, which transitions as follows:\nUNDEFINED =\u0026gt; SETUP =\u0026gt; INITIALIZED =\u0026gt; STARTED \u0026lt;=\u0026gt; STOPPED =\u0026gt; DESTROYED\nComponents can switch between STARTED and STOPPED, but once DESTROYED no further state change is possible. The component\u0026rsquo;s state is changed by changes to the agent\u0026rsquo;s lifecycle.\nA component\u0026rsquo;s state is changed by a call to the appropriate method on the component, override the methods as necessary. Note that it is good practice that the initialize()-method is used to get all the configuration properties, and that they should there be evaluated and that the method so return quickly. The start()-method is called after the agent\u0026rsquo;s initialization and should be where additional threads are started. Correctly implementing these methods allows to quickly detect a wrongly configured agent, which might take longer to start for whatever reason.\nThe following shows a basic implementation of a component on the basis of a post initializer (a component which performs some actions in its start()-method which should be done after everything else is started in the agent).\npublic class SimplePostInitializer extends StrolchComponent implements PostInitializer { public SimplePostInitializer(ComponentContainer container, String componentName) { super(container, componentName); } @Override public void initialize(ComponentConfiguration configuration) { // do some initialization, validate configuration values, etc. // now call super, to update state super.initialize(configuration); } @Override public void start() { // start any threads, or perform long running start work // now call super, to update state super.start(); } @Override public void stop() { // stop threads and timers, but be ready to start again // now call super, to update state super.stop(); } @Override public void destroy() { // destroy this component, release all resources and don\u0026#39;t worry about // being called to start again now call super, to update state super.destroy(); } } The new component would then be registered in the StrolchConfiguration.xml as follows:\n\u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;...\u0026#34;\u0026gt; ... \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;SimplePostInitializer\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.agent.api.PostInitializer\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.documentation.SimplePostInitializer\u0026lt;/impl\u0026gt; \u0026lt;/Component\u0026gt; ... \u0026lt;/env\u0026gt; \u0026lt;/StrolchConfiguration\u0026gt; And can be access at runtime using:\nPostInitializer postInitializer = getContainer().getComponent(PostInitializer.class); Starting the agent When a Strolch runtime is started, then the root path to the runtime configuration must be passed. In Java this is done by calling:\nStrolchAgent agent = new StrolchAgent(); agent.setup(environment, rootPath); agent.initialize(); agent.start(); In Servlet 3.0 applications one would implement the javax.servlet.ServletContextListener interface, add the @WebListener annotation to the class and in the contextInitialized()-method start Strolch:\nString realPath = sce.getServletContext().getRealPath(\u0026#34;/WEB-INF\u0026#34;); String environment = StrolchEnvironment.getEnvironmentFromEnvProperties(pathF); this.agent = new StrolchAgent(); this.agent.setup(environment, new File(realPath)); this.agent.initialize(); this.agent.start(); "},{"uri":"https://strolch.li/tutorial/","title":"Tutorial","tags":[],"description":"","content":"Let\u0026rsquo;s build a bookshop! In this tutorial we will build a book store using Strolch. This book store will be without a UI, but we will do everything using REST APIs, which should make it easy to add a UI later using whatever framework suits one most.\nThe book store will have the following features:\n The store owner can add, update and remove books The store owner can edit the stock quantity Users can view a list of books Users can add books to a virtual cart Users can create and verify an account using an e-mail address Users can submit an order for the books in their cart The store owner can see the orders by state (pending, preparing, sent) The store owner can update the state of an order (preparing, sent) Notify the user when the order is sent The code to the book can be downloaded from GitHub and will be updated as this tutorial is updated.\nThe tutorial consists of the following parts: Configuration Model CRUD Book "},{"uri":"https://strolch.li/plc/architecture/","title":"Architecture","tags":[],"description":"","content":"Architecture Overview The Strolch PLC architecture sees the Strolch Agent as the server, managing logical devices, i.e. multiple sensors and actors together and thus deciding on further steps. With this architecture multiple PLCs can be combined together in one agent for flow control.\nPLC Architecture On the agent side the two main classes are the PlcGwServerHandler and the PlcGwService\nThe PlcGwServerHandler handles connections from remote PLCs over WebSockets and sends the requests to these PLCs. A PlcGwService instance will be notified and can then decide on an action. In an execution model with Activities, the PlcNotificationListener interface can be implemented, or the PlcExecutionPolicy can be directly extended.\nOn the PLC side, the PlcGwClientHandler is optional if no agent is required. The PlcHandler initializes the model and connections. The Plc class is Strolch agnostic and manages the connections and notifies PlcListener instances on changes coming from the underlying connections. The PlcService implementations implement business logic, and can also be notified on updates from connections.\n"},{"uri":"https://strolch.li/documentation/realms/","title":"Realms","tags":[],"description":"","content":"Realms Realms implement multi-tenant capabilities. A Strolch agent can have an arbitrary number of realms configured and each realm has its own persistence configuration, allowing to separate mandates completely.\nA realm can run in one of the following modes:\n EMPTY This is a transient data store mode, where no model changes are persisted - they are only kept in memory. When the Strolch agent is started, this realm is empty as no data is loaded. TRANSIENT This is the same as EMPTY, but with the difference that when the Strolch agent is started, a model file is parsed and the in-memory realm is populated with the elements parsed from the model file. CACHED In this mode, all data is stored in-memory, and any changes made are written back to the persistence layer. This allows for fast in-memory qeuries, but makes sure no data is lost when the agent is restarted. Realms are mostly hidden from a developer as a StrolchTransaction exposes all important operations needed to access Strolch objects. A developer will however need to configure the realms for their specific project. If the project only requires one realm, then the defaultRealm can be used, where the developer only is required to configure the mode and any relevant model file.\nIf the mode is CACHED, then the PersistenceHandler component is required to be configured, so that the DAOs know how to access the underlying database.\nThe configuration in the StrolchConfiguration.xml file is as follows:\n\u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;dev\u0026#34;\u0026gt; ... \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;RealmHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.agent.api.RealmHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.agent.impl.DefaultRealmHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;!-- if CACHED: --\u0026gt; \u0026lt;!--depends\u0026gt;PersistenceHandler\u0026lt;/depends--\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;dataStoreMode\u0026gt;EMPTY|TRANSIENT|CACHED\u0026lt;/dataStoreMode\u0026gt; \u0026lt;dataStoreFile\u0026gt;StrolchModel.xml\u0026lt;/dataStoreFile\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; ... \u0026lt;/env\u0026gt; \u0026lt;/StrolchConfiguration\u0026gt; Multi-Realm A multi-realm configuration would be as follows.\nNote how the defaultRealm is still enabled, and has its configuration as before. Further the PostgreSQL PersistenceHandler is configured to show how the realms are connected to the persistence handler:\n \u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;dev\u0026#34;\u0026gt; ... \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;RealmHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.agent.api.RealmHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.agent.impl.DefaultRealmHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;depends\u0026gt;PersistenceHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;realms\u0026gt;defaultRealm, cachedRealm\u0026lt;/realms\u0026gt; \u0026lt;dataStoreMode\u0026gt;TRANSIENT\u0026lt;/dataStoreMode\u0026gt; \u0026lt;dataStoreFile\u0026gt;DefaultRealm.xml\u0026lt;/dataStoreFile\u0026gt; \u0026lt;dataStoreMode.cachedRealm\u0026gt;CACHED\u0026lt;/dataStoreMode.cachedRealm\u0026gt; \u0026lt;dataStoreMode.emptyRealm\u0026gt;EMPTY\u0026lt;/dataStoreMode.emptyRealm\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;PersistenceHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.persistence.api.PersistenceHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.persistence.postgresql.PostgreSqlPersistenceHandler\u0026lt;/impl\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;allowSchemaCreation\u0026gt;true\u0026lt;/allowSchemaCreation\u0026gt; \u0026lt;allowSchemaDrop\u0026gt;true\u0026lt;/allowSchemaDrop\u0026gt; \u0026lt;db.url.cachedRealm\u0026gt;jdbc:postgresql://localhost/testdb2\u0026lt;/db.url.cachedRealm\u0026gt; \u0026lt;db.username.cachedRealm\u0026gt;testuser2\u0026lt;/db.username.cachedRealm\u0026gt; \u0026lt;db.password.cachedRealm\u0026gt;test\u0026lt;/db.password.cachedRealm\u0026gt; \u0026lt;db.pool.maximumPoolSize.cachedRealm\u0026gt;1\u0026lt;/db.pool.maximumPoolSize.cachedRealm\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; ... \u0026lt;/env\u0026gt; \u0026lt;/StrolchConfiguration\u0026gt; Access realm Accessing a realm is done in multiple ways. Important is to note, that a user should use the StrolchTransaction object, instead of accessing the Realm directly.\nOpening a transaction is done from a Service by calling one of the openTx()-methods. Nevertheless, the realm can be accessed as follows:\nComponentContainer container = getAgent().getContainer(); StrolchRealm realm = container.getRealm(StrolchConstants.DEFAULT_REALM); try(StrolchTransaction tx = realm.openTx()) { Resource resource = tx.getResourceBy(\u0026#34;TestType\u0026#34;, \u0026#34;MyTestResource\u0026#34;); ... } "},{"uri":"https://strolch.li/documentation/components/","title":"Components","tags":[],"description":"","content":"Components A Strolch agent can be easily extended with arbitrary components. An agent is basically a container for classes extending StrolchComponent with a life cycle. These classes mostly implement an interface which describes the operations that are supported by the component.\nThe following represents a list of the most used components:\n RealmHandler: li.strolch.agent.impl.DefaultRealmHandler PrivilegeHandler: li.strolch.runtime.privilege.DefaultStrolchPrivilegeHandler EnumHandler: li.strolch.runtime.query.enums.DefaultEnumHandler PolicyHandler: li.strolch.policy.DefaultPolicyHandler ServiceHandler: li.strolch.service.api.DefaultServiceHandler StrolchSessionHandler: li.strolch.rest.DefaultStrolchSessionHandler PersistenceHandler: multiple implementations PostInitializer: project specific implementation MailHandler: li.strolch.handler.mail.SmtpMailHandler A component has a life-cycle, which is governed by the Agent\u0026rsquo;s own life-cycle. The life-cycle is as follows:\nsetup -\u0026gt; initialize -\u0026gt; start \u0026lt;-\u0026gt; stop -\u0026gt; destroy The setup step is used to instantiate the component, the initialize step is used to validate configuration parameters, and the run step is used to start the component, i.e. start threads, etc. The stop step stops these threads and also allows the component to be started again. The destroy step destroys the instance and makes it unusable anymore, i.e. shutdown of the agent.\nEach component has its own configuration parameters. A component is registered in the StrolchConfiguration.xml file with a\n name api class name implementation class name configuration parameters any required dependencies The dependencies is an important feature as the dependencies of a component are always started before the actual component.\nBy example of the MailHandler we shall show how a strolch component would be implemented.\nFirst define an interface:\npublic interface MailHandler { void sendMail(String subject, String text, String recipient); } Then implement a concrete MailHandler:\npublic class SmtpMailHandler extends StrolchComponent implements MailHandler { // instance fields with configuration properties to send the mail public SmtpMailHandler(ComponentContainer container, String componentName) { super(container, componentName); } @Override public void initialize(ComponentConfiguration configuration) throws Exception { // store any properties needed from the configuration super.initialize(configuration); } @Override public void sendMail(String subject, String text, String recipient) { // send the e-mail using SMTP, or store in stack to send by thread } } Now that the component is written, it must be registered on the component, so that it is loaded when the agent is started. For this the StrolchConfiguration.xml file must be modified to include a component element:\n\u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;dev\u0026#34;\u0026gt; ... \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;MailHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.handler.mail.MailHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.handler.mail.SmtpMailHandler\u0026lt;/impl\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;username\u0026gt;test\u0026lt;/username\u0026gt; \u0026lt;password\u0026gt;test\u0026lt;/password\u0026gt; \u0026lt;hostName\u0026gt;localhost\u0026lt;/hostName\u0026gt; ... \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; ... \u0026lt;/env\u0026gt; \u0026lt;/StrolchConfiguration\u0026gt; Now when the agent is started, the component can be retrieved and used. E.g from inside a Service:\nMailHandler mailHandler = getComponent(MailHandler.class); mailHandler.sendMail(\u0026#34;My Subject\u0026#34;, \u0026#34;Hello World\u0026#34;, \u0026#34;test@test.ch\u0026#34;); "},{"uri":"https://strolch.li/documentation/services-and-commands/","title":"Services and Commands","tags":[],"description":"","content":"Services and Commands Services are written to implement a specific use-case. Commands are written to implement re-usable parts of a use-case. The use-case can be abstract e.g., AddResourceService or very specific e.g. CreatePatientService.\nShould the use-case be re-usable in different scenarios, then commands should implement the logic, and the services should then execute the commands. E.g. The CreatePatientService would use a CreatePatientResourceCommand and then use an AddResourceCommand in a single transaction, so that the task of creating the actual Patient Resource can be re-used somewhere else.\nServices extend the abstract class AbstractService and then implement the method internalDoService(ServiceArgument). AbstractService defines generic template arguments with which the concrete service can define a specific input ServiceArgument class and output ServiceResult class.\nThe AbstractService class has multiple helper methods:\n openTx():StrolchTransaction - to open a transaction runPrivileged() - to perform a SystemUserAction getComponent():V - to retrieve a specific StrolchComponent there are more - check the JavaDocs.\nCommands extend the Command class and then implement the method doCommand(). Commands have helper methods:\n tx() - to get the current transaction getPolicy() - to retrieve a StrolchPolicy instance runPrivileged() - to perform a SystemUserAction there are more - check the JavaDocs.\nThe following code snippets shows how a Service and Command are used to perform the task of adding a new Order. Note how:\n the Service opens the transaction adds the command to the TX calls tx.commitOnClose() the command validates its input locks the object performs the work and implements an undo AddOrderService:\npublic class AddOrderService extends AbstractService\u0026lt;AddOrderService.AddOrderArg, ServiceResult\u0026gt; { @Override protected ServiceResult getResultInstance() { return new ServiceResult(); } @Override protected ServiceResult internalDoService(AddOrderArg arg) { try (StrolchTransaction tx = openTx(arg.realm)) { AddOrderCommand command = new AddOrderCommand(getContainer(), tx); command.setOrder(arg.order); tx.addCommand(command); tx.commitOnClose(); } return ServiceResult.success(); } public static class AddOrderArg extends ServiceArgument { public Order order; } } AddOrderCommand:\npublic class AddOrderCommand extends Command { private Order order; public AddOrderCommand(ComponentContainer container, StrolchTransaction tx) { super(container, tx); } public void setOrder(Order order) { this.order = order; } @Override public void validate() { DBC.PRE.assertNotNull(\u0026#34;Order may not be null!\u0026#34;, this.order); } @Override public void doCommand() { tx().lock(this.order); OrderMap orderMap = tx().getOrderMap(); if (orderMap.hasElement(tx(), this.order.getType(), this.order.getId())) { String msg = MessageFormat.format(\u0026#34;The Order {0} already exists!\u0026#34;, this.order.getLocator()); throw new StrolchException(msg); } orderMap.add(tx(), this.order); } @Override public void undo() { if (this.order != null \u0026amp;\u0026amp; tx().isRollingBack()) { OrderMap orderMap = tx().getOrderMap(); if (orderMap.hasElement(tx(), this.order.getType(), this.order.getId())) orderMap.remove(tx(), this.order); } } } "},{"uri":"https://strolch.li/download/","title":"Download","tags":[],"description":"","content":"Download Strolch is on Maven central , but if the latest version is not there, then build it locally. A guide can be found on the development page.\nStrolch is also built on Jenkins, so you can see if the latest version passes all tests.\n"},{"uri":"https://strolch.li/documentation/searches/","title":"Searches","tags":[],"description":"","content":"Searches As is custom for every framework, querying, or searching, the model must be possible. Strolch searches are implemented using the StrolchSearch class and one of its concrete implementations: ResourceSearch, OrderSearch, ActivitySearch.\nA Strolch element always has two identifiers: Type and Id. The type is important as it classifies an element. So if a car and a house would be modelled in Strolch, then those would both be a Resource, but one of type Car and the other of type House. Both would have different parameters. Thus when searching for objects, the first thing to do is define the type of object being searched.\nThe Strolch search API is very expressive and offers multiple ways to perform the same search. The search API consists of three components: The search classes, the search expressions and the search predicates. The concept was taken from the Apache Camel project.\nThere are four main search classes:\n RootElementSearch - search for any of Resource, Order or Activity elements ResourceSearch - search for Resources OrderSearch - search for Orders ActivitySearch - search for Activities No search is useful without a where clause, which are called search expressions. When writing a search, there are multiple ways to add such where clauses. Either\n override the define()-method in your sub class and add the where clauses by calling the where() method, or define special methods on the class e.g. byColor() which also calls the where()-method to add a search expression, or directly call the where()-method after instantiating a search. When extending the class, then the search expressions are available as methods on the super class, otherwise you can statically import them from ExpressionsSupport .\nAnd of course a where clause needs operators, which are called search predicates. Just as search expressions are available in sub classes, so are search predicates and can also be statically imported through PredicatesSupport .\nExamples of search expressions with search predicates follow:\nResourceSearch search=new ResourceSearch(); // predicate either as parameter, or chained search.where(id().isEqualTo(\u0026#34;myId\u0026#34;)); search.where(id(isEqualTo(\u0026#34;myId\u0026#34;))); // negating search.where(id(isEqualTo(\u0026#34;myId\u0026#34;)).not()); search.where(param(\u0026#34;bagId\u0026#34;,\u0026#34;paramId\u0026#34;).isIn(Arrays.asList(\u0026#34;red\u0026#34;,\u0026#34;blue\u0026#34;,\u0026#34;green\u0026#34;))); search.where(paramNull(\u0026#34;bagId\u0026#34;,\u0026#34;paramId\u0026#34;))); // boolean operations search.where(id(isEqualTo(\u0026#34;myId\u0026#34;)) // \t.or(name(isEqualTo(\u0026#34;myName\u0026#34;)))); Note how the predicates can be chained to the search expression, or passed as a parameter to the expression.\nIn addition to using predefined search search expressions, one can also just pass a lambda expression which performs a custom filter:\npersonSearch.where(person -\u0026gt; person.getName().length() == 3); See the StrolchSearchTest for many ways in which you can implement tests.\nNote that strolch searches requires privileges, thus when you use a strolch search, add it to the role of the user in PrivilegeRoles.xml:\n \u0026lt;Privilege name=\u0026#34;li.strolch.search.StrolchSearch\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;Allow\u0026gt;internal \u0026lt;/Allow\u0026gt; \u0026lt;!-- internal used for when the search is done in an internal service --\u0026gt; \u0026lt;Allow\u0026gt;li.strolch.bookshop.search.BookSearch\u0026lt;/Allow\u0026gt; \u0026lt;/Privilege\u0026gt; "},{"uri":"https://strolch.li/documentation/queries/","title":"Queries","tags":[],"description":"","content":"Queries The Query API is deprecated and the search API should be used instead.\n As is custom for every framework, querying the model must be possible. Strolch queries are implemented using the StrolchQuery interface and one of its concrete implementations: ResourceQuery, OrderQuery, ActivityQuery.\nA Strolch element always has two identifiers: Type and Id. The type is important as it classifies an element. So if a car and a house would be modelled in Strolch, then those would both be a Resource, but one of type Car and the other of type House. Both would have different parameters.\nThus one of the inputs for every query is it\u0026rsquo;s type, which is defined as the navigation. It is said that we navigate to the Cars, or Houses. Thus when instantiating a ResourceQuery, pass the navigation to the type of Resource as well. Same applies for Orders and Activities.\nFurther input for a StrolchQuery are the selections. These selections get translated into RDBMS WHERE clauses. Selections support boolean operations thus allowing for complex querying.\nStrolchQueries also support Ordering and object transformation. Following classes provide the most used scenarios:\n OrderById OrderByName OrderByParameter *ToDomVisitor *ToSaxVisitor *ToJsonVisitor *ToFlatJsonVisitor Example: Query all resources of type Car:\ntry (StrolchTransaction tx = openTx()) { ResourceQuery\u0026lt;Resource\u0026gt; query = ResourceQuery.query(\u0026#34;Car\u0026#34;); query.withAny(); List\u0026lt;Resource\u0026gt; cars = tx.doQuery(query); } Example: Query all resources of type Car, order by Name and transform to JSON:\ntry (StrolchTransaction tx = openTx()) { ResourceQuery\u0026lt;JsonObject\u0026gt; query = ResourceQuery.query(\u0026#34;Car\u0026#34;, new ResourceToJsonVisitor(), new OrderByName()); query.withAny(); List\u0026lt;JsonObject\u0026gt; cars = tx.doQuery(query); } the previous example can also be written as follows:\ntry (StrolchTransaction tx = openTx()) { ResourceQuery\u0026lt;JsonObject\u0026gt; query = new ResourceQuery\u0026lt;\u0026gt;(); query.setNavigation(new StrolchTypeNavigation(\u0026#34;Car\u0026#34;)); query.setResourceVisitor(new ResourceToJsonVisitor()); query.withAny(); List\u0026lt;JsonObject\u0026gt; cars = tx.doQuery(query); } Example: Query all resources of type Car with color blue:\ntry (StrolchTransaction tx = openTx()) { ResourceQuery\u0026lt;Resource\u0026gt; query = ResourceQuery.query(\u0026#34;Car\u0026#34;); query.with(ParameterSelection.stringSelection(\u0026#34;parameters\u0026#34;, \u0026#34;color\u0026#34;, \u0026#34;blue\u0026#34;, StringMatchMode.es())); List\u0026lt;Resource\u0026gt; cars = tx.doQuery(query); } Example: Query all resources of type Car which are not blue:\ntry (StrolchTransaction tx = openTx()) { ResourceQuery\u0026lt;Resource\u0026gt; query = ResourceQuery.query(\u0026#34;Car\u0026#34;); query.not(ParameterSelection.stringSelection(\u0026#34;parameters\u0026#34;, \u0026#34;color\u0026#34;, \u0026#34;blue\u0026#34;, StringMatchMode.es())); List\u0026lt;Resource\u0026gt; cars = tx.doQuery(query); } Example: Query all resources of type Car with color blue or yellow:\ntry (StrolchTransaction tx = openTx()) { ResourceQuery\u0026lt;Resource\u0026gt; query = ResourceQuery.query(\u0026#34;Car\u0026#34;); query.or().with( ParameterSelection.stringSelection(\u0026#34;parameters\u0026#34;, \u0026#34;color\u0026#34;, \u0026#34;blue\u0026#34;, StringMatchMode.es()), ParameterSelection.stringSelection(\u0026#34;parameters\u0026#34;, \u0026#34;color\u0026#34;, \u0026#34;yellow\u0026#34;, StringMatchMode.es())); List\u0026lt;Resource\u0026gt; cars = tx.doQuery(query); } Example: Query all resources of type Car with color blue or yellow owned by Jill:\ntry (StrolchTransaction tx = openTx()) { ResourceQuery\u0026lt;Resource\u0026gt; query = ResourceQuery.query(\u0026#34;Car\u0026#34;); StringParameterSelection owner = ParameterSelection.stringSelection(\u0026#34;parameters\u0026#34;, \u0026#34;owner\u0026#34;, \u0026#34;Jill\u0026#34;, StringMatchMode.es()); OrSelection colors = new OrSelection().with( ParameterSelection.stringSelection(\u0026#34;parameters\u0026#34;, \u0026#34;color\u0026#34;, \u0026#34;blue\u0026#34;, StringMatchMode.es()), ParameterSelection.stringSelection(\u0026#34;parameters\u0026#34;, \u0026#34;color\u0026#34;, \u0026#34;yellow\u0026#34;, StringMatchMode.es())); query.and().with(owner, colors); List\u0026lt;Resource\u0026gt; cars = tx.doQuery(query); } "},{"uri":"https://strolch.li/development/","title":"Development","tags":[],"description":"","content":"Prerequisites To start developing Strolch you need an installed:\n Java JDK 11 Apache Maven 3.x Building Strolch Setting up Strolch is just a few lines:\ngit clone https://github.com/4treesCH/strolch.git cd strolch mvn clean install -DskipTests Note: To run the tests you will need to configure the PostgreSQL Databases. See the README in the module.\n After running the Maven build, you will have a full build of all Strolch projects. Now you can start modifying the projects, and add your own features, or, far more interesting, start developing your projects using the Strolch agent.\nCreating a Strolch App To create your own Strolch App, you can use Maven\u0026rsquo;s archetype generation. There are two versions, one is a simple Java App which you can use to directly access the Strolch runtime, and the second is to create a Java Web App, which is the usual way to run Strolch runtimes.\nNote: you need to have installed Strolch to your local maven repo, otherwise the archetype won\u0026rsquo;t be available.\n Creating a Java Strolch Web App The following shows the maven command to create the new maven project. Note that you should replace the placeholders in the brackets:\nmvn archetype:generate \\ -DarchetypeGroupId=li.strolch \\ -DarchetypeArtifactId=li.strolch.mvn.archetype.webapp \\ -DarchetypeVersion=1.6.0-SNAPSHOT \\ -DgroupId=\u0026lt;my.groupid\u0026gt; \\ -DartifactId=\u0026lt;my-artifactId\u0026gt; \\ -Dversion=\u0026lt;my.version\u0026gt; \\ -DappName=\u0026#34;\u0026lt;my app name\u0026gt;\u0026#34; Install the web dependencies The Strolch Web App uses NodeJS v11.x to build the web dependencies. Please download the relevant platform\u0026rsquo;s package, unpack it, and add the bin directory to your path variable.\nOnce NodeJS is installed, then you can prepare the web dependencies:\ncd src/main/webapp/ npm install gulp -g npm install gulp Note: Whenever the bower.json is changed then you should again call npm install inside the webapp folder.\n Building the WAR Building the WAR uses the package maven goal, but to have the optimized WAR use the release profile:\nmvn clean package -Prelease Happy coding =))\nCreating a simple Java Strolch App The following shows the maven command to create the new maven project. Note that you should replace the placeholders in the brackets:\nmvn archetype:generate \\ -DarchetypeGroupId=li.strolch \\ -DarchetypeArtifactId=li.strolch.mvn.archetype.main \\ -DarchetypeVersion=1.6.0-SNAPSHOT \\ -DgroupId=\u0026lt;my.groupid\u0026gt; \\ -DartifactId=\u0026lt;my-artifactId\u0026gt; \\ -Dversion=\u0026lt;my.version\u0026gt; \\ -DappName=\u0026#34;\u0026lt;my app name\u0026gt;\u0026#34; You change into the directory of the new project and then build the project by calling:\ncd \u0026lt;my-artifactId\u0026gt; mvn clean package Start the program using:\nmvn exec:java Happy coding =))\nTools used The following tools are used to develop Strolch and Strolch-based projects:\n IntelliJ Apache Maven Git SCM "},{"uri":"https://strolch.li/plc/example-set-up/","title":"Example Set-Up","tags":[],"description":"","content":"Example Set-Up This example setup describes the movement of containers over conveyors. The conveyors have motors which can be started and stopped by a GPIO output pin controlled on a Raspberry Pi and each conveyor has a light barrier to detect the occupancy of a container and the Raspberry Pi detects this on GPIO input pins.\nFurther at each conveyor location is a barcode reader to read the ID of a container.\nThe general idea is that the PLC notifies a Strolch agent of changes, and only turns conveyors on, when the agent gives the command. Thus the agent handles business logic and the PLC controls the I/Os.\nNew Project First create a new Strolch Web project using the Strolch Maven archetype Now add the following Maven dependencies: \u0026lt;project\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;strolch.version\u0026gt;1.6.0-SNAPSHOT\u0026lt;/strolch.version\u0026gt; \u0026lt;strolch.plc.version\u0026gt;0.1.0-SNAPSHOT\u0026lt;/strolch.plc.version\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;dependencyManagement\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;li.strolch.bom\u0026lt;/artifactId\u0026gt; \u0026lt;type\u0026gt;pom\u0026lt;/type\u0026gt; \u0026lt;version\u0026gt;${strolch.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;import\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;strolch-plc-bom\u0026lt;/artifactId\u0026gt; \u0026lt;type\u0026gt;pom\u0026lt;/type\u0026gt; \u0026lt;version\u0026gt;${strolch.plc.version}\u0026lt;/version\u0026gt; \u0026lt;scope\u0026gt;import\u0026lt;/scope\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;/dependencyManagement\u0026gt; \u0026lt;dependencies\u0026gt; \u0026lt;!-- PLC --\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;strolch-plc-core\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;strolch-plc-rest\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;li.strolch\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;strolch-plc-gw-client\u0026lt;/artifactId\u0026gt; \u0026lt;/dependency\u0026gt; \u0026lt;/dependencies\u0026gt; \u0026lt;/project\u0026gt; Add a bower dependency: \u0026quot;strolch-wc-plc\u0026quot;: \u0026quot;4treesCH/strolch-wc-plc#^0.3.4\u0026quot; to src/main/webapp/bower.json\nAfter adding the dependency, run gulp in the webapp directory. Gulp should have been installed through the instructions from the development page.\n Now we need to add the PLC web views to our new project. This is added in the src/main/webapp/app/src/c-app.html file. Add the following:\n \u0026lt;!-- HTML Imports --\u0026gt; \u0026lt;link rel=\u0026#34;import\u0026#34; href=\u0026#34;../bower_components/strolch-wc-plc/strolch-wc-plc-connections.html\u0026#34;\u0026gt; \u0026lt;link rel=\u0026#34;import\u0026#34; href=\u0026#34;../bower_components/strolch-wc-plc/strolch-wc-plc-logical-devices.html\u0026#34;\u0026gt; \u0026lt;!-- Change default-page to plcLogicalDevices --\u0026gt; \u0026lt;c-app-routing id=\u0026#34;appRouting\u0026#34; login-page=\u0026#34;login\u0026#34; default-page=\u0026#34;plcLogicalDevices\u0026#34; auth-valid=\u0026#34;[[authTokenValid]]\u0026#34; page=\u0026#34;{{page}}\u0026#34; route-tail=\u0026#34;{{routeTail}}\u0026#34; use-hash-as-path\u0026gt;\u0026lt;/c-app-routing\u0026gt; \u0026lt;!-- Add the new pages in the iron-pages element: --\u0026gt; \u0026lt;template is=\u0026#34;dom-if\u0026#34; if=\u0026#34;[[equal(page, \u0026#39;plcConnections\u0026#39;)]]\u0026#34; restamp\u0026gt; \u0026lt;strolch-wc-plc-connections id=\u0026#34;plcConnections\u0026#34; base-path=\u0026#34;../\u0026#34; base-rest-path=\u0026#34;[[baseRestPath]]\u0026#34; route=\u0026#34;{{subroute}}\u0026#34;\u0026gt;\u0026lt;/strolch-wc-plc-connections\u0026gt; \u0026lt;/template\u0026gt; \u0026lt;template is=\u0026#34;dom-if\u0026#34; if=\u0026#34;[[equal(page, \u0026#39;plcLogicalDevices\u0026#39;)]]\u0026#34; restamp\u0026gt; \u0026lt;strolch-wc-plc-logical-devices id=\u0026#34;plcLogicalDevices\u0026#34; base-path=\u0026#34;../\u0026#34; base-rest-path=\u0026#34;[[baseRestPath]]\u0026#34; base-ws-path=\u0026#34;[[baseWsPath]]\u0026#34; route=\u0026#34;{{subroute}}\u0026#34;\u0026gt;\u0026lt;/strolch-wc-plc-logical-devices\u0026gt; \u0026lt;/template\u0026gt; // add a new property to the WebSocket path for observing changes on the PLC wsObserverPath: { type: String, value: function () { return CustomWeb.baseWsPath + \u0026#34;/plc/observer\u0026#34;; } } Don\u0026rsquo;t forget to add the PLC Rest classes to your ResourceConfig @ApplicationPath(\u0026#34;rest\u0026#34;) public class RestfulApplication extends ResourceConfig { public RestfulApplication() { ... // strolch plc services packages(PlcConnectionsResource.class.getPackage().getName()); ... } } Now we need to configure the PLC\u0026rsquo;s runtime by modifying runtime/StrolchConfiguration.xml and adding the following: \u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;dev\u0026#34;\u0026gt; \u0026lt;!-- This component configures the PlcHandler by loading the PlcConnections, PlcAddresses and PlcTelegrams --\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;PlcHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.plc.core.PlcHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.plc.core.DefaultPlcHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;RealmHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;!-- The component handling the low level connections --\u0026gt; \u0026lt;plcClass\u0026gt;li.strolch.plc.core.hw.DefaultPlc\u0026lt;/plcClass\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;!-- This component handles registrations of the PlcServices, i.e. your PLC business logic --\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;PlcServiceInitializer\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.plc.core.PlcServiceInitializer\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.plc.example.CustomPlcServiceInitializer\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PlcHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;!-- This component notifies a Strolch agent of changes on the PLC only if you have a Strolch server with a configured li.strolch.plc.gw.server.PlcServerWebSocketEndpoint ready to accept connections --\u0026gt; \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;PlcGwClientHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.plc.gw.client.PlcGwClientHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.plc.gw.client.PlcGwClientHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PlcHandler\u0026lt;/depends\u0026gt; \u0026lt;depends\u0026gt;PlcServiceInitializer\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;plcId\u0026gt;plc-01\u0026lt;/plcId\u0026gt; \u0026lt;gwUsername\u0026gt;plc-01\u0026lt;/gwUsername\u0026gt; \u0026lt;gwPassword\u0026gt;plc-01\u0026lt;/gwPassword\u0026gt; \u0026lt;gwServerUrl\u0026gt;ws://localhost:8080/agent/websocket/strolch/plc \u0026lt;/gwServerUrl\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;/env\u0026gt; \u0026lt;/StrolchConfiguration\u0026gt; Now we add the custom classes we just declared. PlcServiceInitializer\nimport java.util.ArrayList; import java.util.List; import li.strolch.plc.example.services.*; import li.strolch.agent.api.ComponentContainer; import li.strolch.plc.core.PlcHandler; import li.strolch.plc.core.PlcService; import li.strolch.plc.core.PlcServiceInitializer; public class CustomPlcServiceInitializer extends PlcServiceInitializer { public CustomPlcServiceInitializer(ComponentContainer container, String componentName) { super(container, componentName); } @Override protected List\u0026lt;PlcService\u0026gt; getPlcServices(PlcHandler plcHandler) { ArrayList\u0026lt;PlcService\u0026gt; plcServices = new ArrayList\u0026lt;\u0026gt;(); StartupPlcService startupPlcService = new StartupPlcService(plcHandler); ConveyorPlcService conveyorPlcService = new ConveyorPlcService(plcHandler); plcServices.add(conveyorPlcService); plcServices.add(startupPlcService); return plcServices; } } PlcPostInitializer\nimport li.strolch.agent.api.ComponentContainer; import li.strolch.plc.core.PlcPostInitializer; public class CustomPostInitializer extends PlcPostInitializer { public CustomPostInitializer(ComponentContainer container, String componentName) { super(container, componentName); } // override the initialize(), start(), stop() and destroy() methods as needed } In the CustomPlcServiceInitializer we added two PlcServices, for which the code is missing. The following are simple examples: StartupPlcService\nimport li.strolch.persistence.api.StrolchTransaction; import li.strolch.plc.core.PlcHandler; import li.strolch.plc.core.PlcService; public class StartupPlcService extends PlcService { public static final String PLC = \u0026#34;PLC\u0026#34;; public static final String STARTED = \u0026#34;Started\u0026#34;; public static final String STOPPED = \u0026#34;Stopped\u0026#34;; public StartupPlcService(PlcHandler plcHandler) { super(plcHandler); } @Override public void start(StrolchTransaction tx) { send(PLC, STARTED); super.start(tx); } @Override public void stop() { send(PLC, STOPPED); super.stop(); } } ConveyorPlcService\nimport java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import li.strolch.plc.core.PlcHandler; import li.strolch.plc.core.PlcService; import li.strolch.plc.model.PlcAddress; public class ConveyorPlcService extends PlcService { public static final int BOX_TRANSFER_DURATION = 30; private static final String R_CONVEYOR_01 = \u0026#34;Conveyor01\u0026#34;; private static final String A_START_BUTTON = \u0026#34;StartButton\u0026#34;; private static final String T_MOTOR_ON = \u0026#34;MotorOn\u0026#34;; private static final String T_MOTOR_OFF = \u0026#34;MotorOff\u0026#34;; private static final String A_BOX_DETECTED = \u0026#34;BoxDetected\u0026#34;; private boolean motorOn; private ScheduledFuture\u0026lt;?\u0026gt; motorStopTask; public ConveyorPlcService(PlcHandler plcHandler) { super(plcHandler); } @Override public void handleNotification(PlcAddress address, Object value) { String resource = address.resource; String action = address.action; if (!resource.equals(\u0026#34;Conveyor01\u0026#34;)) throw new IllegalStateException(\u0026#34;Unexpected resource \u0026#34; + resource); boolean active = (boolean) value; if (action.equals(A_START_BUTTON)) { if (active) { logger.info(\u0026#34;Start button pressed. Starting motors...\u0026#34;); send(R_CONVEYOR_01, T_MOTOR_ON); this.motorOn = true; scheduleStopTask(); } } else if (action.equals(A_BOX_DETECTED)) { if (active \u0026amp;\u0026amp; this.motorOn) { logger.info(\u0026#34;Container detected, refreshing stop task...\u0026#34;); scheduleStopTask(); } } else { logger.info(\u0026#34;Unhandled notification \u0026#34; + address.toKeyAddress()); } } private void scheduleStopTask() { if (this.motorStopTask != null) this.motorStopTask.cancel(false); this.motorStopTask = schedule(this::stopMotor, BOX_TRANSFER_DURATION, TimeUnit.SECONDS); } private void stopMotor() { send(R_CONVEYOR_01, T_MOTOR_OFF); } @Override public void register() { this.plcHandler.register(R_CONVEYOR_01, A_START_BUTTON, this); this.plcHandler.register(R_CONVEYOR_01, A_BOX_DETECTED, this); super.register(); } @Override public void unregister() { this.plcHandler.unregister(R_CONVEYOR_01, A_START_BUTTON, this); this.plcHandler.unregister(R_CONVEYOR_01, A_BOX_DETECTED, this); super.unregister(); } } Now the last part is to add the model, i.e. PlcConnections, PlcAddresses and PlcTelegrams. To have less configuration files and make it easier to reconfigure at runtime, this data is stored in normal Strolch Resources.\nIn this example we will use simple Raspberry Pi GPIOs. For convenience, and also when sharing I/O definitions with external partners, it is easier to use a CSV file to define the I/Os and then use the PlcAddressGenerator to generate and validate the model.\nFor this purpose in this example, we will use one conveyor with 2 inputs and 1 output. The CSV file should have the following content:\n Description,Type,SubType,Device,Pin,Resource,Action1,Action2,Connection,DeviceId Material Flow,Group,,,,,,,,MaterialFlow Conveyor 1,Input,Pin,,4,Conveyor,Occupied,,raspiBcmGpioInput Conveyor 1,Input,Pin,,17,Conveyor,BoxDetected,,raspiBcmGpioInput Conveyor 1,Output,Pin,,18,Conveyor,MotorOn,MotorOff,raspiBcmGpioOutput The CSV headers are as follows:\n Description a simple description for this PlcAddress Type Group Must be the first line and generates a PlcLogicalDevice, all succeeding lines are grouped to this device. Add additional to group further devices Input defines a boolean input Output defines a boolean output Virtual defines a virtual address which has no corresponding hardware connection. Used for internal communication. DataLogicScanner defines an address to read barcodes from a DataLogic Scanner. The actions must be left empty as the keys Barcode (address), On and Off (telegrams) will be generated. SubType For Input and Output types DevPin, DevPin0 Generates the address as \u0026lt;Connection\u0026gt;.\u0026lt;Device\u0026gt;.\u0026lt;Pin\u0026gt;. DevPin0 decrements the Device and Pin values by one. Pin Generates the address as \u0026lt;Connection\u0026gt;.\u0026lt;Pin\u0026gt;. For Virtual types Boolean String Device Device number Pin The pin number on the device Resource The resource ID with which to notify the agent Action1 The action ID Action2 The second action ID if required Connection The ID of the PlcConnection with which this I/O is attached DeviceId For type Group: Set the ID of this PlcLogicalDevice being generated When you use this file as input for the PlcAddressGenerator, then it will generate PlcLogicalDevice, PlcAddress and PlcTelegram elements:\n\u0026lt;StrolchModel\u0026gt; \u0026lt;Resource Id=\u0026#34;D_MaterialFlow\u0026#34; Name=\u0026#34;MaterialFlow\u0026#34; Type=\u0026#34;PlcLogicalDevice\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;description\u0026#34; Name=\u0026#34;Description\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;Material Flow\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;group\u0026#34; Name=\u0026#34;Group\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;01 Material Flow\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;index\u0026#34; Name=\u0026#34;Index\u0026#34; Type=\u0026#34;Integer\u0026#34; Value=\u0026#34;10\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;relations\u0026#34; Name=\u0026#34;Relations\u0026#34; Type=\u0026#34;Relations\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;addresses\u0026#34; Name=\u0026#34;Addresses\u0026#34; Type=\u0026#34;StringList\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;PlcAddress\u0026#34; Value=\u0026#34;A_Conveyor-Occupied, A_Conveyor-BoxDetected, A_Conveyor-MotorOn\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;telegrams\u0026#34; Name=\u0026#34;Telegrams\u0026#34; Type=\u0026#34;StringList\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;PlcTelegram\u0026#34; Value=\u0026#34;T_Conveyor-MotorOn, T_Conveyor-MotorOff\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Resource Id=\u0026#34;A_Conveyor-Occupied\u0026#34; Name=\u0026#34;Conveyor - Occupied\u0026#34; Type=\u0026#34;PlcAddress\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;description\u0026#34; Name=\u0026#34;Description\u0026#34; Type=\u0026#34;String\u0026#34; Index=\u0026#34;5\u0026#34; Value=\u0026#34;Conveyor 1\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;address\u0026#34; Name=\u0026#34;HW Address\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;PlcConnection\u0026#34; Index=\u0026#34;10\u0026#34; Value=\u0026#34;raspiBcmGpioInput.4\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;resource\u0026#34; Name=\u0026#34;Resource ID for PlcAddress\u0026#34; Type=\u0026#34;String\u0026#34; Index=\u0026#34;20\u0026#34; Value=\u0026#34;Conveyor\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;action\u0026#34; Name=\u0026#34;Action ID for PlcAddress\u0026#34; Type=\u0026#34;String\u0026#34; Index=\u0026#34;30\u0026#34; Value=\u0026#34;Occupied\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;index\u0026#34; Name=\u0026#34;Index\u0026#34; Type=\u0026#34;Integer\u0026#34; Index=\u0026#34;40\u0026#34; Value=\u0026#34;10\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;value\u0026#34; Name=\u0026#34;Value\u0026#34; Type=\u0026#34;Boolean\u0026#34; Index=\u0026#34;100\u0026#34; Value=\u0026#34;false\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Resource Id=\u0026#34;T_Conveyor-MotorOn\u0026#34; Name=\u0026#34;Conveyor - MotorOn\u0026#34; Type=\u0026#34;PlcTelegram\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;description\u0026#34; Name=\u0026#34;Description\u0026#34; Type=\u0026#34;String\u0026#34; Index=\u0026#34;5\u0026#34; Value=\u0026#34;Conveyor 1\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;address\u0026#34; Name=\u0026#34;HW Address\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;PlcConnection\u0026#34; Index=\u0026#34;10\u0026#34; Value=\u0026#34;raspiBcmGpioOutput.18\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;resource\u0026#34; Name=\u0026#34;Resource ID for PlcAddress\u0026#34; Type=\u0026#34;String\u0026#34; Index=\u0026#34;20\u0026#34; Value=\u0026#34;Conveyor\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;action\u0026#34; Name=\u0026#34;Action ID for PlcAddress\u0026#34; Type=\u0026#34;String\u0026#34; Index=\u0026#34;30\u0026#34; Value=\u0026#34;MotorOn\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;index\u0026#34; Name=\u0026#34;Index\u0026#34; Type=\u0026#34;Integer\u0026#34; Index=\u0026#34;40\u0026#34; Value=\u0026#34;10\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;value\u0026#34; Name=\u0026#34;Value\u0026#34; Type=\u0026#34;Boolean\u0026#34; Index=\u0026#34;100\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;/StrolchModel\u0026gt; The PlcLogicalDevice references the PlcAddress and PlcTelegram objects, and is then used in the UI for grouping.\nThe PlcAddress is used to store the current value and defines the keys with which the agent will be notified\nThe PlcTelegram is used to store default values to send, for specific keys. E.g. The action On would send true, and Off would send false. This is semantics, and is defined in each project depending on the hardware.\n Copy the file plc-state.xml to your runtime and reference it by use of a \u0026lt;IncludeFile file=\u0026quot;plc-state.xml\u0026quot; /\u0026gt; element. Modify the PlcId to be the same as the one you defined in the StrolchConfiguration.xml.\n Now that we have a model, the PlcConnections are to be defined. In the previous example we used a Raspberry Pi\u0026rsquo;s GPIOs. This needs to be defined as a PlcConnection:\n \u0026lt;StrolchModel\u0026gt; \u0026lt;Resource Id=\u0026#34;raspiBcmGpioOutput\u0026#34; Name=\u0026#34;Raspi BCM GPIO Output\u0026#34; Type=\u0026#34;PlcConnection\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;className\u0026#34; Name=\u0026#34;Connection Class\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;li.strolch.plc.core.hw.gpio.RaspiBcmGpioOutputConnection\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;state\u0026#34; Name=\u0026#34;Connection State\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Enumeration\u0026#34; Uom=\u0026#34;ConnectionState\u0026#34; Value=\u0026#34;Disconnected\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;stateMsg\u0026#34; Name=\u0026#34;Connection State Msg\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Enumeration\u0026#34; Uom=\u0026#34;ConnectionState\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;inverted\u0026#34; Name=\u0026#34;Inverted\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;false\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;bcmOutputPins\u0026#34; Name=\u0026#34;BCM Output Pins\u0026#34; Type=\u0026#34;IntegerList\u0026#34; Value=\u0026#34;27\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;Resource Id=\u0026#34;raspiBcmGpioInput\u0026#34; Name=\u0026#34;Raspi BCM GPIO Input\u0026#34; Type=\u0026#34;PlcConnection\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;Parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;className\u0026#34; Name=\u0026#34;Connection Class\u0026#34; Type=\u0026#34;String\u0026#34; Value=\u0026#34;li.strolch.plc.core.hw.gpio.RaspiBcmGpioInputConnection\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;state\u0026#34; Name=\u0026#34;Connection State\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Enumeration\u0026#34; Uom=\u0026#34;ConnectionState\u0026#34; Value=\u0026#34;Disconnected\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;stateMsg\u0026#34; Name=\u0026#34;Connection State Msg\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Enumeration\u0026#34; Uom=\u0026#34;ConnectionState\u0026#34; Value=\u0026#34;\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;inverted\u0026#34; Name=\u0026#34;Inverted\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;bcmInputPins\u0026#34; Name=\u0026#34;BCM Input Pins\u0026#34; Type=\u0026#34;IntegerList\u0026#34; Value=\u0026#34;4\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;/Resource\u0026gt; \u0026lt;/StrolchModel\u0026gt; See strolch-plc-example-connections.xml for further examples.\n"},{"uri":"https://strolch.li/documentation/transactions/","title":"Transactions","tags":[],"description":"","content":"Transactions Strolch Transactions play a central role in a Strolch agent. A transaction is opened for a realm, and grants access to the model of the agent. Transactions are implemented as a Java try-with-resources by implementing the AutoCloseable interface. This makes it trivial to understand the scope of a transaction.\nTransactions handle the following:\n Opening and closing database connections Releasing locks to strolch elements, if tx.lock(StrolchRootElement) or tx.lock(Locator) was called Performing Commands by executing them in the added order, and validating them first. Exception handling Auditing Updating observers When a transaction is opened, it is by default read-only, i.e. does not perform any commands when it is closed. Should the TX perform commands, then it is important to call tx.commitOnClose(), but only at the end of the work, so that exception handling can properly work if something goes wrong.\nStrolchTransaction offers a myriad of methods:\n find element by its Locator get methods for elements by type and id, or using a StringParameter or StringListParameter references methods to add, update or remove elements assert privilege access get a new element by its template check if an element exists by type and id get streams for elements add commands for execution Transactions are opened by accessing the realm, but there are convenience methods depending on the use-case:\n In Services: by calling one of the openTx()-methods In Commands: Transactions are already open, use method tx() to get instance. REST API: RestfulStrolchComponent.openTx() Note: don\u0026rsquo;t open a new TX inside a TX for the same realm!\n Important is to always open the transaction as a try-with-resource block and to define if the TX should commit, or not:\ntry (StrolchTransaction tx = openTx(...)) { // read lock our object Locator ferrariLoc = Resource.locatorFor(\u0026#34;Car\u0026#34;, \u0026#34;ferrari\u0026#34;); tx.lock(ferrariLoc); // find a car by locator Resource ferrari = tx.findElement(ferrariLoc); // get a car by ID Resource opel = tx.getResourceBy(\u0026#34;Car\u0026#34;, \u0026#34;opel\u0026#34;, true); // modify ball opel.setName(\u0026#34;Opel Corsa\u0026#34;); tx.update(opel); // get by string reference StringParameter ownerP = ferrari.getParameter(\u0026#34;relations\u0026#34;, \u0026#34;owner\u0026#34;, true); Resource owner = tx.getResourceBy(ownerP, true); // get by string list reference StringListParameter previousOwnersP = opel.getParameter(\u0026#34;relations\u0026#34;, \u0026#34;previousOwners\u0026#34;, true); List\u0026lt;Resource\u0026gt; previousOwners = tx.getResourcesBy(previousOwnersP, true); // check resource exists if (tx.hasResource(\u0026#34;Car\u0026#34;, \u0026#34;audi\u0026#34;)) { Resource audi = tx.getResourceBy(\u0026#34;Car\u0026#34;, \u0026#34;audi\u0026#34;, true); // assert has privilege to remove a car tx.assertHasPrivilege(Operation.REMOVE, audi); // remove the car tx.remove(audi); } // iterate all cars tx.streamResources(\u0026#34;Car\u0026#34;).forEach(car -\u0026gt; { logger.info(\u0026#34;Car: \u0026#34; + car.getId()); }); // commit if TX was changed if (tx.needsCommit()) tx.commitOnClose(); } "},{"uri":"https://strolch.li/documentation/policies/","title":"Policies","tags":[],"description":"","content":"Policies Policies are an integral part when writing business logic in Strolch. In many cases it would suffice to write all such logic in Services and Commands, but as soon as behaviour can change, depending on the element being accessed, then this would quickly lead to many if/else blocks.\nSince writing large if/else blocks is not maintanable in the long run, Strolch offers a different approach. All Strolch elements can store Policy definitions. This is a simple key/value store where the key defines the type of policy, and the value references the policy to use.\nCurrently there are two ways to reference a policy in Strolch, either via a key which defines a further lookup in the PolicyHandler, or directly as the name of the class to instantiate.\nUsing policies in Strolch gives the additional possibility of easily changing the behaviour at runtime, as a Service and/or Command would delegate the behaviour to the currently configured policy on the releveant element.\nPolicies are implemented by defining an abstract class and extends StrolchPolicy. This abstract class then defines the API of the actual policy. A concrete class then extends this abstract class and implements the concrete methods.\nPolicies are registered on Resources, Orders, Activities and Actions. The following shows defining two policies on a Resource, a PlanningPolicy, an ExecutionPolicy in XML:\n\u0026lt;Resource ...\u0026gt; ... \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;PlanningPolicy\u0026#34; Value=\u0026#34;key:SimplePlanning\u0026#34; /\u0026gt; \u0026lt;Policy Type=\u0026#34;ExecutionPolicy\u0026#34; Value=\u0026#34;java:li.strolch.policytest.TestSimulatedExecutionPolicy\u0026#34; /\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Resource\u0026gt; Note how the PlanningPolicy has a value of key:SimplePlanning and the ExecutionPolicy defines a reference to an actual class.\n To use the PolicyHandler, it must be configured in the StrolchConfiguration.xml as follows:\n\u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;PolicyHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.policy.PolicyHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.policy.DefaultPolicyHandler\u0026lt;/impl\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;readPolicyFile\u0026gt;true\u0026lt;/readPolicyFile\u0026gt; \u0026lt;policyConfigFile\u0026gt;StrolchPolicies.xml\u0026lt;/policyConfigFile\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; And this policy handler implementation requires a file where the lookups for the policies is defined, e.g.:\n\u0026lt;StrolchPolicies\u0026gt; \u0026lt;PolicyType Type=\u0026#34;PlanningPolicy\u0026#34; Api=\u0026#34;li.strolch.policytest.TestPlanningPolicy\u0026#34;\u0026gt; \u0026lt;Policy Key=\u0026#34;SimplePlanning\u0026#34; Class=\u0026#34;li.strolch.policytest.TestSimplePlanningPolicy\u0026#34;/\u0026gt; \u0026lt;/PolicyType\u0026gt; \u0026lt;PolicyType Type=\u0026#34;ConfirmationPolicy\u0026#34; Api=\u0026#34;li.strolch.policytest.TestConfirmationPolicy\u0026#34;\u0026gt; \u0026lt;Policy Key=\u0026#34;NoConfirmation\u0026#34; Class=\u0026#34;li.strolch.policytest.TestNoConfirmationPolicy\u0026#34;/\u0026gt; \u0026lt;/PolicyType\u0026gt; \u0026lt;/StrolchPolicies\u0026gt; Now at runtime we can access the policies:\nPolicyHandler policyHandler=getComponent(PolicyHandler.class); try(StrolchTransaction tx=openTx()){ Resource res=tx.getResourceBy(\u0026#34;TestType\u0026#34;,\u0026#34;MyTestResource\u0026#34;); PolicyDefs policyDefs=res.getPolicyDefs(); PolicyDef planningPolicyDef=policyDefs.getPolicyDef(\u0026#34;PlanningPolicy\u0026#34;); PlanningPolicy planningPolicy=policyHandler.getPolicy(planningPolicyDef,tx); planningPolicy.plan(...); PolicyDef executionPolicyDef=res.getPolicyDefs().getPolicyDef(\u0026#34;ExecutionPolicy\u0026#34;); ExecutionPolicy executionPolicy=policyHandler.getPolicy(executionPolicyDef,tx); executionPolicy.execute(...); } "},{"uri":"https://strolch.li/documentation/observers/","title":"Observers","tags":[],"description":"","content":"Observers All changes done in a Strolch transaction are recorded and then propagated to any registered observers.\nThe observer feature is opt-in and is configured for each realm. In the StrolchConfiguration.xml file enable observers by adding the enableObserverUpdates property per realm:\n\u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;dev\u0026#34;\u0026gt; ... \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;RealmHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.agent.api.RealmHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.agent.impl.DefaultRealmHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;realms\u0026gt;defaultRealm, otherRealm\u0026lt;/realms\u0026gt; \u0026lt;enableObserverUpdates\u0026gt;true\u0026lt;/enableObserverUpdates\u0026gt; \u0026lt;dataStoreMode\u0026gt;TRANSIENT\u0026lt;/dataStoreMode\u0026gt; \u0026lt;dataStoreFile\u0026gt;StrolchModel.xml\u0026lt;/dataStoreFile\u0026gt; \u0026lt;enableObserverUpdates.otherRealm\u0026gt;true\u0026lt;/enableObserverUpdates.otherRealm\u0026gt; \u0026lt;dataStoreMode.otherRealm\u0026gt;TRANSIENT\u0026lt;/dataStoreMode.otherRealm\u0026gt; \u0026lt;dataStoreFile.otherRealm\u0026gt;StrolchModel.xml\u0026lt;/dataStoreFile.otherRealm\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;/env\u0026gt; ... \u0026lt;/StrolchConfiguration\u0026gt; Registering for updates is done by registering an Observer on the ObserverHandler of the realm itself:\nObserverHandler observerHandler = container.getRealm(StrolchConstants.DEFAULT_REALM).getObserverHandler(); observerHandler.registerObserver(Tags.RESOURCE, new Observer() { @Override public void update(String key, List\u0026lt;StrolchRootElement\u0026gt; elements) { logger.info(elements.size() + \u0026#34; resources were updated!\u0026#34;); } @Override public void remove(String key, List\u0026lt;StrolchRootElement\u0026gt; elements) { logger.info(elements.size() + \u0026#34; resources were removed!\u0026#34;); } @Override public void add(String key, List\u0026lt;StrolchRootElement\u0026gt; elements) { logger.info(elements.size() + \u0026#34; resources were added!\u0026#34;); } }); "},{"uri":"https://strolch.li/documentation/versioning/","title":"Versioning","tags":[],"description":"","content":"Versioning One of Strolch\u0026rsquo;s features that sets it apart from other frameworks, is that versioning is baked into Strolch\u0026rsquo;s fabric. The feature is opt-in, as it is not required in all projects, but it only needs enabling, for all modifications to objects to be versioned, so that rollbacks can be done when needed.\nThe feature is enabled for each realm. In the StrolchConfiguration.xml file enable it by adding the enableVersioning propery per realm:\n\u0026lt;StrolchConfiguration\u0026gt; \u0026lt;env id=\u0026#34;dev\u0026#34;\u0026gt; ... \u0026lt;Component\u0026gt; \u0026lt;name\u0026gt;RealmHandler\u0026lt;/name\u0026gt; \u0026lt;api\u0026gt;li.strolch.agent.api.RealmHandler\u0026lt;/api\u0026gt; \u0026lt;impl\u0026gt;li.strolch.agent.impl.DefaultRealmHandler\u0026lt;/impl\u0026gt; \u0026lt;depends\u0026gt;PrivilegeHandler\u0026lt;/depends\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;realms\u0026gt;defaultRealm, otherRealm\u0026lt;/realms\u0026gt; \u0026lt;enableVersioning\u0026gt;true\u0026lt;/enableVersioning\u0026gt; \u0026lt;dataStoreMode\u0026gt;TRANSIENT\u0026lt;/dataStoreMode\u0026gt; \u0026lt;dataStoreFile\u0026gt;StrolchModel.xml\u0026lt;/dataStoreFile\u0026gt; \u0026lt;enableVersioning.otherRealm\u0026gt;true\u0026lt;/enableVersioning.otherRealm\u0026gt; \u0026lt;dataStoreMode.otherRealm\u0026gt;TRANSIENT\u0026lt;/dataStoreMode.otherRealm\u0026gt; \u0026lt;dataStoreFile.otherRealm\u0026gt;StrolchModel.xml\u0026lt;/dataStoreFile.otherRealm\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/Component\u0026gt; \u0026lt;/env\u0026gt; ... \u0026lt;/StrolchConfiguration\u0026gt; Once versioning is enabled, versioning is handled automatically. The API for versioning is implemented on the ElementMaps.\nExample: Revert to previous version of a Resource:\nResource res = tx.getResourceBy(\u0026#34;TestType\u0026#34;, \u0026#34;MyTestResource\u0026#34;); ResourceMap resourceMap = tx.getResourceMap(); Resource previousVersion = resourceMap.revertToVersion(tx, res); // or Resource previousVersion = resourceMap.revertToVersion(tx, \u0026#34;TestType\u0026#34;, \u0026#34;MyTestResource\u0026#34;, 1); Example: Retrieve all versions of a Resource:\nList\u0026lt;Resource\u0026gt; versions = resourceMap.getVersionsFor(tx, \u0026#34;TestType\u0026#34;, \u0026#34;MyTestResource\u0026#34;); Note: When reverting to a previous version, it is important to remember, that any references on an element to other elements will also be restored. As long as the relationship is to the same element, then this is not an issue, but should the relationship have changed, then it this must be handled and the user performing a revert be allowed to decided which element to reference in the reverted version.\n "},{"uri":"https://strolch.li/documentation/reports/","title":"Reports","tags":[],"description":"","content":"Reports Since Strolch has a generic model, it was rather straight forward to create a simple API for writing reports. In Strolch a report is defined by using its own model, i.e. a Report is a Resource of type Report.\nA report consists of the following parts:\n policy definition, thus allowing extensions basic configuration like base object type, order direction, etc. column definitions joins ordering definition filters An example of a report is as follows:\n\u0026lt;Resource Id=\u0026#34;stockReport\u0026#34; Name=\u0026#34;Stock Report\u0026#34; Type=\u0026#34;Report\u0026#34;\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;objectType\u0026#34; Index=\u0026#34;20\u0026#34; Hidden=\u0026#34;false\u0026#34; Name=\u0026#34;Object Type\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Player\u0026#34; Value=\u0026#34;Player\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;descending\u0026#34; Name=\u0026#34;Descending order\u0026#34; Type=\u0026#34;Boolean\u0026#34; Value=\u0026#34;true\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;ordering\u0026#34; Name=\u0026#34;Ordering\u0026#34; Type=\u0026#34;Ordering\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;name\u0026#34; Name=\u0026#34;Name\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Player\u0026#34; Value=\u0026#34;$name\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;noTeamFilter\u0026#34; Name=\u0026#34;Filter\u0026#34; Type=\u0026#34;Filter\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;policy\u0026#34; Name=\u0026#34;Filter Policy\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;ReportFilterPolicy\u0026#34; Uom=\u0026#34;key:Equals\u0026#34; Value=\u0026#34;!\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;fieldRef\u0026#34; Name=\u0026#34;Field reference\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Slot\u0026#34; Value=\u0026#34;Bags/relations/team\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;columns\u0026#34; Name=\u0026#34;Display Columns\u0026#34; Type=\u0026#34;Display\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;name\u0026#34; Name=\u0026#34;Player\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Player\u0026#34; Value=\u0026#34;$name\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;birthDate\u0026#34; Name=\u0026#34;Birth date\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Player\u0026#34; Value=\u0026#34;Bags/parameters/birthDate\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;team\u0026#34; Name=\u0026#34;Team\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Team\u0026#34; Value=\u0026#34;$name\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;joins\u0026#34; Name=\u0026#34;Joins\u0026#34; Type=\u0026#34;Joins\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;Team\u0026#34; Index=\u0026#34;10\u0026#34; Hidden=\u0026#34;false\u0026#34; Name=\u0026#34;Team\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Team\u0026#34; Value=\u0026#34;Player\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;Policies\u0026gt; \u0026lt;Policy Type=\u0026#34;ReportPolicy\u0026#34; Value=\u0026#34;java:li.strolch.report.policy.GenericReport\u0026#34;/\u0026gt; \u0026lt;/Policies\u0026gt; \u0026lt;/Resource\u0026gt; This report\n shows all Resources of type player (parameter objectType) marks the object type to be show in the filter criteria (default), and that its sorting index is at 20. orders the report by player\u0026rsquo;s name (parameter bag ordering) filters out all players with no team assigned (parameter bag noTeamFilter) defines three columns: Player, Birth date, Team (paramger bag columns) joins in the resource of type Team Uses the GenericReport class to generate the report GenericReport The default generic report implemented in Strolch has the following features and options:\nParameters The parameters bag can contain the following parameters:\n objectType the base type of object to get the input for the report. This means that the Interpretation is set to one of:\n Resource-Ref Order-Ref Activity-Ref and that the UOM and value of the parameter is set to the type of element with which to retrieve the elements from the strolch model.\n descending boolean flag to define if sorting is in descending order\n allowMissingColumns flag to define if no exception should be thrown if a column is missing\n dateRangeSel defines a lookup parameter to use as a date range selector. This requires input when executing the report\n Note: that the attributes Hidden and Index define the visibility and sorting index as filter criteria respectively.\n Lookups Many of the features of the generic report rely on looking up a value on the referenced element. The following shows the ways that a lookup can be performed:\n $id lookup the ID of the element $name lookup the name of the element $type lookup the type of the element $date lookup the date of the element (only possible on Order and Activity elements) $state lookup the state of the element (only possible on Order and Activity elements) Bags/\u0026lt;bag_id\u0026gt;/\u0026lt;param_id\u0026gt; a lookup on the selected element by bag ID and parameter ID $search:\u0026lt;parent_ref_id\u0026gt;:Bags/\u0026lt;bag_id\u0026gt;/\u0026lt;param_id\u0026gt; searches for a parameter with the given bag and parameter, and if it does not exist, looks for the parent with the given parent_ref_id on the element. This allows a recursive search up a tree of elements which all have the same parameter referencing a parent. relations bag Note: these definitions are set as the value of a Parameter, and the Interpretation and UOM of the parameter is used to find the element on which to perform the lookup. I.e. the following definition:\n \u0026lt;Parameter Id=\u0026#34;name\u0026#34; Name=\u0026#34;Player\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Player\u0026#34; Value=\u0026#34;$name\u0026#34;/\u0026gt; defines that we want to lookup the name of the resource of type Player.\nOrdering Ordering, i.e sorting is done by adding the parameter bag with the id ordering and each parameter defines a column to order by. The sequence of the ordering is defined by the index value assigned to each parameter.\nFiltering Filtering use additional Strolch Policies which implement the operator function. I.e. performing an equals, etc. The following ReportFilterPolicy are available and should be added in your StrolchPolicies.xml file:\n\u0026lt;StrolchPolicies\u0026gt; ... \u0026lt;PolicyType Type=\u0026#34;ReportFilterPolicy\u0026#34; Api=\u0026#34;li.strolch.report.policy.ReportFilterPolicy\u0026#34;\u0026gt; \u0026lt;Policy Key=\u0026#34;GreaterThan\u0026#34; Class=\u0026#34;li.strolch.report.policy.GreaterThanReportFilter\u0026#34;/\u0026gt; \u0026lt;Policy Key=\u0026#34;LessThan\u0026#34; Class=\u0026#34;li.strolch.report.policy.LessThanReportFilter\u0026#34;/\u0026gt; \u0026lt;Policy Key=\u0026#34;Equals\u0026#34; Class=\u0026#34;li.strolch.report.policy.EqualsReportFilter\u0026#34;/\u0026gt; \u0026lt;Policy Key=\u0026#34;Contains\u0026#34; Class=\u0026#34;li.strolch.report.policy.ContainsReportFilter\u0026#34;/\u0026gt; \u0026lt;Policy Key=\u0026#34;IsIn\u0026#34; Class=\u0026#34;li.strolch.report.policy.IsInReportFilter\u0026#34;/\u0026gt; \u0026lt;Policy Key=\u0026#34;ValueRef\u0026#34; Class=\u0026#34;li.strolch.report.policy.ValueRefReportFilter\u0026#34;/\u0026gt; \u0026lt;/PolicyType\u0026gt; ... \u0026lt;/StrolchPolicies\u0026gt; From this we can see that we can perform a GreaterThan, LessThan and Equals filtering. These filters can also be negated by prefixing the filter value with an exclamation mark (!).\nA special case for the filter values are filters on dates. If you are filtering on a date, then you can use the special operator now(). This filter will use the current date and time and will add/subtract the ISO8601 period passed as an argument to the operator.\nThe following shows examples of these filters:\n\u0026lt;ParameterBag Id=\u0026#34;minQtyFilter\u0026#34; Name=\u0026#34;Filter\u0026#34; Type=\u0026#34;Filter\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;policy\u0026#34; Name=\u0026#34;Filter Policy\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;ReportFilterPolicy\u0026#34; Uom=\u0026#34;key:GreaterThan\u0026#34; Value=\u0026#34;10\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;fieldRef\u0026#34; Name=\u0026#34;Field reference\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Product\u0026#34; Value=\u0026#34;Bags/parameters/quantity\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;notEmptyFilter\u0026#34; Name=\u0026#34;Filter\u0026#34; Type=\u0026#34;Filter\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;policy\u0026#34; Name=\u0026#34;Filter Policy\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;ReportFilterPolicy\u0026#34; Uom=\u0026#34;key:Equals\u0026#34; Value=\u0026#34;!\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;fieldRef\u0026#34; Name=\u0026#34;Field reference\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Team\u0026#34; Value=\u0026#34;Bags/relations/team\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; \u0026lt;ParameterBag Id=\u0026#34;threeMonthsAgoFilter\u0026#34; Name=\u0026#34;Filter\u0026#34; Type=\u0026#34;Filter\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;policy\u0026#34; Name=\u0026#34;Filter Policy\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;ReportFilterPolicy\u0026#34; Uom=\u0026#34;key:LessThan\u0026#34; Value=\u0026#34;now(-P3M)\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;fieldRef\u0026#34; Name=\u0026#34;Field reference\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;FromStock\u0026#34; Value=\u0026#34;$date\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; Note: One parameter defines which policy gets used and the key:\u0026lt;name\u0026gt; value references a policy defined in the StrolchPolicies.xml file. Further the lookup is defined in the fieldRef parameter.\n Joins To add columns from data which is not on the element denoted by the base object type, we can join further elements. This is done by adding the parameter bag joins and then each parameter references an element to join. The joining is done as follows:\n The Intepretation and UOM define which object we want to join, i.e. resource of type foo The value of the parameter defines the type of element on which to find the reference The join ordering is not relevant, as the tree is traversed accordingly At least one join must reference the base object type The lookup of the join is done by finding a parameter with any ID, which has the same Interpretation and UOM as the join definition The attributes Hidden and Index define the visibility and sorting index as filter criteria respectively. Thus the following:\n\u0026lt;ParameterBag Id=\u0026#34;joins\u0026#34; Name=\u0026#34;Joins\u0026#34; Type=\u0026#34;Joins\u0026#34;\u0026gt; \u0026lt;Parameter Id=\u0026#34;Team\u0026#34; Index=\u0026#34;10\u0026#34; Hidden=\u0026#34;false\u0026#34; Name=\u0026#34;Team\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Team\u0026#34; Value=\u0026#34;Player\u0026#34;/\u0026gt; \u0026lt;Parameter Id=\u0026#34;Country\u0026#34; Index=\u0026#34;5\u0026#34; Hidden=\u0026#34;false\u0026#34; Name=\u0026#34;Team\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Country\u0026#34; Value=\u0026#34;Team\u0026#34;/\u0026gt; \u0026lt;/ParameterBag\u0026gt; Performs two joins: First we join a resource of type Team by finding the relevant parameter on the Player resource, and then we lookup a resource of type Country on the previously joined Team resource.\nExecution of Reports To execute a reports, we must instantiate the Report and can then directly generate a JsonObject stream, which we can then pipe to a browser, file, etc.:\nStream\u0026lt;JsonObject\u0026gt; jsonObjectStream = new Report(tx, reportId).doReportAsJson(); If you prefer a CSV report:\ntry (CSVPrinter csvP = new CSVPrinter(new OutputStreamWriter(out), CSVFormat.DEFAULT.withHeader(headers).withDelimiter(\u0026#39;;\u0026#39;))) { // do report without AsJson, and then iterating each row and sending to a CSV writer report.doReport().forEach(row -\u0026gt; { try { csvP.printRecord(row.valueStream().collect(Collectors.toList())); // add to CSV } catch (Exception e) { logger.error(\u0026#34;Could not write CSV row\u0026#34;, e); } }); } Filter Criteria Predefining filters is a good start, but in some case you only want a portion of the actual filtered data. For instance if you make a stock report, you might only want one location. This information is dynamic and thus not stored on the report definition.\nTo perform these dynamic filterings, one would call the filter()-method on the report, passing the type of element to be filtered, and to which element IDs to reduce the report data to. The following reduces the report to only return the rows with the product01 Product and location02 Location elements:\nnew Report(tx, \u0026#34;stockReport\u0026#34;) .filter(\u0026#34;Product\u0026#34;, \u0026#34;product01\u0026#34;) .filter(\u0026#34;Location\u0026#34;, \u0026#34;location02\u0026#34;) .doReportAsJson() It is possible to find the possible filter criteria dynamically using the generateFilterCriteria() method.\nDate Range Filtering The last option to filter dynamically is using a date range selector. Define the dateRangeSel lookup parameter, and then set the date range on the instantiated report:\nModel the report in XML:\n\u0026lt;ParameterBag Id=\u0026#34;parameters\u0026#34; Name=\u0026#34;parameters\u0026#34; Type=\u0026#34;Parameters\u0026#34;\u0026gt; ... \u0026lt;Parameter Id=\u0026#34;dateRangeSel\u0026#34; Name=\u0026#34;Date Range Selector\u0026#34; Type=\u0026#34;String\u0026#34; Interpretation=\u0026#34;Resource-Ref\u0026#34; Uom=\u0026#34;Product\u0026#34; Value=\u0026#34;Bags/parameters/expirationDate\u0026#34;/\u0026gt; ... \u0026lt;/ParameterBag\u0026gt; And now call the report in Java:\nDate from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000); Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000); DateRange dateRange = new DateRange().from(from, true).to(to, false); List\u0026lt;JsonObject\u0026gt; result = new Report(tx, \u0026#34;stockReport\u0026#34;) // .filter(\u0026#34;Product\u0026#34;, \u0026#34;product01\u0026#34;) // .dateRange(dateRange) // .doReportAsJson() Note: See the GenericReportTest for examples.\n "},{"uri":"https://strolch.li/documentation/priviles/","title":"Privileges","tags":[],"description":"","content":"Privileges No framework is complete without user management and privilege validation. The basic form would be Users and Roles, and then validating that an authenticated user has a given role. In Strolch we go a step further: A User has roles assigned, and each role has a set of Privileges. The privileges can overlap, a validation is performed to make sure that the one role doesn\u0026rsquo;t deny and another role allows a specific action.\nAs explained in the Privilege Configuration section, users are defined in the PrivilegeUsers.xml file, and roles are defined in the PrivilegeRoles.xml file.\nLet\u0026rsquo;s assume the following user and role definition:\n\u0026lt;Users\u0026gt; \u0026lt;User userId=\u0026#34;1\u0026#34; username=\u0026#34;jill\u0026#34; password=\u0026#34;$PBKDF2WithHmacSHA512,10000,256$61646d696e$cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344\u0026#34;\u0026gt; \u0026lt;Firstname\u0026gt;Jill\u0026lt;/Firstname\u0026gt; \u0026lt;Lastname\u0026gt;Someone\u0026lt;/Lastname\u0026gt; \u0026lt;State\u0026gt;ENABLED\u0026lt;/State\u0026gt; \u0026lt;Locale\u0026gt;en-GB\u0026lt;/Locale\u0026gt; \u0026lt;Roles\u0026gt; \u0026lt;Role\u0026gt;AppUser\u0026lt;/Role\u0026gt; \u0026lt;/Roles\u0026gt; \u0026lt;Properties\u0026gt; \u0026lt;Property name=\u0026#34;realm\u0026#34; value=\u0026#34;execution\u0026#34; /\u0026gt; \u0026lt;/Properties\u0026gt; \u0026lt;/User\u0026gt; \u0026lt;/Users\u0026gt; \u0026lt;Roles\u0026gt; \u0026lt;Role name=\u0026#34;AppUser\u0026#34;\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.service.api.Service\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.model.query.StrolchQuery\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;Privilege name=\u0026#34;li.strolch.search.StrolchSearch\u0026#34; policy=\u0026#34;DefaultPrivilege\u0026#34;\u0026gt; \u0026lt;AllAllowed\u0026gt;true\u0026lt;/AllAllowed\u0026gt; \u0026lt;/Privilege\u0026gt; \u0026lt;/Role\u0026gt; \u0026lt;/Roles\u0026gt; This configuration contains one user and one role. The user jill has the role AppUser and the role AppUser has three privileges assigned.\nNote how the user\u0026rsquo;s password is configured similar to a unix password definition: Using the dollar sign \u0026amp; first the hashing algorithm is configured ( algorithm, iterations, key length), then the salt, followed by the password hash.\nNote: The password can also still be saved using two separate fields: a salt and password field. This configuration will be immediately changed to the unix form, so won\u0026rsquo;t be described further here.\n Further a user always has a firstname and last name. Optionally a locale can be set, otherwise the system locale is used. The user\u0026rsquo;s state must be defined as one of NEW, ENABLED, DISABLED, EXPIRED or SYSTEM. A user can only authenticate/login with the state ENABLED. A user can have any number of properties, which can then be used at runtime. A user can also reference any number of roles, the assigned privilege can overlap, a global configuration mode defines how duplicate privileges are handled.\nRoles have a name and any number of Privilege definitions. A Privilege has a name, which in many cases is the name of Java class/interface on which an action is being invoked. The policy value defines which policy to use when evaluating the privilege access. The privilege definition is defined in the PrivilegeConfig.xml and is the name of a class to call for privilege validation.\nFurther the privilege definitions can have a AllAllowed boolean flag, or any number of Allow or Deny values. How these values are interpreted is defined in the policy implementation. A policy takes three input parameters:\n PrivilegeContext supplied by privilege and gives access to the Certificate, thus identifying the user for which privilege access is to be validated. IPrivilege Contains the privilege values: AllAllowed, Allow and Deny Restrictable An interface from which the privilege name is retrieved, and the associated value. The value is an object, and is cast to the relevant input in the concrete privilege policy. The following privilege policies are already implemented:\n DefaultPrivilege simple policy where the passed Restrictable is expected to return a String value, which is compared with allow and deny values. Internal: RoleAccessPrivilege policy used for the internal privileges PrivilegeGetRole, PrivilegeAddRole, PrivilegeModifyRole or PrivilegeModifyRole Internal: UserAccessPrivilege policy used for the internal privileges PrivilegeGetUser, PrivilegeAddUser, PrivilegeRemoveUser, PrivilegeModifyUser, PrivilegeAddRoleToUser, PrivilegeRemoveRoleFromUser, PrivilegeSetUserState, PrivilegeSetUserLocale or PrivilegeSetUserPassword Internal: UserAccessWithSameOrganisationPrivilege Same as the UserAccessPrivilege but expects the authenticated user to have a property organisation and validates that the user being modified is in the same organisation. Internal: UsernameFromCertificatePrivilege This policy expects a Restrictable to return the certificate of another authenticated user and is used when modifying an authenticated user, i.e. killing a session, or modifying its current state, e.g. locale etc. Internal: UsernameFromCertificateWithSameOrganisationPrivilege Same as UsernameFromCertificatePrivilege but expects the authenticated user to have a property organisation and validates that the user being modified is in the same organisation. Note: As a rule, the sequence is AllAllowed Allow Deny default deny\n "},{"uri":"https://strolch.li/categories/","title":"Categories","tags":[],"description":"","content":""},{"uri":"https://strolch.li/","title":"Strolch Overview","tags":[],"description":"","content":"Strolch Overview Strolch is framework for developing Software. It\u0026rsquo;s main features are:\n Complete persisted data model: Parameters and values by time Resources, Orders with arbitrary parameter grouping Activity/Action hierarchy with arbitrary depth Policies for delegation JSON as well as XML transformation Locator API Transactions with pessimistic locking and optional read-locking Search API Component based Deeply integrated privilege handling Fully in-memory Persisted auditing, versioning, operations log DAOs for file system or PostgreSQL, easily extended Execution framework Service / Command oriented Reporting API configured by Resource objects REST API for data access WebComponents UI for Inspector Users Roles Operations Log Login Screen Jobs runs on plain old Java SE Strolch Intro It is a different framework to Spring and other similar type of Java frameworks, as the model is defined as an abstract model, where you always have the same three types of objects: Resources, Orders and Activities. The fields are mapped as Parameter objects, of which the important primitives are available.\nThe nice part about this framework is, that you can be up and ready in a matter of minutes, and start building your project immediately in that you open your favourite XML editor and start modelling your data.\nOnce your data is defined, you write your business logic in the form of Services, Commands and Searches. There are many predefined services and commands to manipulate the object model, so that you write your own services when you need to enforce special business rules.\nThrough the use of Policy objects, you decouple algorithms from your object model, so that at runtime you can change the behaviour, or easily implement different behaviour depending on your use-case. For instance you might have a simple billing service which performs a few preparatory steps, and then calls the configured billing policy to execute the billing depending on the customer, the warehouse, etc.\nAnd of course persistence is as simple as configuring the persistence handler, pointing to your RDBMS and then setting the mode to CACHED. For you as a developer there is no more thinking in terms of SQL etc., as this is completely hidden from the developer. There is even a simple file persistence layer if you are running IoT devices.\nThe runtime can be just about anything. Usually it is run inside an Apache Tomcat instance as a webapp, as a WEB UI has been required for all current Strolch projects. You could just as well use a main class. Accessing the Strolch Agent remotely is usually done through REST.\nStrolch is being actively developed, and customers constantly give us reasons to improve and extend the framework. There is a Polymer Inspector component which makes it easy to see and manipulate the actual data. The new Search API makes it really easy to query your data.\nYes, Strolch is different, but the concept has come out of the planning and execution segment, and has been refined over the years until it has become what it is today.\nAPI Check out the API page to see how to use Strolch.\nMore to motivation etc.\n"},{"uri":"https://strolch.li/tags/","title":"Tags","tags":[],"description":"","content":""}]