[Project] Initial Commit

This commit is contained in:
Robert von Burg 2021-07-11 20:36:03 +02:00
commit d2dd19801a
282 changed files with 15858 additions and 0 deletions

33
.github/workflows/publish-site.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: hugo publish
on:
push:
branches:
- develop
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
jobs:
build-deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.80.0'
- name: Build
run: hugo --minify
- name: Commit the generated website and push to git repository
run: |
git config --global user.email "dev@strolch.li"
git config --global user.name "Strolch GitHub Actions Build"
git status
git add publish/.
git commit -am "[AUTO] New version of website"
git push origin HEAD:develop

48
.gitignore vendored Normal file
View File

@ -0,0 +1,48 @@
# Build files
out
target
gen
bin
# IntelliJ files
.idea
*.iml
# Eclipse files
.settings
.classpath
.project
.externalToolBuilders
.metadata
maven-eclipse.xml
# Vim files
*.swp
# Mac files
.DS_Store
# Windows files
Thumbs.db
# Package Files #
*.jar
*.war
*.ear
*.zip
/nbactions.xml
# PlatformIO specific IDE files
.pioenvs
.piolibdeps
.pio
# Microsoft Visual Studio Code IDE files
.vscode
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
# Netbeans IDE files
/nbproject/private/
/nbproject/

1
CNAME Normal file
View File

@ -0,0 +1 @@
www.strolch.li

66
README.md Normal file
View File

