strolch/li.strolch.website/www.strolch.li/api.html

511 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="google-site-verification" content="CPhbjooaiTdROm7Vs4E7kuHZvBfkeLUtonGgcVUbTL8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<link rel="shortcut icon" href="ico/favicon.ico">
<title>Strolch: API</title>
<!-- Bootstrap core CSS -->
<link href="css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/custom.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --><!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script><![endif]-->
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="index.html">Strolch</a>
</div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li><a href="index.html">Overview</a></li>
<li class="active"><a href="api.html">API</a></li>
<li><a href="documentation.html">Documentation</a></li>
<li><a href="tutorial.html">Tutorial</a></li>
<li><a href="downloads.html">Downloads</a></li>
<li><a href="development.html">Development</a></li>
<li><a href="blog.html">Blog</a></li>
</ul>
</div>
<!--/.nav-collapse -->
</div>
</div>
<div class="container">
<div class="page-header">
<h1 class="page-title">Strolch API</h1>
<p class="lead page-description">This page describes the Strolch API.</p>
</div>
<div class="content">
<h2>Overview</h2>
<p>The Strolch API revolves around the <i>StrolchTransaction</i> object. The main concept is to implement your
use cases in <i>Service</i> implementations. You open a transaction using the <i>openTx(String)</i>-method
and then perform the use case by adding your <i>Command</i> instances to the transaction.</p>
<p>Transactions are opened on a <i>StrolchRealm</i>. The realms are used to separate mandates in a single
runtime instance of Strolch. Each realm has its own <i>ResourceMap</i>, <i>OrderMap</i>, <i>ActivityMap</i>
instances from which the TX retrieves the elements.</p>
<h2>Model</h2>
<p>The Strolch model is implemented in the project li.strolch.model.</p>
<p>The Strolch model consists of three root level elements: <i>Resource</i>, <i>Order</i> and <i>Activity</i>.
Each element has at least the following attributes:</p>
<ul>
<li>Id &rarr; the element's id</li>
<li>Name &rarr; the element's name</li>
<li>Type &rarr; the element's type</li>
</ul>
<p>Each root element can have any number of <i>ParameterBag</i> instances on it, which in turn can have any
number of <i>Parameters</i> on it. Accessing these objects is always done by their IDs. Strolch root elements
are always stored in the respective <i>ElementMaps</i> in their Strolch realm. Thus accessing a certain
parameter from a <i>Resource</i> would look like this:</p>
<pre class="pre-scrollable">
try (StrolchTransaction tx = openTx(realmName)) {
Resource resource = tx.getResourceBy("TestType", "MyTestResource");
DateParameter dateP = resource.getParameter("@bag01", "@param6");
Date date = dateP.getValue();
logger.info("@param6 date is " + date);
}</pre>
XML Presentation of Strolch's top level elements of <i>Resources</i>:
<pre class="pre-scrollable">
&lt;!-- Resource instance --&gt;
&lt;Resource Id="MyTestResource" Name="Test Name" Type="TestType"&gt;
&lt;ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag"&gt;
&lt;Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" /&gt;
&lt;Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" /&gt;
&lt;Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" /&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="@bag02" Name="Test Bag" Type="TestBag"&gt;
&lt;Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" /&gt;
&lt;Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" /&gt;
&lt;Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" /&gt;
&lt;Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" /&gt;
&lt;/ParameterBag&gt;
&lt;TimedState Id="@integerState" Name="Integer State" Type="IntegerState"&gt;
&lt;Value Time="0" Value="1" /&gt;
&lt;Value Time="1" Value="2" /&gt;
&lt;Value Time="2" Value="3" /&gt;
&lt;TimedState>
&lt;/Resource&gt;</pre>
XML Presentation of Strolch's top level elements of <i>Orders</i>:
<pre class="pre-scrollable">
&lt;!-- Order instance --&gt;
&lt;Order Id="MyTestOrder" Name="Test Name" Type="TestType" Date="2013-11-20T07:42:57.699Z" State="CREATED"&gt;
&lt;ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag"&gt;
&lt;Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" /&gt;
&lt;Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" /&gt;
&lt;Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" /&gt;
&lt;/ParameterBag&gt;
&lt;ParameterBag Id="@bag02" Name="Test Bag" Type="TestBag"&gt;
&lt;Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" /&gt;
&lt;Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" /&gt;
&lt;Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" /&gt;
&lt;Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" /&gt;
&lt;/ParameterBag&gt;
&lt;/Order&gt;</pre>
XML Presentation of Strolch's top level elements of <i>Activities</i>:
<pre class="pre-scrollable">
&lt;!-- Activity instance --&gt;
&lt;Activity Id="bicycleProduction" Name="Bicycle Production" Type="Series"&gt;
&lt;Activity Id="componentProduction" Name="Production of components" Type="Series"&gt;
&lt;Action Id="consumeGears" Name="Gears"
ResourceId="gears" ResourceType="Article" Type="Consume"&gt;
&lt;ParameterBag Id="objectives" Name="Production goals" Type="Objectives"&gt;
&lt;Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" /&gt;
&lt;Parameter Id="duration" Name="Duration" Type="Duration" Value="PT0S" /&gt;
&lt;/ParameterBag&gt;
&lt;/Action&gt;
&lt;Activity Id="frameProduction" Name="Production frame" Type="Series"&gt;
&lt;Action Id="produce" Name="Production frame"
ResourceId="frameProduction" ResourceType="Machine" Type="Use"&gt;
&lt;ParameterBag Id="objectives" Name="Production goals" Type="Objectives"&gt;
&lt;Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" /&gt;
&lt;Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" /&gt;
&lt;/ParameterBag&gt;
&lt;/Action&gt;
&lt;Action Id="toStock" Name="Frame ToStock"
ResourceId="frame" ResourceType="Article" Type="Produce"&gt;
&lt;ParameterBag Id="objectives" Name="Production goals" Type="Objectives"&gt;
&lt;Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" /&gt;
&lt;Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" /&gt;
&lt;/ParameterBag&gt;
&lt;/Action&gt;
&lt;/Activity&gt;
&lt;Activity Id="brakeProduction" Type="Series" Name="Herstellen Bremsen" TimeOrdering="Series"&gt;
&lt;Action Id="produce" Name="Production saddle"
ResourceId="saddleProduction" ResourceType="Machine" Type="Use"&gt;
&lt;ParameterBag Id="objectives" Name="Production goals" Type="Objectives"&gt;
&lt;Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" /&gt;
&lt;Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" /&gt;
&lt;/ParameterBag&gt;
&lt;/Action&gt;
&lt;Action Id="toStock" Name="Saddle ToStock"
ResourceId="frame" ResourceType="Article" Type="Produce"&gt;
&lt;ParameterBag Id="objectives" Name="Production goals" Type="Objectives"&gt;
&lt;Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" /&gt;
&lt;Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" /&gt;
&lt;/ParameterBag&gt;
&lt;/Action&gt;
&lt;/Activity&gt;
&lt;/Activity&gt;
&lt;Action Id="assembly" Name="Bicycle assemble"
ResourceId="bicycleAssembly" ResourceType="Assembly" Type="Use"&gt;
&lt;ParameterBag Id="objectives" Name="Production goals" Type="Objectives"&gt;
&lt;Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" /&gt;
&lt;Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" /&gt;
&lt;/ParameterBag&gt;
&lt;/Action&gt;
&lt;Action Id="toStock" Name="Bicycle to stock"
ResourceId="bicycle" ResourceType="Product" Type="Produce"&gt;
&lt;ParameterBag Id="objectives" Name="Production goals" Type="Objectives"&gt;
&lt;Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" /&gt;
&lt;Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" /&gt;
&lt;/ParameterBag&gt;
&lt;/Action&gt;
&lt;/Activity&gt;</pre>
<h2>Realms</h2>
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:
<ul>
<li>EMPTY
<p>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.</p></li>
<li>TRANSIENT
<p>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.</p>
</li>
<li>CACHED
<p>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.</p></li>
</ul>
<p>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:</p>
<pre class="pre-scrollable">
ComponentContainer container = getAgent().getContainer();
StrolchRealm realm = container.getRealm(StrolchConstants.DEFAULT_REALM);
try(StrolchTransaction tx = realm.openTx()) {
Resource resource = tx.getResourceBy("TestType", "MyTestResource");
...
}</pre>
In a Service implementation there is a convenience method, so that this is as simple as calling <i>openTx(String)</i>.
<h2>Services and Commands</h2>
<p>In the motivation section, it was discusses that writing business logic is what developing is about and a
reason why Strolch is a different approach to the Java EE ecosystem. So this is where Services and Commands
come into play, and tries to make writing business logic a first class citizen.</p>
<p>Services are to be used once for each use case. Services are not re-used or called by other services.
Services open transactions are implement the calling of the re-usable commands. Thus when writing projects
using Strolch, the first thing to do after configuring the runtime environment for your situation, Services
will be implemented.</p>
<p>Commands on the other hand are re-usable and should be implemented in such a way, that they encapsulate the
use case's different actions. Commands are then passed to a transaction for execution and, when the
transaction is committed, will be executed. Commands also implement undoing its operation in the case of
exceptions. Strolch transactions handle the life-cycle of a command. A further function of Commands is to
lock the relevant Strolch elements before execution.</p>
<p>A typical Service and Command implementation would look as follows:</p>
<pre class="pre-scrollable">
public class SetParameterService extends AbstractService&lt;SetParameterArg, ServiceResult&gt; {
public static final long serialVersionUID = 1L;
@Override
protected ServiceResult internalDoService(SetParameterArg arg) {
// open a new transaction
try (StrolchTransaction tx = openTx(arg.realm)) {
// find parameter to modify
Parameter&lt;?&gt; parameter = tx.findElement(arg.locator);
// instantiate the command
SetParameterCommand command = new SetParameterCommand(getContainer(), tx);
// set the arguments
command.setParameter(parameter);
command.setName(arg.name);
command.setInterpretation(arg.interpretation);
command.setUom(arg.uom);
command.setHidden(arg.hidden);
command.setIndex(arg.index);
command.setValueAsString(arg.valueAsString);
// add the command to the transaction
tx.addCommand(command);
// only now do we say we want to commit so that a rollback works nicely
tx.commitOnClose();
}
// return the execution result of the service
return ServiceResult.success();
}
/**
* The argument class for this service
*/
public static class SetParameterArg extends ServiceArgument {
public static final long serialVersionUID = 1L;
public Locator locator;
public String name;
public String interpretation;
public String uom;
public Boolean hidden;
public Integer index;
public String valueAsString;
}
@Override
protected ServiceResult getResultInstance() {
return new ServiceResult();
}
}
</pre>
<pre class="pre-scrollable">
public class SetParameterCommand extends Command {
// input fields
private Parameter&lt;?&gt; parameter;
private String valueAsString;
// undo fields
private String oldValueAsString;
private StrolchRootElement replacedElement;
/**
* @param container
* @param tx
*/
public SetParameterCommand(ComponentContainer container, StrolchTransaction tx) {
super(container, tx);
}
// setters for input ...
// getters for output ...
@Override
public void validate() {
DBC.PRE.assertNotNull("Parameter may not be null!", this.parameter);
}
@Override
public void doCommand() {
// lock the element to be modified
StrolchRootElement rootElement = this.parameter.getRootElement();
tx().lock(rootElement);
// perform changes
if (this.valueAsString != null) {
this.oldValueAsString = this.parameter.getValueAsString();
SetParameterValueVisitor visitor = new SetParameterValueVisitor();
visitor.setValue(this.parameter, this.valueAsString);
}
// update root element
if (hasChanges()) {
replacedElement = new UpdateElementVisitor(tx()).update(rootElement);
}
}
private boolean hasChanges() {
return this.oldValueAsString != null || this.oldName != null || this.oldInterpretation != null
|| this.oldUom != null || this.oldHidden != null || this.oldIndex != null;
}
@Override
public void undo() {
// undo changes
if (this.oldValueAsString != null) {
SetParameterValueVisitor visitor = new SetParameterValueVisitor();
visitor.setValue(this.parameter, this.oldValueAsString);
}
// update root element
if (hasChanges()
&amp;&amp; this.replacedElement != null
&amp;&amp; this.replacedElement != this.parameter.getRootElement()) {
new UpdateElementVisitor(tx()).update(replacedElement);
}
}
}
}</pre>
<h2>Code</h2>
<p>The Strolch code can be retrieved from GitHub, where the code is hosted. Each commit triggers a continuous
integration build, so that we are sure no tests are broken. The CI is viewable at
<a href="https://ci.4trees.ch" target="_blank">4trees CI Server</a>.</p>
<p>Strolch is divided up into different projects on GitHub so that these projects can be developed, or bugfixed
independently and not all parts are required in every context.</p>
<p><a href="https://github.com/4treesCH/strolch" target="_blank">Strolch on GitHub</a></p>
<h3>Main Strolch components</h3>
<ul>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.model">li.strolch.model</a>
<p>This project implements the Strolch model. This is where you will find the different elements that
can store data at runtime e.g. <i>Resources</i>, <i>Orders</i> and <i>Activities</i></p></li>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.agent">li.strolch.agent</a>
<p>The agent is the Strolch runtime and is the component which implements the core Agent functionality.
That is:</p>
<ul>
<li>Provide the Agent instance which loads the configuration and is the entry point to the runtime
</li>
<li>Provide the ComponentContainer instance from which the registered components can be accessed
</li>
<li>Configure and maintain the realms, which implement the multi-client capability</li>
<li>Provide a default ServiceHandler to perform Services at runtime</li>
<li>Implements the realms which each can operate in different modes data store modes: CACHED,
TRANSIENT
</li>
</ul>
</li>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.service">li.strolch.service</a>
<p>Implements the basic Services and the re-usable Commands:</p>
<ul>
<li>CRUD Services and Commands to modify the model</li>
<li>Commands to import and export the model to XML</li>
<li>Further services and commands...</li>
</ul>
</li>
</ul>
<h3>Additional components</h3>
<ul>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.persistence.postgresql">li.strolch.persistence.postgresql</a>
<p>Implements a PostgreSQL persistence layer so that the Strolch model can be persisted to a PostgreSQL
RDBMS when the realm is configured to have a data store mode of CACHED.</p>
</li>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.persistence.xml">li.strolch.persistence.xml</a>
<p>Implements an XML persistence layer so that the Strolch model can be persisted to XML files when the
realm is configured to have a data store mode of CACHED.</p>
</li>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.rest">li.strolch.rest</a>
<p>Implements a Restful API to communicate with the Strolch runtime from clients and external
systems.</p>
</li>
</ul>
<h3>Meta projects</h3>
<ul>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.bom">li.strolch.bom</a>
<p>This bill of material is a Maven project which, when imported in one's own Strolch project, pulls in
all required dependencies needed to set up a minimal working Strolch environment.</p>
</li>
<li><a href="https://github.com/4treesCH/strolch/tree/master/li.strolch.testbase">li.strolch.testbase</a>
<p>Implements a test base so that writing tests for Strolch is easy. It provides a RuntimeMock, which
handles setting up and tearing down Strolch runtimes during tests.</p>
</li>
</ul>
<h3>Example projects</h3>
<ul>
<li><a href="https://github.com/4treesCH/strolch/tree/develop/strolch_minimal_rest" target="_blank">strolch_minimal_rest</a>
<p>A minimal project to get started using REST with Strolch.</p>
</li>
<li><a href="https://github.com/4treesCH/strolch-bookshop" target="_blank">Strolch Bookshop</a>
<p>An example implentation of services etc. where we show how to use Strolch by implementing a simple
book shop.</p>
</li>
</ul>
<h2>Development</h2>
<p>To start getting involved with Strolch Development, or create your own applications using Strolch, then see
the <a href="development.html">development page</a></p>
</div>
<!-- /.content -->
<div id="footer">
<div class="container">
<p class="text-muted">&copy; Strolch / <a href="mailto:eitch@eitchnet.ch">Robert von Burg</a> / Hosting by
<a href="http://www.eitchnet.ch">eitchnet.ch</a></p>
</div>
</div>
</div>
<!-- /.container -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual xsd as needed -->
<script src="js/bootstrap.min.js"></script>
<!-- Piwik -->
<script type="text/javascript">
var _paq = _paq || [];
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = (("https:" == document.location.protocol) ? "https" : "http") + "://piwik.eitchnet.ch/";
_paq.push(['setTrackerUrl', u + 'piwik.php']);
_paq.push(['setSiteId', 2]);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.type = 'text/javascript';
g.defer = true;
g.async = true;
g.src = u + 'piwik.js';
s.parentNode.insertBefore(g, s);
})();
</script>
<noscript><p><img src="http://piwik.eitchnet.ch/piwik.php?idsite=2" style="border:0;" alt="" /></p></noscript>
<!-- End Piwik Code -->
</body>
</html>