@ -0,0 +1,66 @@
# Strolch website
## GoHugo
This website is created with the static site generator [Hugo](https://gohugo.io/).
### Theme
Uses the [Hugo-theme-learn](https://learn.netlify.app/en/).
Special layout components are explained on [learn.netlify.app/en/shortcodes](https://learn.netlify.app/en/shortcodes/notice/).
### Additional tools
#### Gallery image view
https://www.liwen.id.au/heg/#gallery-usage
https://github.com/liwenyip/hugo-easy-gallery/
Example use:
```
{{< gallery >}}
{{< figure link="/assets/... .png" caption="" caption-position="center" caption-effect="fade" >}}
{{< figure link="/assets/... .png" caption="" caption-position="center" caption-effect="fade" >}}
{{< figure link="/assets/... .png" caption="" caption-position="center" caption-effect="fade" >}}
{{< /gallery >}}
{{< load-photoswipe >}}
```
The last line only must be added once per page if multiple galleries are used.
### Text format
All pages are separate md-files inside the [content](content/) directory. The formatting
of the text needs to use the rules specified in [commonmark.org](https://spec.commonmark.org/0.29/).
### Test locally
To test the site locally, first install Hugo as described on ["Install Hugo"](https://gohugo.io/getting-started/installing/).
Example, for Mac:
```
brew install hugo
```
To run the site, open the terminal in the directory with the sources of this site and run the following command:
```
cd strolch.li
hugo serve
```
The website is now available on [localhost:1313](http://localhost:1313/).
### Build and run on GitHub Pages
Using a GitHub Action, the site is published on each commit into the main branch of this repository.
***The "docs" directory is auto-generated by this GitHub Action so should never be manually touched. This directory is pushed to GitHub Pages.***
This process is described here:
* [Automate your GitHub Pages Deployment using Hugo and Actions](https://medium.com/@asishrs/automate-your-github-pages-deployment-using-hugo-and-actions-518b959a51f9)
* [Deploy Hugo project to GitHub Pages with GitHub Actions](https://discourse.gohugo.io/t/deploy-hugo-project-to-github-pages-with-github-actions/20725)

110
config.toml Normal file
View File

@ -0,0 +1,110 @@
baseURL = "https://strolch.li/"
languageCode = "en-us"
title = "Strolch"
theme = "hugo-theme-learn"
publishDir = "publish"
refLinksErrorLevel = "WARNING"
[markup.goldmark.renderer]
unsafe = true
# For search functionality
[outputs]
home = ["HTML", "RSS", "JSON"]
[params]
# Prefix URL to edit current page. Will display an "Edit this page" button on top right hand corner of every page.
# Useful to give opportunity to people to create merge request for your doc.
# See the config.toml file from this documentation site to have an example.
editURL = "https://github.com/strolch-li/strolch/tree/develop/li.strolch.website"
# Author of the site, will be used in meta information
author = "Strolch"
# Description of the site, will be used in meta information
description = "Strolch is a parameterized framework for use on servers and IoT "
# Shows a checkmark for visited pages on the menu
showVisitedLinks = false
# Disable search function. It will hide search bar
disableSearch = false
# Javascript and CSS cache are automatically busted when new version of site is generated.
# Set this to true to disable this behavior (some proxies don't handle well this optimization)
disableAssetsBusting = false
# Set this to true to disable copy-to-clipboard button for inline code.
disableInlineCopyToClipBoard = false
# A title for shortcuts in menu is set by default. Set this to true to disable it.
disableShortcutsTitle = false
# If set to false, a Home button will appear below the search bar on the menu.
# It is redirecting to the landing page of the current language if specified. (Default is "/")
disableLandingPageButton = false
# When using mulitlingual website, disable the switch language button.
disableLanguageSwitchingButton = false
# Hide breadcrumbs in the header and only show the current page title
disableBreadcrumb = false
# If set to true, prevents Hugo from including the mermaid module if not needed (will reduce load times and traffic)
disableMermaid = false
# Specifies the remote location of the mermaid js
customMermaidURL = "https://unpkg.com/mermaid@8.8.0/dist/mermaid.min.js"
# Hide Next and Previous page buttons normally displayed full height beside content
disableNextPrev = false
# Order sections in menu by "weight" or "title". Default to "weight"
ordersectionsby = "weight"
# Change default color scheme with a variant one. Can be "red", "blue", "green".
themeVariant = "green"
# Provide a list of custom css files to load relative from the `static/` folder in the site root.
# custom_css = ["css/foo.css", "css/bar.css"]
# Change the title separator. Default to "::".
titleSeparator = " - "
[imaging]
# Default resample filter used for resizing. Default is Box,
# a simple and fast averaging filter appropriate for downscaling.
# See https://github.com/disintegration/imaging
resampleFilter = "box"
# Default JPEG quality setting. Default is 75.
quality = 75
# Anchor used when cropping pictures.
# Default is "smart" which does Smart Cropping, using https://github.com/muesli/smartcrop
# Smart Cropping is content aware and tries to find the best crop for each image.
# Valid values are Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight
anchor = "smart"
# Default background color.
# Hugo will preserve transparency for target formats that supports it,
# but will fall back to this color for JPEG.
# Expects a standard HEX color string with 3 or 6 digits.
# See https://www.google.com/search?q=color+picker
bgColor = "#ffffff"
[imaging.exif]
# Regexp matching the fields you want to Exclude from the (massive) set of Exif info
# available. As we cache this info to disk, this is for performance and
# disk space reasons more than anything.
# If you want it all, put ".*" in this config setting.
# Note that if neither this or ExcludeFields is set, Hugo will return a small
# default set.
includeFields = ""
# Regexp matching the Exif fields you want to exclude. This may be easier to use
# than IncludeFields above, depending on what you want.
excludeFields = ""
# Hugo extracts the "photo taken" date/time into .Date by default.
# Set this to true to turn it off.
disableDate = false
# Hugo extracts the "photo taken where" (GPS latitude and longitude) into
# .Long and .Lat. Set this to true to turn it off.
disableLatLong = false
[[menu.shortcuts]]
name = "<i class='fas fa-tags'></i> Tags"
url = "/tags"
weight = 5
[[menu.shortcuts]]
name = "<i class='fab fa-github'></i> GitHub project"
identifier = "github"
url = "https://github.com/strolch-li"
weight = 10

82
content/_index.md Normal file
View File

@ -0,0 +1,82 @@
---
title: Strolch Overview
---
## Strolch Overview
Strolch is framework for developing Software. It's main features are:
* 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.
The 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.
Once 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.
Through 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.
And 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.
The 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.
Strolch 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.
Yes, 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.
## API
Check out the API page to see how to use Strolch.
[**More to motivation etc**](/history/).

184
content/api/_index.md Normal file
View File

@ -0,0 +1,184 @@
---
title: 'API'
weight: 10
---
## 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.
Transactions 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.
## Model
The Strolch model is implemented in the project li.strolch.model.
The Strolch model consists of three root level elements: Resource,
Order and Activity. Each element has at least the following attributes:
* Id → the element's id
* Name → the element's name
* Type → the element'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:
```java
try (StrolchTransaction tx = openTx(realmName)) {
Resource resource = tx.getResourceBy("TestType", "MyTestResource");
Date date = resource.getDate("@bag01", "@param6");
logger.info("@param6 date is " + date);
}
```
XML Presentation of Strolch's top level elements of Resources:
```xml
<!-- Resource instance -->
<Resource Id="MyTestResource" Name="Test Name" Type="TestType">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
<Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
<Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
</ParameterBag>
<ParameterBag Id="@bag02" Name="Test Bag" Type="TestBag">
<Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
<Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
<Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
</ParameterBag>
<TimedState Id="@integerState" Name="Integer State" Type="IntegerState">
<Value Time="0" Value="1" />
<Value Time="1" Value="2" />
<Value Time="2" Value="3" />
</TimedState>
</Resource>
```
XML Presentation of Strolch's top level elements of Orders:
```xml
<!-- Order instance -->
<Order Id="MyTestOrder" Name="Test Name" Type="TestType" Date="2013-11-20T07:42:57.699Z" State="CREATED">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
<Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
<Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
</ParameterBag>
<ParameterBag Id="@bag02" Name="Test Bag" Type="TestBag">
<Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
<Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
<Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
</ParameterBag>
</Order>
```
XML Presentation of Strolch's top level elements of Activities:
```xml
<!-- Activity instance -->
<Activity Id="bicycleProduction" Name="Bicycle Production" Type="Series">
<Activity Id="componentProduction" Name="Production of components" Type="Series">
<Action Id="consumeGears" Name="Gears"
ResourceId="gears" ResourceType="Article" Type="Consume">
<ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
<Parameter Id="duration" Name="Duration" Type="Duration" Value="PT0S" />
</ParameterBag>
</Action>
<Activity Id="frameProduction" Name="Production frame" Type="Series">
<Action Id="produce" Name="Production frame"
ResourceId="frameProduction" ResourceType="Machine" Type="Use">
<ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
<Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" />
</ParameterBag>
</Action>
<Action Id="toStock" Name="Frame ToStock"
ResourceId="frame" ResourceType="Article" Type="Produce">
<ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
<Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" />
</ParameterBag>
</Action>
</Activity>
<Activity Id="brakeProduction" Type="Series" Name="Herstellen Bremsen" TimeOrdering="Series">
<Action Id="produce" Name="Production saddle"
ResourceId="saddleProduction" ResourceType="Machine" Type="Use">
<ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
<Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" />
</ParameterBag>
</Action>
<Action Id="toStock" Name="Saddle ToStock"
ResourceId="frame" ResourceType="Article" Type="Produce">
<ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
<Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" />
</ParameterBag>
</Action>
</Activity>
</Activity>
<Action Id="assembly" Name="Bicycle assemble"
ResourceId="bicycleAssembly" ResourceType="Assembly" Type="Use">
<ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
<Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" />
</ParameterBag>
</Action>
<Action Id="toStock" Name="Bicycle to stock"
ResourceId="bicycle" ResourceType="Product" Type="Produce">
<ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
<Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" />
</ParameterBag>
</Action>
</Activity>
```
## 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:
* 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:
```java
ComponentContainer container = getAgent().getContainer();
StrolchRealm realm = container.getRealm(StrolchConstants.DEFAULT_REALM);
try(StrolchTransaction tx = realm.openTx()) {
Resource resource = tx.getResourceBy("TestType", "MyTestResource");
...
}
```

View File

@ -0,0 +1,127 @@
---
title: 'Development'
weight: 90
---
## Prerequisites
To start developing Strolch you need an installed:
* Java JDK 11
* Apache Maven 3.x
## Building Strolch
Setting up Strolch is just a few lines:
```shell
git clone https://github.com/4treesCH/strolch.git
cd strolch
mvn clean install -DskipTests
```
{{% notice tip %}}
Note: To run the tests you will need to configure the PostgreSQL Databases. See
the README in the module.
{{% /notice %}}
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.
## Creating a Strolch App
To create your own Strolch App, you can use Maven'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.
{{% notice tip %}}
Note: you need to have installed Strolch to your local maven repo, otherwise the
archetype won't be available.
{{% /notice %}}
### 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:
```shell
mvn archetype:generate \
-DarchetypeGroupId=li.strolch \
-DarchetypeArtifactId=li.strolch.mvn.archetype.webapp \
-DarchetypeVersion=1.6.0-SNAPSHOT \
-DgroupId=<my.groupid> \
-DartifactId=<my-artifactId> \
-Dversion=<my.version> \
-DappName="<my app name>"
```
#### Install the web dependencies
The Strolch Web App uses [NodeJS v11.x](https://nodejs.org/download/release/v11.15.0/) to build the web dependencies. Please
download the relevant platform's package, unpack it, and add the `bin` directory
to your path variable.
Once NodeJS is installed, then you can prepare the web dependencies:
```shell
cd src/main/webapp/
npm install gulp -g
npm install
gulp
```
{{% notice tip %}} Note: Whenever the bower.json is changed then you should
again call npm install inside the webapp folder. {{% /notice %}}
#### Building the WAR
Building the WAR uses the `package` maven goal, but to have the optimized WAR
use the `release` profile:
```shell
mvn clean package -Prelease
```
Happy coding =))
### Creating 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:
```shell
mvn archetype:generate \
-DarchetypeGroupId=li.strolch \
-DarchetypeArtifactId=li.strolch.mvn.archetype.main \
-DarchetypeVersion=1.6.0-SNAPSHOT \
-DgroupId=<my.groupid> \
-DartifactId=<my-artifactId> \
-Dversion=<my.version> \
-DappName="<my app name>"
```
You change into the directory of the new project and then build the project by calling:
```shell
cd <my-artifactId>
mvn clean package
```
Start the program using:
```shell
mvn exec:java
```
Happy coding =))
## Tools used
The following tools are used to develop Strolch and Strolch-based projects:
* [IntelliJ](https://www.jetbrains.com/idea/download/#section=linux "{target='_blank'}")
* [Apache Maven](https://maven.apache.org/ "{target='_blank'}")
* [Git SCM](http://git-scm.com/ "{target='_blank'}")

View File

@ -0,0 +1,14 @@
---
title: 'Documentation'
weight: 20
---
## Overview
Strolch'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.
Currently we have the following topics of discussion:
{{% children %}}

View File

@ -0,0 +1,56 @@
---
title: 'Architecture'
weight: 10
---
## Architecture
### Birds View
A Strolch agent'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.
The agent itself implements the business logic using Services, Commands,
Queries etc.
The agent can communicate with other 3rd systems using any API, where it is
preferred to use JSON over REST.
The 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.
The following diagram helps visualize this:
![Strolch Architecture Birds View](/assets/images/Strolch-Bird-View.svg)
### Squirrel View
The following diagram shows a more detailed view of the architecture of a
Strolch Agent:
![Strolch Architecture Squirrel View](/assets/images/Strolch-Squirrel-View.svg)
A Strolch agent consists of the following main parts:
* 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:
* 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.

View File

@ -0,0 +1,113 @@
---
title: 'Components'
weight: 60
---
## 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.
The following represents a list of the most used components:
* `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's own life-cycle.
The life-cycle is as follows:
```
setup -> initialize -> start <-> stop -> 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.
Each component has its own configuration parameters. A component is
registered in the `StrolchConfiguration.xml` file with a
* 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.
By example of the `MailHandler` we shall show how a strolch component would
be implemented.
First define an interface:
```java
public interface MailHandler {
void sendMail(String subject, String text, String recipient);
}
```
Then implement a concrete `MailHandler`:
```java
public 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:
```xml
<StrolchConfiguration>
<env id="dev">
...
<Component>
<name>MailHandler</name>
<api>li.strolch.handler.mail.MailHandler</api>
<impl>li.strolch.handler.mail.SmtpMailHandler</impl>
<Properties>
<username>test</username>
<password>test</password>
<hostName>localhost</hostName>
...
</Properties>
</Component>
...
</env>
</StrolchConfiguration>
```
Now when the agent is started, the component can be retrieved and used.
E.g from inside a Service:
```java
MailHandler mailHandler = getComponent(MailHandler.class);
mailHandler.sendMail("My Subject", "Hello World", "test@test.ch");
```

View File

@ -0,0 +1,18 @@
---
title: 'Do and Don''t'
weight: 30
---
## This page discusses things you should and shouldn't do when using Strolch
The following is a simple list of do's and don'ts:
* 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't want to use a search.
* Don'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.

View File

@ -0,0 +1,305 @@
---
title: 'Model'
weight: 20
---
## Model
Before we dive into the entire model, let's show an example and how it would
be modelled in Strolch and use in Strolch:
![Strolch model example](/assets/images/strolch-model-example.png)
A possible model would look as follows:
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<StrolchModel xmlns="https://strolch.li/xsd/StrolchModel-1.6.xsd">
<Resource Id="Product" Name="Product Template" Type="Template">
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
<Parameter Id="description" Name="Description" Type="String" Value=""/>
<Parameter Id="color" Name="Color" Type="String" Value=""/>
<Parameter Id="form" Name="Form" Type="String" Value=""/>
</ParameterBag>
<ParameterBag Id="relations" Name="Relations" Type="Relations">
<Parameter Id="articles" Name="Articles" Type="StringList" Interpretation="Resource-Ref" Uom="Article" Value=""/>
</ParameterBag>
</Resource>
<Resource Id="Article" Name="Article Template" Type="Template">
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
<Parameter Id="description" Name="Description" Type="String" Value=""/>
<Parameter Id="barcode" Name="Barcode" Type="String" Value=""/>
</ParameterBag>
<ParameterBag Id="relations" Name="Relations" Type="Relations">
<Parameter Id="product" Name="Product" Type="String" Interpretation="Resource-Ref" Uom="Product" Value=""/>
</ParameterBag>
</Resource>
<Resource Id="Customer" Name="Customer Template" Type="Template">
<ParameterBag Id="address" Name="Address" Type="Address">
<Parameter Id="street" Name="Street" Type="String" Value=""/>
<Parameter Id="zip" Name="Zip" Type="String" Value=""/>
<Parameter Id="city" Name="City" Type="String" Value=""/>
<Parameter Id="country" Name="Country" Type="String" Value=""/>
</ParameterBag>
</Resource>
<Order Id="Order" Name="Order" Type="Template">
<ParameterBag Id="quantities" Name="Quantities per Article Id" Type="Quantities">
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="0"/>
</ParameterBag>
<ParameterBag Id="relations" Name="Relations" Type="Relations">
<Parameter Id="articles" Name="Articles" Type="StringList" Interpretation="Resource-Ref" Uom="Article" Value=""/>
<Parameter Id="customer" Name="Customer" Type="String" Interpretation="Resource-Ref" Uom="Customer" Value=""/>
</ParameterBag>
</Order>
</StrolchModel>
```
Let's go through this model:
* In the above model we see that the id and name fields are always on the
element, and thus aren'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="Resource-Ref" Uom="Product" 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'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:
```java
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, false)) {
/*
* create a new product
*/
Resource dafalgan = tx.getResourceTemplate("Product", true);
dafalgan.setName("Dafalgan 100mg");
dafalgan.getParameter("description", true).setValue("Dafalgan is for pain.");
dafalgan.getParameter("color", true).setValue("Yellow");
dafalgan.getParameter("form", true).setValue("flat");
StringListParameter articlesP = dafalgan.getRelationsParam("articles", true);
/*
* create articles
*/
Resource dafalgan1 = tx.getResourceTemplate("Article", true);
dafalgan1.setName("Dafalgan 100mg 10 pce");
dafalgan1.getParameter("description", true).setValue("This is pack with 10 pieces.");
dafalgan1.getParameter("barcode", true).setValue("654654");
Resource dafalgan2 = tx.getResourceTemplate("Article", true);
dafalgan2.setName("Dafalgan 100mg 20 pce");
dafalgan2.getParameter("description", true).setValue("This is pack with 20 pieces.");
dafalgan2.getParameter("barcode", true).setValue("654655");
/*
* add reference to product
*/
dafalgan1.getRelationParam("product").setValue(dafalgan.getId());
articlesP.addValue(dafalgan1.getId());
dafalgan2.getRelationParam("product").setValue(dafalgan.getId());
articlesP.addValue(dafalgan2.getId());
/*
* create a new customer
*/
Resource customer1 = tx.getResourceTemplate("Customer", true);
customer1.setName("John Doe");
// set address
ParameterBag addressBag = customer1.getParameterBag("address", true);
addressBag.getParameter("street", true).setValue("Main Str. 1");
addressBag.getParameter("zip", true).setValue("1234");
addressBag.getParameter("city", true).setValue("Hometown");
addressBag.getParameter("country", true).setValue("Switzerland");
/*
* create a new order
*/
Order order = tx.getOrderTemplate("Order", true);
order.setName("Order for " + customer1.getName());
order.setDate(LocalDate.of(2021, 2, 1));
order.setState(State.PLANNED);
// store reference to customer
order.getRelationParam("customer", true).setValue(customer1.getId());
StringListParameter orderArticlesP = order.getRelationsParam("articles", true);
ParameterBag quantitiesBag = order.getParameterBag("quantities", true);
FloatParameter quantityT = quantitiesBag.removeParameter("quantity");
// 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();
}
```
```java
try (StrolchTransaction tx = runtimeMock.openUserTx(certificate, true)) {
// get order
Order order = tx.getOrderBy("Order", orderId, true);
assertNotNull(orderId);
assertEquals("Order for John Doe", order.getName());
// get customer
Resource customer = tx.getResourceByRelation(order, "customer", true);
assertNotNull(customer);
assertEquals("John Doe", customer.getName());
// get articles
List<Resource> articles = tx.getResourcesByRelation(order, "articles", true);
assertEquals(2, articles.size());
// get products
List<Resource> products = articles.stream().map(a -> tx.getResourceByRelation(a, "product", true))
.distinct().collect(Collectors.toList());
assertEquals(1, products.size());
// search for all orders in state PLANNED and with customer
List<Order> orders = new OrderSearch().types("Order").stateIsIn(State.PLANNED)
.where(ExpressionsSupport.relationParam("customer").isEqualTo(customerId)).search(tx).toList();
assertEquals(1, orders.size());
}
```
{{% notice tip %}}
Note: Checkout [example-model.xml](https://github.com/4treesCH/strolch/blob/develop/li.strolch.service/src/test/resources/transienttest/data/example-model.xml "{target='_blank'}") and [SimpleModelTest.java](https://github.com/4treesCH/strolch/blob/develop/li.strolch.service/src/test/java/li/strolch/service/SimpleModelTest.java "{target='_blank'}") for these examples.
{{% /notice %}}
{{% notice tip %}}
There is an XML Schema which defines the model in XML: [StrolchModel-1.6.xsd](/assets/xsd/StrolchModel-1.6.xsd "{target='_blank'}")
{{% /notice %}}
Here is an example of all the possible elements in Strolch:
```xml
<StrolchModel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://strolch.li/xsd/StrolchModel-1.6.xsd"
xsi:schemaLocation="https://strolch.li/xsd/StrolchModel-1.6.xsd StrolchModel-1.6.xsd">
<IncludeFile file="Include1.xml"/>
<Order Id="@test1" Name="Test Order" Type="Order">
<Version Version="0" CreatedBy="test" CreatedAt="2012-11-30T18:12:05.628+01:00" Deleted="false"/>
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
</ParameterBag>
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
</ParameterBag>
<Policies>
<Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
<Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
</Policies>
</Order>
<Resource Id="MyTestResource" Name="Test Name" Type="TestType">
<Version Version="0" CreatedBy="test" CreatedAt="2012-11-30T18:12:05.628+01:00" Deleted="false"/>
<ParameterBag Id="@bag01" Name="Test Bag 01" Type="TestBag">
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
</ParameterBag>
<ParameterBag Id="@bag02" Name="Test Bag 02" Type="TestBag">
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
</ParameterBag>
<TimedState Id="@booleanState" Name="Boolean State" Type="Boolean">
<Value Time="1970-01-01T00:02:00.000+01:00" Value="false"/>
</TimedState>
<Policies>
<Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
<Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
</Policies>
</Resource>
<Activity Id="activity_1" Name="Activity" Type="parentType" TimeOrdering="Series">
<Version Version="0" CreatedBy="test" CreatedAt="2012-11-30T18:12:05.628+01:00" Deleted="false"/>
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
</ParameterBag>
<Policies>
<Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
<Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
</Policies>
<Action Id="action_1" Name="Action 1" ResourceId="dummyId" ResourceType="dummyType" State="Created" Type="Use">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
</ParameterBag>
<Policies>
<Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
<Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
</Policies>
<ValueChange StateId="dummyId" Time="2012-11-30T18:12:05.628+01:00" Value="5" Type="Integer"/>
<ValueChange StateId="dummyId" Time="2012-11-30T18:12:06.628+01:00" Value="6" Type="Integer"/>
</Action>
<Activity Id="child_activity" Name="Child Activity" Type="childType" TimeOrdering="Series">
<ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
<Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true"/>
</ParameterBag>
<Policies>
<Policy Type="PlanningPolicy" Value="key:SimplePlanning"/>
<Policy Type="ConfirmationPolicy" Value="key:NoConfirmation"/>
</Policies>
<Action Id="action_2" Name="Action 2" ResourceId="dummyId" ResourceType="dummyType" State="Planned"
Type="Use">
<ValueChange StateId="dummyId" Time="2012-11-30T18:12:05.628+01:00" Value="5" Type="Integer"/>
<ValueChange StateId="dummyId" Time="2012-11-30T18:12:06.628+01:00" Value="6" Type="Integer"/>
</Action>
<Action Id="action_3" Name="Action 3" ResourceId="dummyId" ResourceType="dummyType" State="Created"
Type="Use"/>
</Activity>
</Activity>
</StrolchModel>
```

View File

@ -0,0 +1,62 @@
---
title: 'Observers'
weight: 110
---
## Observers
All changes done in a Strolch transaction are recorded and then propagated to
any registered observers.
The 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:
```xml
<StrolchConfiguration>
<env id="dev">
...
<Component>
<name>RealmHandler</name>
<api>li.strolch.agent.api.RealmHandler</api>
<impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
<depends>PrivilegeHandler</depends>
<Properties>
<realms>defaultRealm, otherRealm</realms>
<enableObserverUpdates>true</enableObserverUpdates>
<dataStoreMode>TRANSIENT</dataStoreMode>
<dataStoreFile>StrolchModel.xml</dataStoreFile>
<enableObserverUpdates.otherRealm>true</enableObserverUpdates.otherRealm>
<dataStoreMode.otherRealm>TRANSIENT</dataStoreMode.otherRealm>
<dataStoreFile.otherRealm>StrolchModel.xml</dataStoreFile.otherRealm>
</Properties>
</Component>
</env>
...
</StrolchConfiguration>
```
Registering for updates is done by registering an Observer on the
ObserverHandler of the realm itself:
```java
ObserverHandler observerHandler = container.getRealm(StrolchConstants.DEFAULT_REALM).getObserverHandler();
observerHandler.registerObserver(Tags.RESOURCE, new Observer() {
@Override
public void update(String key, List<StrolchRootElement> elements) {
logger.info(elements.size() + " resources were updated!");
}
@Override
public void remove(String key, List<StrolchRootElement> elements) {
logger.info(elements.size() + " resources were removed!");
}
@Override
public void add(String key, List<StrolchRootElement> elements) {
logger.info(elements.size() + " resources were added!");
}
});
```

View File

@ -0,0 +1,103 @@
---
title: 'Policies'
weight: 100
---
## 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.
Since 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.
Currently 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.
Using 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.
Policies 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.
Policies are registered on Resources, Orders, Activities and Actions. The
following shows defining two policies on a Resource, a PlanningPolicy, an
ExecutionPolicy in XML:
```xml
<Resource ...>
...
<Policies>
<Policy Type="PlanningPolicy" Value="key:SimplePlanning" />
<Policy Type="ExecutionPolicy" Value="java:li.strolch.policytest.TestSimulatedExecutionPolicy" />
</Policies>
</Resource>
```
{{% notice tip %}} Note how the PlanningPolicy has a value of key:SimplePlanning
and the ExecutionPolicy defines a reference to an actual class. {{% /notice %}}
To use the PolicyHandler, it must be configured in the StrolchConfiguration.xml
as follows:
```xml
<Component>
<name>PolicyHandler</name>
<api>li.strolch.policy.PolicyHandler</api>
<impl>li.strolch.policy.DefaultPolicyHandler</impl>
<Properties>
<readPolicyFile>true</readPolicyFile>
<policyConfigFile>StrolchPolicies.xml</policyConfigFile>
</Properties>
</Component>
```
And this policy handler implementation requires a file where the lookups for the
policies is defined, e.g.:
```xml
<StrolchPolicies>
<PolicyType Type="PlanningPolicy"
Api="li.strolch.policytest.TestPlanningPolicy">
<Policy Key="SimplePlanning"
Class="li.strolch.policytest.TestSimplePlanningPolicy"/>
</PolicyType>
<PolicyType Type="ConfirmationPolicy"
Api="li.strolch.policytest.TestConfirmationPolicy">
<Policy Key="NoConfirmation"
Class="li.strolch.policytest.TestNoConfirmationPolicy"/>
</PolicyType>
</StrolchPolicies>
```
Now at runtime we can access the policies:
```java
PolicyHandler policyHandler=getComponent(PolicyHandler.class);
try(StrolchTransaction tx=openTx()){
Resource res=tx.getResourceBy("TestType","MyTestResource");
PolicyDefs policyDefs=res.getPolicyDefs();
PolicyDef planningPolicyDef=policyDefs.getPolicyDef("PlanningPolicy");
PlanningPolicy planningPolicy=policyHandler.getPolicy(planningPolicyDef,tx);
planningPolicy.plan(...);
PolicyDef executionPolicyDef=res.getPolicyDefs().getPolicyDef("ExecutionPolicy");
ExecutionPolicy executionPolicy=policyHandler.getPolicy(executionPolicyDef,tx);
executionPolicy.execute(...);
}
```

View File

@ -0,0 +1,119 @@
---
title: 'Privileges'
weight: 140
---
## 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't deny and another
role allows a specific action.
As explained in
the [Privilege Configuration](/documentation/runtime-configuration.md) section,
users are defined in the `PrivilegeUsers.xml` file, and roles are defined in the
`PrivilegeRoles.xml` file.
Let's assume the following user and role definition:
```xml
<Users>
<User userId="1" username="jill" password="$PBKDF2WithHmacSHA512,10000,256$61646d696e$cb69962946617da006a2f95776d78b49e5ec7941d2bdb2d25cdb05f957f64344">
<Firstname>Jill</Firstname>
<Lastname>Someone</Lastname>
<State>ENABLED</State>
<Locale>en-GB</Locale>
<Roles>
<Role>AppUser</Role>
</Roles>
<Properties>
<Property name="realm" value="execution" />
</Properties>
</User>
</Users>
```
```xml
<Roles>
<Role name="AppUser">
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="li.strolch.model.query.StrolchQuery" policy="DefaultPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
<AllAllowed>true</AllAllowed>
</Privilege>
</Role>
</Roles>
```
This configuration contains one user and one role. The user `jill` has the role
`AppUser` and the role `AppUser` has three privileges assigned.
Note how the user's password is configured similar to a unix password
definition: Using the dollar sign & first the hashing algorithm is configured (
algorithm, iterations, key length), then the salt, followed by the password
hash.
{{% notice tip %}}
Note: 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't be described further here.
{{% /notice %}}
Further a user always has a firstname and last name. Optionally a locale can be
set, otherwise the system locale is used. The user'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.
Roles 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.
Further 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:
* `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:
* `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.
{{% notice tip %}}
Note: As a rule, the sequence is `AllAllowed → Allow → Deny → default deny`
{{% /notice %}}

View File

@ -0,0 +1,115 @@
---
title: 'Queries'
weight: 89
---
## Queries
{{% notice warning %}}
The Query API is deprecated and the search API should be used instead.
{{% /notice %}}
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`.
A 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 one of the inputs for every query is it'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.
Further input for a StrolchQuery are the selections. These selections get
translated into RDBMS `WHERE` clauses. Selections support boolean operations thus
allowing for complex querying.
StrolchQueries also support Ordering and object transformation. Following
classes provide the most used scenarios:
* OrderById
* OrderByName
* OrderByParameter
* *ToDomVisitor
* *ToSaxVisitor
* *ToJsonVisitor
* *ToFlatJsonVisitor
Example: Query all resources of type Car:
```java
try (StrolchTransaction tx = openTx()) {
ResourceQuery<Resource> query = ResourceQuery.query("Car");
query.withAny();
List<Resource> cars = tx.doQuery(query);
}
```
Example: Query all resources of type Car, order by Name and transform to JSON:
```java
try (StrolchTransaction tx = openTx()) {
ResourceQuery<JsonObject> query = ResourceQuery.query("Car", new ResourceToJsonVisitor(),
new OrderByName());
query.withAny();
List<JsonObject> cars = tx.doQuery(query);
}
```
the previous example can also be written as follows:
```java
try (StrolchTransaction tx = openTx()) {
ResourceQuery<JsonObject> query = new ResourceQuery<>();
query.setNavigation(new StrolchTypeNavigation("Car"));
query.setResourceVisitor(new ResourceToJsonVisitor());
query.withAny();
List<JsonObject> cars = tx.doQuery(query);
}
```
Example: Query all resources of type Car with color blue:
```java
try (StrolchTransaction tx = openTx()) {
ResourceQuery<Resource> query = ResourceQuery.query("Car");
query.with(ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()));
List<Resource> cars = tx.doQuery(query);
}
```
Example: Query all resources of type Car which are not blue:
```java
try (StrolchTransaction tx = openTx()) {
ResourceQuery<Resource> query = ResourceQuery.query("Car");
query.not(ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()));
List<Resource> cars = tx.doQuery(query);
}
```
Example: Query all resources of type Car with color blue or yellow:
```java
try (StrolchTransaction tx = openTx()) {
ResourceQuery<Resource> query = ResourceQuery.query("Car");
query.or().with(
ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()),
ParameterSelection.stringSelection("parameters", "color", "yellow", StringMatchMode.es()));
List<Resource> cars = tx.doQuery(query);
}
```
Example: Query all resources of type Car with color blue or yellow owned by Jill:
```java
try (StrolchTransaction tx = openTx()) {
ResourceQuery<Resource> query = ResourceQuery.query("Car");
StringParameterSelection owner = ParameterSelection.stringSelection("parameters", "owner", "Jill", StringMatchMode.es());
OrSelection colors = new OrSelection().with(
ParameterSelection.stringSelection("parameters", "color", "blue", StringMatchMode.es()),
ParameterSelection.stringSelection("parameters", "color", "yellow", StringMatchMode.es()));
query.and().with(owner, colors);
List<Resource> cars = tx.doQuery(query);
}
```

View File

@ -0,0 +1,118 @@
---
title: 'Realms'
weight: 50
---
## 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.
A realm can run in one of the following modes:
* 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.
If the mode is `CACHED`, then the `PersistenceHandler` component is required to be
configured, so that the DAOs know how to access the underlying database.
The configuration in the `StrolchConfiguration.xml` file is as follows: