[Project] Removed website, after moving to github pages
|
@ -1,5 +0,0 @@
|
|||
target/
|
||||
.project
|
||||
.settings
|
||||
.classpath
|
||||
*.iml
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,4 +0,0 @@
|
|||
li.strolch.website
|
||||
==================
|
||||
|
||||
This project contains the sources to create Strolch's website at www.strolch.li
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch</artifactId>
|
||||
<version>1.6.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>li.strolch.website</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>li.strolch.website</name>
|
||||
<description>Strolch's Website</description>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -1,511 +0,0 @@
|
|||
<!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="plc.html">PLC</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 → the element's id</li>
|
||||
<li>Name → the element's name</li>
|
||||
<li>Type → 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">
|
||||
<!-- 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></pre>
|
||||
|
||||
XML Presentation of Strolch's top level elements of <i>Orders</i>:
|
||||
<pre class="pre-scrollable">
|
||||
<!-- 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></pre>
|
||||
|
||||
XML Presentation of Strolch's top level elements of <i>Activities</i>:
|
||||
<pre class="pre-scrollable">
|
||||
<!-- 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></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<SetParameterArg, ServiceResult> {
|
||||
|
||||
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<?> parameter = tx.findElement(arg.locator);
|
||||
|
||||
// instantiate the command
|
||||
SetParameterCommand command = new SetParameterCommand(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<?> parameter;
|
||||
private String valueAsString;
|
||||
|
||||
// undo fields
|
||||
private String oldValueAsString;
|
||||
|
||||
private StrolchRootElement replacedElement;
|
||||
|
||||
/**
|
||||
* @param container
|
||||
* @param tx
|
||||
*/
|
||||
public SetParameterCommand(StrolchTransaction tx) {
|
||||
super(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()
|
||||
&& this.replacedElement != null
|
||||
&& 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">© 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>
|
|
@ -1,707 +0,0 @@
|
|||
<!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: Blog</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="css/custom.css" rel="stylesheet">
|
||||
<link href="css/blog.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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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 class="active"><a href="blog.html">Blog</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">The Strolch Blog</h1>
|
||||
|
||||
<p class="lead page-description">The official Strolch blog with news, ideas, and thoughts on using Strolch.</p>
|
||||
</div>
|
||||
|
||||
<!-- container for whole blog -->
|
||||
<div class="row">
|
||||
|
||||
<!-- blog items -->
|
||||
<div class="col-sm-8 blog-main">
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Strolch Reports</h2>
|
||||
|
||||
<p class="blog-post-meta">30. June 2017 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Strolch can do reports!</p>
|
||||
<hr>
|
||||
<p>A feature we haven't written about yet is the report API. Strolch has it's own API to generate
|
||||
reports of data, and since we have a generic model, we use <code>Resource</code> of type
|
||||
<code>Report</code> to define them.</p>
|
||||
|
||||
<p>Go check out the <a href="documentation-reports.html">documentation</a> and then enjoy using this
|
||||
easy way to deliver the reports your peers require.</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Strolch Searches</h2>
|
||||
|
||||
<p class="blog-post-meta">23. June 2017 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Strolch queries are deprecated!</p>
|
||||
<hr>
|
||||
<p>Strolch has had once again many changes, and fixes etc. One important thing to note is that we have
|
||||
removed support for transactional mode and have rewritten how models are searched. Thus the search
|
||||
API was born.</p>
|
||||
|
||||
<p>Go check out the <a href="documentation-searches.html">Strolch Search</a> documentation and then go
|
||||
rewrite your searches =)).</p>
|
||||
|
||||
<p>Strolch tag <a href="http://search.maven.org/#search%7Cga%7C1%7Cstrolch" target="_blank">1.6.51</a>
|
||||
has all those juicy changes!</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Wow, the many changes!</h2>
|
||||
|
||||
<p class="blog-post-meta">21. March, 2017 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>So many changes, and so long no update - not good!</p>
|
||||
<hr>
|
||||
<p>Oh boy, have we forgotten to update you all on the latest awesome features in Strolch! There are over
|
||||
<a href="https://github.com/4treesCH/strolch/compare/1.3.0...develop">123 commits</a> since the last
|
||||
tag 1.3.0, so that alone merits a new blog post.</p>
|
||||
<p>Currently the latest tag is 1.5.5, but this version is actually already quite old, as it was created
|
||||
on 31. January 2017 and there are 53 new commits ahead of the tag.</p>
|
||||
|
||||
<p>Enough of all the commits, lets get to the new features:</p>
|
||||
<ul>
|
||||
<li>Added new generic report creator</li>
|
||||
<li>Added Activity.TimeOrdering and updated Model XSD</li>
|
||||
<li>Implemented State Model on Activity/Actions</li>
|
||||
<li>Implemented execution of Activities</li>
|
||||
<li>Implemented EventBasedExecutionHandler</li>
|
||||
<li>Added StrolchXmlParser to quickly parse from a file</li>
|
||||
<li>Add Activity.remove(String) to remove an element</li>
|
||||
<li>Refactored LockHandler to use Locator</li>
|
||||
<li>Added Activity.getActionsWithState(State)</li>
|
||||
<li>Moved *ToFlat and *FromFlat Json Visitors to strolch model</li>
|
||||
<li>Added StrolchElementQuery.internal()</li>
|
||||
<li>Added Parameter.clearValue() and list parameters use , as sep</li>
|
||||
<li>Json Tags are now in Tags.Json and are drinking camel-case</li>
|
||||
<li>Moved PrivilegeAddUserService to command, added tests</li>
|
||||
<li>Lots of JavaDoc updates</li>
|
||||
<li>Refactored code for REST Inspector to use gson</li>
|
||||
<li>Added inspector REST api for activities</li>
|
||||
<li>Inspector now has offset/limit for queries</li>
|
||||
<li>Added new StringMapArgument for StrolchServices</li>
|
||||
<li>Added missing activity observer calls in AbstractTransaction</li>
|
||||
<li>Added StringMapResult to use as a ServiceResult</li>
|
||||
<li>Removed many visitors and implemented proper visitor pattern...</li>
|
||||
<li>Don't log stack trace if certificate does not exist</li>
|
||||
<li>SmtpMailer now understands whitelists for override</li>
|
||||
<li>Fixed locator finding for Activity and Action</li>
|
||||
<li>Fixed undo logic for general commands</li>
|
||||
</ul>
|
||||
|
||||
<p>To summarize, <b>execution</b> and <b>reporting</b> are the two new features that make Strolch really
|
||||
awesome! We use execution to perform a number of actions on a remote device connected to a Strolch
|
||||
agent through WebSockets. This allows serial and parallel execution of actions and of course locking
|
||||
of concurrently used resources.</p>
|
||||
|
||||
<p>In an enterprise world reports can never be missed, so we needed an API to create reports. Of course
|
||||
that API was created in a way that all things are done in Strolch: generically. Thus a report is
|
||||
created as a Resource, defining the report object, columns and any relevant joins.</p>
|
||||
|
||||
<p>And one of the really cool things is that we have started with a UI for Strolch. There is now an <a
|
||||
href="https://github.com/4treesCH/strolch-wc-inspector">Inspector</a> with which the entire data
|
||||
model of a running agent can be seen. This inspector is built using Polymer and WebComponents and
|
||||
thus can be easily embedded in your application.</p>
|
||||
<p>To facilitate the authentication of a user for the inspector, an
|
||||
<a href="https://github.com/4treesCH/strolch-wc-auth">authentication component</a> was created as
|
||||
well. And of course i18n can't be forgotten, so there is a component for
|
||||
<a href="https://github.com/4treesCH/strolch-wc-localize-behavior">that</a> too.</p>
|
||||
<p>To simplify tasks in a web project, there is also a <a href="https://github.com/4treesCH/strolchjs">StrolchJs</a>
|
||||
repository where certain Strolch specific things are handled e.g. querying the authenticated user's
|
||||
roles etc.</p>
|
||||
|
||||
<p>The release of the next Strolch version isn't defined yet, as we are internally building a project on
|
||||
all these changes and with the release 1.0.0 of that project (which will be soon), we shall perform
|
||||
the next release of Strolch.</p>
|
||||
|
||||
<p>Until then, happy coding!</p>
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Strolch now on Maven Central</h2>
|
||||
|
||||
<p class="blog-post-meta">22. September, 2016 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Release Version 1.3.0 released and deployed to Maven Central</p>
|
||||
<hr>
|
||||
<p>We have released a new version of Strolch so that you can now go and use the the latest features in
|
||||
Strolch.</p>
|
||||
<p>Further we have now deployed Strolch to Maven Central, so it is easier than ever to use Strolch in
|
||||
your projects. No need to download first or use a special repository - just define the dependencies
|
||||
as you would any other dependency.</p>
|
||||
|
||||
<p>Some of the new features:</p>
|
||||
<ul>
|
||||
<li>Marshallers for JSON</li>
|
||||
<li>Versioning built into Strolch</li>
|
||||
<li>Implemented password reset API for Privilege</li>
|
||||
<li>New Component MailHandler</li>
|
||||
<li>New ToFlatJsonVisitor for simple marshalling in REST APIs</li>
|
||||
<li>Added CRUD Commands and Services for Activities</li>
|
||||
<li>Further additional bugfixes</li>
|
||||
</ul>
|
||||
|
||||
<p>Strolch has also been moved to another <a target="_blank" href="https://github.com/4treesCH/strolch">organisation</a>
|
||||
on GitHub, so if you're compiling Strolch from source, please update your GIT remote configurations.
|
||||
</p>
|
||||
|
||||
<p>Have fun using the latest and greatest version of Strolch!</p>
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Versioning of objects</h2>
|
||||
|
||||
<p class="blog-post-meta">8. August, 2016 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Opt-In versioning of objects</p>
|
||||
<hr>
|
||||
<p>A major new feature has landed in Strolch. Now, using opt-in, it is possible to have all changes to
|
||||
the object model be versioned. This means that any change to <code>Order</code>,
|
||||
<code>Resource</code> or <code>Activity</code> is automatically versioned and one can then revert to
|
||||
this version later on.</p>
|
||||
|
||||
<p>This will make it far easier to implement undo operations in applications since it is an inherent
|
||||
part of the lifecycle of objects in Strolch.</p>
|
||||
|
||||
<p>Since Strolch is supposed to be used also in small footprint hardware, this option is opt-in.</p>
|
||||
|
||||
<p>A side affect of this new feature is that we have for the time being not ported the XML persistence
|
||||
layer. If this is required, then someone drop us a note and we'll check on it.</p>
|
||||
|
||||
<p>So now go ahead and add <code><enableVersioning>true</enableVersioning></code> to your
|
||||
Realm so that versioning is enabled.</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Release 1.2.0</h2>
|
||||
|
||||
<p class="blog-post-meta">4. July, 2016 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Release of Strolch 1.2.0</p>
|
||||
<hr>
|
||||
<p>A few months ago we informed of the soon to be released version 1.1.0. Well, we decided to jump to
|
||||
1.2.0 because we did some refactorings. All the eitchnet projects have been melted into Strolch and
|
||||
thus now it's all one nice package. This will result in simpler development and less constraints on
|
||||
APIs between the two projects.</p>
|
||||
<p>Other than that, not much changed, but we are continually working on Strolch, so go grab your latest
|
||||
copy and have fun coding!</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Strolch Update</h2>
|
||||
|
||||
<p class="blog-post-meta">April 9, 2016 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Long due update on Strolch development.</p>
|
||||
<hr>
|
||||
<p>Although we have been rather quiet in the last couple of months, anyone viewing Strolch's commit log,
|
||||
will see that we certainly didn't halt Strolch development.</p>
|
||||
<p>We have been hard at work, using Strolch in projects, which required many new features and fixes. The
|
||||
commit log shows as of today over 180 commits since the release tag 1.0.0.</p>
|
||||
<p>Some of the most exciting changes are:</p>
|
||||
<ul>
|
||||
<li>REST API to query model, incl. privilege management.</li>
|
||||
<li>JSON marshalling of all elements.</li>
|
||||
<li>Added <code>Policies</code></li>
|
||||
<li>Added persisting of user sessions.</li>
|
||||
<li>New JavaScript based UI to view Strolch's model. This is an initial version and more UI elements
|
||||
and functions will follow.
|
||||
</li>
|
||||
<li>Basic planning engine functionality.</li>
|
||||
</ul>
|
||||
|
||||
<p>Further new features and changes are:</p>
|
||||
<ul>
|
||||
<li>Implemented a REST API to the privilege management - Now users can be added, changed, etc. via
|
||||
call to the appropriate URL under <code>../strolch/privilege/*</code>.
|
||||
</li>
|
||||
<li>Implemented a REST API to query the user sessions. Incl. invalidating sessions to forcefully
|
||||
logout users.
|
||||
</li>
|
||||
<li>Implemented a REST API to query <code>Audits</code>.</li>
|
||||
<li>Implemented REST API to query <code>Orders</code>, <code>Resources</code> and
|
||||
<code>Activities</code>/<code>Actions</code></li>
|
||||
<li>Implemented REST API to update <code>Resources</code> and <code>Orders</code> from XML</li>
|
||||
<li>REST API to authenticate now adds a cookie, so authorization is much simpler.</li>
|
||||
<li>Added convenience methods in <code>Service</code> and <code>Command</code> to easily perform
|
||||
system user actions.
|
||||
</li>
|
||||
<li>Added audits for login/logout of users.</li>
|
||||
<li>Added audits for changes to privilege management.</li>
|
||||
<li>PostgreSQL persistence layer now uses <a href="https://github.com/brettwooldridge/HikariCP">HikariCP</a>
|
||||
for connection pooling.
|
||||
</li>
|
||||
<li>Implemented a performance test project</li>
|
||||
<li>Added new Parameters of type <code>IntegerList</code>, <code>FloatList</code> and
|
||||
<code>LongList</code>.
|
||||
</li>
|
||||
<li>Added feature to ignore a realm on DB init.</li>
|
||||
<li>Implemented core planning functionality.</li>
|
||||
<li>Added <code>strolch_minimal</code> and <code>strolch_minimal_rest</code> projects to easily get
|
||||
started.
|
||||
</li>
|
||||
<li>Query API now has built in ordering.</li>
|
||||
<li>Added <code>Policy</code> to all root elements.</li>
|
||||
<li>Added new planning web app project. This is a test application for demoing the planning engine
|
||||
functionality of Strolch.
|
||||
</li>
|
||||
<li>Adding the persisting and reloading of user sessions, so that a new start of Strolch does not
|
||||
logout users.
|
||||
</li>
|
||||
<li>Implemented to JSON visitors for all root elements.</li>
|
||||
</ul>
|
||||
|
||||
<p>So, although we've been rather quiet on the blog and on social media, we have not been quiet in
|
||||
Strolch's development. We are planning to release version 1.1.0 soon, so stay tuned!</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Activities: Beginning of the planning engine</h2>
|
||||
|
||||
<p class="blog-post-meta">July 8, 2015 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>The ground work of the Strolch planning engine has been laid.</p>
|
||||
<hr>
|
||||
<p>One of the core ideas in building Strolch was to create a planning engine. The planning engine would
|
||||
work in combination of <code>Order</code> objects representing customer orders, <code>Resource</code>
|
||||
objects representing machines, human resources, etc., and <code>Activity</code>/<code>Action</code>
|
||||
hierarchies defining a workflow.</p>
|
||||
|
||||
<p>With the latest couple of commits to Strolch we have now added Activities and a basic planning of
|
||||
Actions onto Resources. Activities have an ordered list of <code>IActivityElement</code> which allows
|
||||
creating an arbitrary deep tree structure of Activity and Action elements.</p>
|
||||
|
||||
<p>Action objects have a list of <code>IValueChange</code> objects which define the start, end and
|
||||
further value changes over time on a referenced Resource. Thus planning an Activity is done by
|
||||
iterating the Activity hierarchy and for every Action selecting a relevant Resource and then then
|
||||
applying the changes of the Action on to the referenced <code>TimeState</code> on the Resource.</p>
|
||||
|
||||
<p>This implementation is currently very simple as it ignores all constraints which a Resource might
|
||||
have. In further development we shall implement a <code>Violation</code> model so that UIs can be
|
||||
built to visualize the over-use of Resources.</p>
|
||||
|
||||
<p>In even further steps we would then start implementing algorithms to not just apply the changes onto
|
||||
a Resource, but to actually search the Resource for time slots when the value changes would not
|
||||
violate any constraints applied to the resource.</p>
|
||||
|
||||
<p>We are very much looking forward to these new features. Stay tuned for your updates - even though
|
||||
they do take their time to arrive =).</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Strolch Documentation</h2>
|
||||
|
||||
<p class="blog-post-meta">April 6, 2015 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Any good software has some decent documentation explaining concepts, best practices and gives
|
||||
examples.</p>
|
||||
<hr>
|
||||
<p>So this post is to announce that there is now a new page on Strolch's website with a bit of
|
||||
documentation. This first documentation explains the Strolch runtime and some of the do and don't in
|
||||
Strolch code.</p>
|
||||
|
||||
<p>Bear with us, writing documentation takes time and can be outdated quickly, so we will make an effort
|
||||
to keep everything up to date and add more documentation, but this is a start.</p>
|
||||
|
||||
<p>So go ahead and read the <a href="documentation.html">documentation</a> , and if you haven't already,
|
||||
also read the rest of the website which should give some more insight into the <a href="index.html">what</a>,
|
||||
<a href="index.html">why</a> and <a href="api.html">how</a> of Strolch.</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Strolch Release 1.0.0</h2>
|
||||
|
||||
<p class="blog-post-meta">March 31, 2015 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Finally Version 1.0.0 of Strolch has been released and can be <a href="downloads.html">downloaded</a>
|
||||
immediately.</p>
|
||||
<hr>
|
||||
<p>Before 1.0.0 could be released, some major changes were decided, all driven by the first big project
|
||||
using Strolch as its underlying stack. Those changes were minor, and really major, but should make
|
||||
Strolch better and was important for the first release.</p>
|
||||
|
||||
<p>Here is a list of the most interesting changes:</p>
|
||||
<ul>
|
||||
<li>Java 8 - Strolch was ported to Java 8. This gives a lot of cool features: The stream API,
|
||||
lambdas, the new time API, etc.
|
||||
</li>
|
||||
<li>TX refactoring: Strolch transactions are instances of <code>Closeable</code> so that they are
|
||||
closed using a try-with-resource block in Java7. The change that was required was to not auto
|
||||
commit. Now a TX is read-only and one has to set the auto commit as the last statement. See <a
|
||||
href="https://github.com/4treesCH/strolch/commit/46ccb921dfa94f140cbaa3f459c2e434c913d720">this</a>
|
||||
commit for more information.
|
||||
</li>
|
||||
<li>Added a <code>tx.flush()</code> to allow an implementation to flush part of a transaction, this
|
||||
feature is vital to perform parts of a transaction before deciding if the TX should be
|
||||
committed.
|
||||
</li>
|
||||
<li>Fixed the issue where data store mode <code>CACHED</code> performed <code>TRANSACTIONAL</code>
|
||||
queries, instead of staying in-memory.
|
||||
</li>
|
||||
<li><code>ParameterSelection.stringListSelection()</code> uses a <code>StringMatchMode</code>
|
||||
instead of just <code>equals()</code></li>
|
||||
<li><code>ParameterSelection.dateRangeSelection()</code> uses a <code>DateRange</code> instead of
|
||||
just <code>equals()</code></li>
|
||||
<li>Added the <code>MigrationsHandler</code> to use to perform code migrations of production data
|
||||
bases where data shouldn't go lost.
|
||||
</li>
|
||||
<li>And many more...</li>
|
||||
</ul>
|
||||
|
||||
<p>Strolch 1.1.0 is already in development and can also be downloaded from the download page. Here you
|
||||
can see the current change list on
|
||||
<a href="https://github.com/4treesCH/strolch/compare/1.0.0...develop">GitHub</a>. For instance heavy
|
||||
work has been done to implement privilege management by adding a REST API. Looking forward to a
|
||||
wonderful next Strolch release.</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">DurationParameter and other minor changes: Release 1.0.0-RC4</h2>
|
||||
|
||||
<p class="blog-post-meta">October 9, 2014 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>New <code>DurationParameter</code> and additional minor changes: Release of 1.0.0-RC4 which can be
|
||||
downloaded on the <a href="downloads.html">download</a> page.</p>
|
||||
<hr>
|
||||
<p>While implementing a use case in a Strolch based application it was detected that an essential
|
||||
parameter type was missing, the <code>DurationParameter</code>. This parameter currently stores the
|
||||
value as a long in memory and serializes to
|
||||
<a href="http://en.wikipedia.org/wiki/ISO_8601#Durations">ISO8601</a>. As soon as we move Strolch to
|
||||
Java8, we will change this to use the <code>Period</code> class in the new Java8 date and time API.
|
||||
</p>
|
||||
|
||||
<p>In addition to the new parameter, a couple of other changes were made:</p>
|
||||
<ul>
|
||||
<li>32c1785 [Major] Added Session timeout handling</li>
|
||||
<li>d55371e [Minor] fixed component version descriptions</li>
|
||||
<li>c1cdfbb [Bugfix] added missing cloning of StringSetTimedState in Resources</li>
|
||||
<li>8f50a15 [Major] changed XML format of time value of TimedStates to be ISO8601</li>
|
||||
<li>5fbbe50 [Bugfix] fix NPE when cloning Resources with no state vars</li>
|
||||
<li>b77f4b2 [New] added TimeVariable.clear()-method</li>
|
||||
<li>Updated sub-module ch.eitchnet.utils to 906d24d</li>
|
||||
<li>Updated sub-module ch.eitchnet.privilege to aa16887</li>
|
||||
</ul>
|
||||
|
||||
<p>So, Strolch 1.0.0-RC4 is out the door, go ahead and <a href="downloads.html">try it out</a>.</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">DB Initialization: Release 1.0.0-RC3</h2>
|
||||
|
||||
<p class="blog-post-meta">August 24, 2014 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Important feature <i>Database Initialization</i> added: Release of 1.0.0-RC3 which can be downloaded
|
||||
on the <a href="downloads.html">download</a> page.</p>
|
||||
<hr>
|
||||
<p>When living <i>continuous integration</i> and <i>continuous delivery</i>, it is vital that things
|
||||
like database migrations and initialization are performed in a controlled, but automatic way.</p>
|
||||
|
||||
<p>A Strolch-based application is using the PostgreSQL persistence layer. The implementation understands
|
||||
the concepts of migration, and validating the database schema, but currently a mechanism to
|
||||
automatically initialize the database with a minimal set of data was missing.</p>
|
||||
|
||||
<p>Migrating a database for Strolch is mostly a one time thing. The object model in Strolch is quite
|
||||
static, so there is seldom a need to migrate the database. Domain specific changes, i.e. new
|
||||
Resources, or adding Parameters to Resources, is not a schema change. Thus, instead of going the way
|
||||
other frameworks go, e.g. Ruby on Rails, we built the data initialization right into the <code>PersistenceHandler</code>.
|
||||
</p>
|
||||
|
||||
<p>Now if the PostgreSQL <code>PersistenceHandler</code> creates the schema, then it might also
|
||||
initialize the minimal set of data. For this to work, the <code>PersistenceHandler</code> checks if
|
||||
the flags <code>allowSchemaCreation</code>, <code>allowSchemaDrop</code> and <code>allowDbInitOnSchemaCreate</code>.
|
||||
If those flags are enabled, and the schema was created during initialization, then the database is
|
||||
also initialized with the contents of the XML file configured under key <code>dataStoreFile</code> of
|
||||
the relevant <code>Realm</code>.</p>
|
||||
|
||||
<p>The database initialization is done as a system user action which must have the name <code>db_initializer</code>.
|
||||
This is another fail-safe, so that on a production system, this user can simply be deleted.</p>
|
||||
|
||||
<p>So, Strolch 1.0.0-RC3 is out the door, go ahead and <a href="downloads.html">try it out</a>.</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Release 1.0.0-RC2</h2>
|
||||
|
||||
<p class="blog-post-meta">August 22, 2014 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>Scratch that RC1, here is the brand new 1.0.0-RC2 which can be downloaded on the
|
||||
<a href="downloads.html">download</a> page.</p>
|
||||
<hr>
|
||||
<p>So, as expected there were a few bugs, for instance the Strolch tutorial apps didn't start, so now i
|
||||
fixed those and released an RC2. Go <a href="downloads.html">get it</a> and give it a try!</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- blog post -->
|
||||
<div class="blog-post">
|
||||
<h2 class="blog-post-title">Release 1.0.0-RC1</h2>
|
||||
|
||||
<p class="blog-post-meta">August 20, 2014 by <a href="#">Robert</a></p>
|
||||
|
||||
<p>With the Go-Live of a Strolch-based application around the corner, it is time to release Version
|
||||
1.0.0 of Strolch. To this affect we have now released version 1.0.0-RC1 which can be downloaded on
|
||||
the <a href="downloads.html">download</a> page.</p>
|
||||
<hr>
|
||||
<h3>Story</h3>
|
||||
|
||||
<p>Strolch as a component based software agent has been two years in the making. The concepts in Strolch
|
||||
have been taken from a proprietary planning, scheduling and controlling software agent, which was,
|
||||
and is been, used in industrial automation, logistics and production. Strolch was created to bring
|
||||
the concepts, which were working well for small teams to go-live with large projects in short to
|
||||
medium time-frames to the open source world.</p>
|
||||
|
||||
<p>Strolch was completely rewritten using the key concepts of a parameterized object model and a
|
||||
component based agent but remembering which clutches the original implementation had, thus trying to
|
||||
eradicate those without bringing in new ones. It might not be perfect in version 1.0.0, but it is a
|
||||
starting point form which to carry on from.</p>
|
||||
|
||||
<h3>Features</h3>
|
||||
|
||||
<p>Strolch isn't feature complete by a long shot, but it sure has got many features which make it
|
||||
useable in a concrete project, thus making sure it is not vaporware =)</p>
|
||||
|
||||
<p>The following is a list of key features, many of which were driven by concrete project
|
||||
requirements:</p>
|
||||
<ul>
|
||||
<li>Separate containers for models (mandates)</li>
|
||||
<li>Parameterized model with full
|
||||
<a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> for Resource and
|
||||
Order objects
|
||||
</li>
|
||||
<li>Timed values on Resources to map values by time</li>
|
||||
<li>Built-in versioning of model - configurable by Realm</li>
|
||||
<li>Transparent runtime modes: TRANSIENT, CACHED, TRANSACTIONAL<sup>*</sup></li>
|
||||
<li>Service and Command pattern for reusing functionality</li>
|
||||
<li><a href="https://github.com/4treesCH/ch.eitchnet.xmlpers">XML File based persistence
|
||||
layer</a><sup>*</sup></li>
|
||||
<li><a href="http://www.postgresql.org/">PostgreSQL</a> persistence layer<sup>*</sup></li>
|
||||
<li>Querying using a <a href="http://en.wikipedia.org/wiki/Fluent_API">fluent API</a></li>
|
||||
<li>Services to import and export a model to XML</li>
|
||||
<li>Integrated authentication and authorization to validate user privileges using
|
||||
<a href="https://github.com/4treesCH/ch.eitchnet.privilege">Privilege</a></li>
|
||||
<li>Ready to use <a href="http://en.wikipedia.org/wiki/Observer_pattern">Observer</a> pattern</li>
|
||||
<li>(currently) Read-only <a href="http://en.wikipedia.org/wiki/Restful">REST</a> API to access the
|
||||
agent model remotely
|
||||
</li>
|
||||
<li>Configurable environments</li>
|
||||
<li>Opt-in audit trail (including read access, and the audits themselves)</li>
|
||||
<li>Basic components required to
|
||||
<a href="https://github.com/4treesCH/ch.eitchnet.utils/tree/master/src/main/java/ch/eitchnet/communication">communicate</a>
|
||||
with external devices using TCP/IP
|
||||
</li>
|
||||
</ul>
|
||||
<p>With the light weight implementation, where there are basically no third party libraries required for
|
||||
the normal runtime, Strolch has a minimal foot print which allows it to run on small devices for
|
||||
instance a <a href="http://beagleboard.org/Products/BeagleBone+Black">BeagleBone Black</a>. Using the
|
||||
in-memory mode, it is an easy feat to set up test environments with little to no further requirements
|
||||
than the JVM.</p>
|
||||
|
||||
<h3>Future</h3>
|
||||
|
||||
<p>So what is planned for the future? Although Strolch has quite a few interesting features, it is by no
|
||||
way feature complete. The greatest wish is for Strolch to become a community driven platform, so many
|
||||
new features will arise in the future, but at least one major future feature which will be tackled in
|
||||
the near future and will certainly drive the next major release is a planning and scheduling engine
|
||||
using a <a href="http://en.wikipedia.org/wiki/Gantt_chart">Gantt</a> chart to visualize the schedule.
|
||||
</p>
|
||||
|
||||
<p>The planning engine will use the timed values on Resources extensively to create a planning engine on
|
||||
which Workflows can be placed and allowing to detect violations and bottlenecks.</p>
|
||||
|
||||
<p>An extension of the planning of the scheduling engine will allow more than just placing Workflows on
|
||||
Resources, but actually searching groups of Resources for a time slot of when to place tasks. This
|
||||
will allow to use capacity constraints to plan and schedule workflows using different algorithms, and
|
||||
respecting calendars etc.</p>
|
||||
|
||||
<p>Further time will be spent on giving Strolch it's own UI. Currently the idea is to use
|
||||
<a href="http://www.polymer-project.org/">Google's Polymer</a> to implement the UI, thus creating
|
||||
reusable widgets that can be used in projects.</p>
|
||||
|
||||
<h3>Take it for a spin</h3>
|
||||
|
||||
<p>So, now the important part is for new users to start using Strolch for their own projects. Go ahead,
|
||||
check out the <a href="downloads.html">Downloads</a> page for the latest release and then checkout
|
||||
the two tutorial applications to get yourself up to speed!</p>
|
||||
|
||||
<p>Don't hesitate to send us feedback or questions, we will be delighted to help you get your
|
||||
Strolch-based application up and running, or provide feedback to your concerns!</p>
|
||||
|
||||
<h3>Developers</h3>
|
||||
|
||||
<p>Robert von Burg<br /> Reto Breitenmoser<br /> Dr. Martin Smock<br /></p>
|
||||
|
||||
<p><sup>*</sup> Currently Transactional mode is missing concrete implementation for querying for the XML
|
||||
persistence</p>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-post -->
|
||||
|
||||
<!-- pagination -->
|
||||
<ul class="pager">
|
||||
<li><a href="#">Previous</a></li>
|
||||
<li><a href="#">Next</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<!-- /.blog-main -->
|
||||
|
||||
<!-- sidebar -->
|
||||
<div-- class="col-sm-3 col-sm-offset-1 blog-sidebar">
|
||||
<div class="sidebar-module sidebar-module-inset">
|
||||
<h4>About</h4>
|
||||
|
||||
<p>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.</p>
|
||||
</div>
|
||||
<!--div class="sidebar-module">
|
||||
<h4>Archives</h4>
|
||||
<ol class="list-unstyled">
|
||||
<li><a href="#">August 2014</a></li>
|
||||
< ! - -
|
||||
<li><a href="#">February 2014</a></li>
|
||||
<li><a href="#">January 2014</a></li>
|
||||
<li><a href="#">December 2013</a></li>
|
||||
<li><a href="#">November 2013</a></li>
|
||||
<li><a href="#">October 2013</a></li>
|
||||
<li><a href="#">September 2013</a></li>
|
||||
<li><a href="#">August 2013</a></li>
|
||||
<li><a href="#">July 2013</a></li>
|
||||
<li><a href="#">June 2013</a></li>
|
||||
<li><a href="#">May 2013</a></li>
|
||||
<li><a href="#">April 2013</a></li>
|
||||
- - >
|
||||
</ol>
|
||||
</div-->
|
||||
<div class="sidebar-module">
|
||||
<h4>Elsewhere</h4>
|
||||
<ol class="list-unstyled">
|
||||
<li><a href="https://github.com/4treesCH">GitHub</a></li>
|
||||
<li><a href="https://twitter.com/eitchme">Twitter</a></li>
|
||||
<li><a href="https://www.facebook.com/strolch.li">Facebook</a></li>
|
||||
<li><a href="https://plus.google.com/u/0/communities/100208129798096060842">Google+</a></li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.blog-sidebar -->
|
||||
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Globals
|
||||
*/
|
||||
|
||||
/*
|
||||
* Override Bootstrap's default container.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Masthead for nav
|
||||
*/
|
||||
|
||||
.blog-masthead {
|
||||
background-color: #428bca;
|
||||
-webkit-box-shadow: inset 0 -2px 5px rgba(0,0,0,.1);
|
||||
box-shadow: inset 0 -2px 5px rgba(0,0,0,.1);
|
||||
}
|
||||
|
||||
/* Nav links */
|
||||
.blog-nav-item {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
font-weight: 500;
|
||||
color: #cdddeb;
|
||||
}
|
||||
.blog-nav-item:hover,
|
||||
.blog-nav-item:focus {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Active state gets a caret at the bottom */
|
||||
.blog-nav .active {
|
||||
color: #fff;
|
||||
}
|
||||
.blog-nav .active:after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: -5px;
|
||||
vertical-align: middle;
|
||||
content: " ";
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: 5px solid;
|
||||
border-left: 5px solid transparent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main column and sidebar layout
|
||||
*/
|
||||
|
||||
.blog-main {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.blog-sidebar {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
/* Sidebar modules for boxing content */
|
||||
.sidebar-module {
|
||||
padding: 15px;
|
||||
margin: 0 -15px 15px;
|
||||
}
|
||||
.sidebar-module-inset {
|
||||
padding: 15px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.sidebar-module-inset p:last-child,
|
||||
.sidebar-module-inset ul:last-child,
|
||||
.sidebar-module-inset ol:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Pagination */
|
||||
.pager {
|
||||
margin-bottom: 60px;
|
||||
text-align: left;
|
||||
}
|
||||
.pager > li > a {
|
||||
width: 140px;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
border-radius: 30px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Blog posts
|
||||
*/
|
||||
|
||||
.blog-post {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.blog-post-title {
|
||||
margin-bottom: 5px;
|
||||
font-size: 2.0em;
|
||||
}
|
||||
.blog-post-meta {
|
||||
margin-bottom: 20px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Footer
|
||||
*/
|
||||
|
||||
.blog-footer {
|
||||
padding: 40px 0;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
background-color: #f9f9f9;
|
||||
border-top: 1px solid #e5e5e5;
|
||||
}
|
||||
.blog-footer p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
|
@ -1,347 +0,0 @@
|
|||
/*!
|
||||
* Bootstrap v3.1.1 (http://getbootstrap.com)
|
||||
* Copyright 2011-2014 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
.btn-default,
|
||||
.btn-primary,
|
||||
.btn-success,
|
||||
.btn-info,
|
||||
.btn-warning,
|
||||
.btn-danger {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-primary:active,
|
||||
.btn-success:active,
|
||||
.btn-info:active,
|
||||
.btn-warning:active,
|
||||
.btn-danger:active,
|
||||
.btn-default.active,
|
||||
.btn-primary.active,
|
||||
.btn-success.active,
|
||||
.btn-info.active,
|
||||
.btn-warning.active,
|
||||
.btn-danger.active {
|
||||
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
|
||||
}
|
||||
.btn:active,
|
||||
.btn.active {
|
||||
background-image: none;
|
||||
}
|
||||
.btn-default {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dbdbdb;
|
||||
border-color: #ccc;
|
||||
}
|
||||
.btn-default:hover,
|
||||
.btn-default:focus {
|
||||
background-color: #e0e0e0;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-default:active,
|
||||
.btn-default.active {
|
||||
background-color: #e0e0e0;
|
||||
border-color: #dbdbdb;
|
||||
}
|
||||
.btn-primary {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.btn-primary:hover,
|
||||
.btn-primary:focus {
|
||||
background-color: #2d6ca2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-primary:active,
|
||||
.btn-primary.active {
|
||||
background-color: #2d6ca2;
|
||||
border-color: #2b669a;
|
||||
}
|
||||
.btn-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-success:hover,
|
||||
.btn-success:focus {
|
||||
background-color: #419641;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-success:active,
|
||||
.btn-success.active {
|
||||
background-color: #419641;
|
||||
border-color: #3e8f3e;
|
||||
}
|
||||
.btn-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-info:hover,
|
||||
.btn-info:focus {
|
||||
background-color: #2aabd2;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-info:active,
|
||||
.btn-info.active {
|
||||
background-color: #2aabd2;
|
||||
border-color: #28a4c9;
|
||||
}
|
||||
.btn-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-warning:hover,
|
||||
.btn-warning:focus {
|
||||
background-color: #eb9316;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-warning:active,
|
||||
.btn-warning.active {
|
||||
background-color: #eb9316;
|
||||
border-color: #e38d13;
|
||||
}
|
||||
.btn-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.btn-danger:hover,
|
||||
.btn-danger:focus {
|
||||
background-color: #c12e2a;
|
||||
background-position: 0 -15px;
|
||||
}
|
||||
.btn-danger:active,
|
||||
.btn-danger.active {
|
||||
background-color: #c12e2a;
|
||||
border-color: #b92c28;
|
||||
}
|
||||
.thumbnail,
|
||||
.img-thumbnail {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-color: #e8e8e8;
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
background-color: #357ebd;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-default {
|
||||
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
|
||||
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.navbar-brand,
|
||||
.navbar-nav > li > a {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
|
||||
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.navbar-inverse .navbar-nav > .active > a {
|
||||
background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%);
|
||||
background-image: linear-gradient(to bottom, #222 0%, #282828 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-inverse .navbar-brand,
|
||||
.navbar-inverse .navbar-nav > li > a {
|
||||
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
|
||||
}
|
||||
.navbar-static-top,
|
||||
.navbar-fixed-top,
|
||||
.navbar-fixed-bottom {
|
||||
border-radius: 0;
|
||||
}
|
||||
.alert {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.alert-success {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #b2dba1;
|
||||
}
|
||||
.alert-info {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #9acfea;
|
||||
}
|
||||
.alert-warning {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #f5e79e;
|
||||
}
|
||||
.alert-danger {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dca7a7;
|
||||
}
|
||||
.progress {
|
||||
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-success {
|
||||
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
|
||||
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-info {
|
||||
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
|
||||
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-warning {
|
||||
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
|
||||
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.progress-bar-danger {
|
||||
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
|
||||
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.list-group {
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
|
||||
}
|
||||
.list-group-item.active,
|
||||
.list-group-item.active:hover,
|
||||
.list-group-item.active:focus {
|
||||
text-shadow: 0 -1px 0 #3071a9;
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #3278b3;
|
||||
}
|
||||
.panel {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
|
||||
}
|
||||
.panel-default > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-primary > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%);
|
||||
background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-success > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
|
||||
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-info > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
|
||||
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-warning > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
|
||||
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.panel-danger > .panel-heading {
|
||||
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
|
||||
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.well {
|
||||
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
|
||||
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #dcdcdc;
|
||||
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-theme.css.map */
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Globals
|
||||
*/
|
||||
|
||||
/*
|
||||
* Override Bootstrap's default container.
|
||||
*/
|
||||
|
||||
.content {
|
||||
padding: 0px 2em 3em;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
font-size: 1.0em;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.container {
|
||||
width: 970px;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0;
|
||||
font-size: 2.2em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.page-description {
|
||||
padding-top: 20px;
|
||||
font-size: 1.6em;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
#footer {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.image {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
.release, .snapshot {
|
||||
float: left;
|
||||
padding: 20px;
|
||||
}
|
||||
.release {
|
||||
width: 350px;
|
||||
}
|
||||
.snapshot {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.previousRelease {
|
||||
clear: both;
|
||||
}
|
|
@ -1,199 +0,0 @@
|
|||
<!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: Development</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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</a></li>
|
||||
<li><a href="tutorial.html">Tutorial</a></li>
|
||||
<li><a href="downloads.html">Downloads</a></li>
|
||||
<li class="active"><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">Development</h1>
|
||||
<p class="lead page-description">This page describes how to setup the development environment.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<h2>Prerequisites</h2>
|
||||
To start developing Strolch you need an installed:
|
||||
<ul>
|
||||
<li>Java JDK 8</li>
|
||||
<li>Apache Maven 3</li>
|
||||
</ul>
|
||||
|
||||
<h2>Building Strolch</h2>
|
||||
<p>Setting up Strolch is just a few lines:</p>
|
||||
<pre class="pre">
|
||||
git clone https://github.com/4treesCH/strolch.git
|
||||
cd strolch
|
||||
mvn clean install -DskipTests</pre>
|
||||
|
||||
<p><b>Note:</b> To run the tests you will need to configure the PostgreSQL Databases. See the README in the
|
||||
module.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h2>Creating a Strolch App</h2>
|
||||
<p>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.</p>
|
||||
|
||||
<p><b>Note:</b> you need to have installed Strolch to your local maven repo, otherwise the archetype won't be
|
||||
available.</p>
|
||||
|
||||
<h3>Creating a simple Java Strolch App</h3>
|
||||
<p>The following shows the maven command to create the new maven project. Note that you should replace the
|
||||
placeholders in the brackets:</p>
|
||||
<pre>
|
||||
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>"</pre>
|
||||
|
||||
<p>You change into the directory of the new project and then build the project by calling:</p>
|
||||
<pre>
|
||||
cd <my-artifactId>
|
||||
mvn clean package</pre>
|
||||
|
||||
<p>Start the program using:</p>
|
||||
<pre>
|
||||
mvn exec:java</pre>
|
||||
|
||||
<p>Happy coding =))</p>
|
||||
|
||||
<h3>Creating a Java Strolch Web App</h3>
|
||||
<p>The following shows the maven command to create the new maven project. Note that you should replace the
|
||||
placeholders in the brackets:</p>
|
||||
<pre>
|
||||
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>"</pre>
|
||||
|
||||
<h4>Install the web dependencies</h4>
|
||||
<p>The Strolch Web App uses <a href="https://nodejs.org/download/release/v11.15.0/">NodeJS v11.x</a> to build
|
||||
the web dependencies. Please download the relevant platform's package, unpack it, and add the
|
||||
<code>bin</code> directory to your path variable. </p>
|
||||
<p>Once NodeJS is installed, then you can prepare the web dependencies:</p>
|
||||
<pre>
|
||||
cd src/main/webapp/
|
||||
npm install gulp -g
|
||||
npm install
|
||||
gulp</pre>
|
||||
|
||||
<p><b>Note:</b> Whenever the <code>bower.json</code> is changed then you should again call <code>npm
|
||||
install</code>
|
||||
inside the webapp folder.</p>
|
||||
|
||||
<h4>Building the WAR</h4>
|
||||
<p>Building the WAR uses the <code>package</code> maven goal, but to have the optimized WAR use the <code>release</code>
|
||||
profile:</p>
|
||||
<pre>
|
||||
mvn clean package -Prelease</pre>
|
||||
|
||||
<p>Happy coding =))</p>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
<h3>Tools used</h3>
|
||||
The following tools are used to develop Strolch and Strolch-based projects:
|
||||
<ul class="list-unstyled">
|
||||
<li><a href="https://www.jetbrains.com/idea/download/#section=linux"><img style="height: 50px"
|
||||
class="img-thumbnail"
|
||||
src="https://upload.wikimedia.org/wikipedia/commons/d/d5/IntelliJ_IDEA_Logo.svg"
|
||||
alt="IntelliJ IDEA" /></a></li>
|
||||
<li><a href="https://maven.apache.org/"><img width="250"
|
||||
style="height: 50px; margin-top: 10px"
|
||||
class="img-thumbnail"
|
||||
src="http://maven.apache.org/images/maventxt_logo_200.gif"
|
||||
alt="Apache Maven 3.0" /></a></li>
|
||||
<li><a href="http://git-scm.com/"><img style="height: 50px; margin-top: 10px"
|
||||
class="img-thumbnail"
|
||||
src="http://git-scm.com/images/logo@2x.png"
|
||||
alt="git scm" /></a></li>
|
||||
</ul>
|
||||
|
||||
</div><!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,159 +0,0 @@
|
|||
<!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: Architecture</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Architecture</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses the architecture of a Strolch Agent</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<h2>Birds View</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>The agent itself implements the business logic using Services, Commands, Queries etc.</p>
|
||||
|
||||
<p>The agent can communicate with other 3rd systems using any API, where it is preferred to use JSON over
|
||||
REST.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>The following diagram helps visualize this:</p>
|
||||
|
||||
<img src="images/Strolch-Bird-View.svg" class="img-responsive center-block" alt="Strolch Agent Bird View">
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Squirrel View</h2>
|
||||
|
||||
<p>The following diagram shows a more detailed view of the architecture of a Strolch Agent.</p>
|
||||
|
||||
<img src="images/Strolch-Squirrel-View.svg"
|
||||
class="img-responsive center-block"
|
||||
alt="Strolch Agent Squirrel View">
|
||||
|
||||
<br />
|
||||
|
||||
<p>A Strolch agent consists of the following main parts:</p>
|
||||
<ul>
|
||||
<li>REST Endpoints → expose an API to access the Strolch agent outside of the Java VM</li>
|
||||
<li>Services and Commands → implements business logic</li>
|
||||
<li>Searches → implements specific queries against the Strolch model</li>
|
||||
<li>Components → Implements additional logic, which is best implement as a component. E.g. active
|
||||
components which have threads, etc.
|
||||
</li>
|
||||
<li>Realms → implements multi-tenant capabilities</li>
|
||||
</ul>
|
||||
|
||||
<p>In addition to the main parts, Strolch contains inherit capabilities, which gives Strolch unique features
|
||||
when compared to other Java Frameworks:</p>
|
||||
<ul>
|
||||
<li>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.
|
||||
</li>
|
||||
<li>Transactions → Transactions handle locking/unlocking of objects, updating the model and are the
|
||||
central API for the developer.
|
||||
</li>
|
||||
<li>Privileges → Strolch is user agnostic and any action, i.e. Service, Query, etc. is authorized
|
||||
against the authenticated user.
|
||||
</li>
|
||||
<li>Observers → modifications to the model are propagated to listeners using the observer pattern.</li>
|
||||
<li>Audits → Every access (read, modify) of the model are audited</li>
|
||||
<li>Versioning → modifications to objects are versioned and thus can be rolled back at a later time.
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,209 +0,0 @@
|
|||
<!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: Components</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Components</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses Strolch Components</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>A Strolch agent can be easily extended with arbitrary components. An agent is basically a container for
|
||||
classes extending <code>StrolchComponent</code>. Theses classes mostly implement an interface which describes
|
||||
the operations that are supported by the component.</p>
|
||||
|
||||
<p>The following represents a list of the most used components:</p>
|
||||
<ul>
|
||||
<li><code>RealmHandler</code>: li.strolch.agent.impl.DefaultRealmHandler</li>
|
||||
<li><code>PrivilegeHandler</code>: li.strolch.runtime.privilege.DefaultStrolchPrivilegeHandler</li>
|
||||
<li><code>EnumHandler</code>: li.strolch.runtime.query.enums.DefaultEnumHandler</li>
|
||||
<li><code>PolicyHandler</code>: li.strolch.policy.DefaultPolicyHandler</li>
|
||||
<li><code>ServiceHandler</code>: li.strolch.service.api.DefaultServiceHandler</li>
|
||||
<li><code>StrolchSessionHandler</code>: li.strolch.rest.DefaultStrolchSessionHandler</li>
|
||||
<li><code>PersistenceHandler</code>: multiple implementations</li>
|
||||
<li><code>PostInitializer</code>: project specific implementation</li>
|
||||
<li><code>MailHandler</code>: li.strolch.handler.mail.SmtpMailHandler</li>
|
||||
</ul>
|
||||
|
||||
<p>A component has a life-cycle, which is governed by the Agent's own life-cycle. The life-cycle is as
|
||||
follows:</p>
|
||||
|
||||
<pre>
|
||||
setup -> initialize -> start <-> stop -> destroy</pre>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>Each component has its own configuration parameters. A component is registered in the <code>StrolchConfiguration.xml</code>
|
||||
file with a</p>
|
||||
<ul>
|
||||
<li>name</li>
|
||||
<li>api class name</li>
|
||||
<li>implementation class name</li>
|
||||
<li>configuration parameters</li>
|
||||
<li>any required dependencies</li>
|
||||
</ul>
|
||||
|
||||
<p>The dependencies is an important feature as the dependencies of a component are always started before the
|
||||
actual component.</p>
|
||||
|
||||
<p>By example of the <code>MailHandler</code> we shall show how a strolch component would be implemented.</p>
|
||||
|
||||
<p>First define an interface:</p>
|
||||
<pre>
|
||||
public interface MailHandler {
|
||||
public void sendMail(String subject, String text, String recipient);
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
<p>Then implement a concrete <code>MailHandler</code>:</p>
|
||||
|
||||
<pre>
|
||||
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
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>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 <code>StrolchConfiguration.xml</code> file must be modified to include a
|
||||
component element:</p>
|
||||
|
||||
<pre>
|
||||
<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>
|
||||
</pre>
|
||||
|
||||
<p>Now when the agent is started, the component can be retrieved and used. E.g from inside a
|
||||
<code>Service</code>:</p>
|
||||
|
||||
<pre>
|
||||
MailHandler mailHandler = getComponent(MailHandler.class);
|
||||
mailHandler.sendMail("My Subject", "Hello World", "test@test.ch");
|
||||
</pre>
|
||||
</pre>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,123 +0,0 @@
|
|||
<!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: Do and Don't</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Do and Don't</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses things you should and shouldn't do when using Strolch.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>Dos and Don'ts</p>
|
||||
<ul>
|
||||
<li>1 <code>Service</code> per use-case, should mostly delegate to <code>Commands</code>.</li>
|
||||
<li><code>Commands</code> implement use-cases or parts of it, and are thus reusable.</li>
|
||||
<li>Subclass <code>ResourceSearch</code>, <code>OrderSearch</code> and <code>ActivitySearch</code> when
|
||||
implementing use-case specific search - this allows privilege checking.
|
||||
</li>
|
||||
<li>One Transaction at a time - no TX inside of another TX.</li>
|
||||
<li>Commands are added to TXs and performed on close: <code>tx.addCommand(Command)</code> and then <code>tx.commitOnClose()</code>
|
||||
</li>
|
||||
<li>Use <code>tx.flush() if you need to perform first some work and then as late as possible call <code>tx.commitOnClose()</code></code>
|
||||
</li>
|
||||
<li>Only access <code>ElementMap</code>s if really no other way, mostly use <code>tx.get*By()</code>, <code>tx.findBy()</code>
|
||||
and queries - if a specific get is missing, then add the method to <code>StrolchTransaction</code> and
|
||||
send a pull request!
|
||||
</li>
|
||||
<li>Use <code>tx.stream*()</code> methods to iterate over all elements, if you don't want to use a search.
|
||||
</li>
|
||||
<li>Don't write logic in REST API beans. Delegate to other services, making your code reusable!</li>
|
||||
<li>Transform to JSON using the <code>StrolchElementToJsonVisitor</code>.</li>
|
||||
<li>References between objects is done by adding a <code>ParameterBag</code> with the id
|
||||
<code>relations</code> to the object and then <code>StringParameters</code> 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.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,391 +0,0 @@
|
|||
<!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: Model</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Model</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses Strolch's model</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>Before we dive into the entire model, let's show an example and how it would be modelled in Strolch and use
|
||||
in Strolch:</p>
|
||||
<img class="image" src="images/strolch-model-example.png" alt="Strolch model example">
|
||||
<p>A possible model would look as follows:</p>
|
||||
<pre>
|
||||
<?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></pre>
|
||||
<p>Let's go through this model:</p>
|
||||
<ul>
|
||||
<li><p>In the above model we see that the <code>id</code> and <code>name</code> fields are always on the
|
||||
element, and thus aren't added as parameters. Further most elements have a <code>parameters</code>
|
||||
ParameterBag, with one or more parameters, modelling the fields.</p></li>
|
||||
<li><p>Note that in this example the <code>Type</code> of all the elements is <code>Template</code>. Strolch
|
||||
has API support to create a clone of these elements, so that they have a unique ID, and the proper
|
||||
type for persistence.</p>
|
||||
</li>
|
||||
<li><p>The <code>Product</code> element has three parameters: <code>description</code>, <code>color</code>
|
||||
and <code>form</code>. In this case they are all of type String. Further the <code>relations</code>
|
||||
ParameterBag defines the relationships, i.e. the product knows its articles. Note how the relation is
|
||||
first defined in a <code>relations</code> ParameterBag and that the Parameter has <code>Interpretation="Resource-Ref"
|
||||
Uom="Product"</code>
|
||||
attributes. Strolch has API support for this, making it trivial to retrieve a dependency.</p></li>
|
||||
<li><p>The <code>Article</code> element has two parameters <code>description</code> and <code>barcode</code>.
|
||||
Further it has a reference to its Product.</p></li>
|
||||
<li>The <code>Order</code> element doesn't model the <code>date</code> and <code>state</code> fields as
|
||||
parameters, as these are inherently part of an Order element. The Order does define two references to
|
||||
<code>customer</code> and <code>articles</code>. A special case is the <code>quantities</code>
|
||||
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.
|
||||
</li>
|
||||
<li>The <code>Customer</code> element models a <code>address</code> ParameterBag to store the address of a
|
||||
customer. Using a separate bag allows for further more direct fields to stored on the default <code>parameters</code>
|
||||
bag.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>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:</p>
|
||||
<pre>
|
||||
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();
|
||||
}
|
||||
|
||||
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());
|
||||
}</pre>
|
||||
|
||||
<p><b>Note:</b> Checkout
|
||||
<a href="https://github.com/4treesCH/strolch/blob/develop/li.strolch.service/src/test/resources/transienttest/data/example-model.xml">example-model.xml</a>
|
||||
and
|
||||
<a href="https://github.com/4treesCH/strolch/blob/develop/li.strolch.service/src/test/java/li/strolch/service/SimpleModelTest.java">SimpleModelTest.java</a>
|
||||
for these examples. </p>
|
||||
|
||||
<p>There is a XML Schema which defines the model in XML:
|
||||
<a href="xsd/StrolchModel-1.6.xsd">StrolchModel-1.6.xsd</a>
|
||||
</p>
|
||||
|
||||
Here is an example of all the possible elements in Strolch:
|
||||
<pre>
|
||||
<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></pre>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,151 +0,0 @@
|
|||
<!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: Observers</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Observers</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses the Strolch Observers</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>All changes done in a Strolch transaction are recorded and then propagated to any registered observers.</p>
|
||||
|
||||
<p>The observer feature is opt-in and is configured for each realm. In the <code>StrolchConfiguration.xml</code>
|
||||
file enable observers by adding the <code>enableObserverUpdates</code> propery per realm:</p>
|
||||
<pre>
|
||||
<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></pre>
|
||||
|
||||
<br />
|
||||
<p>Registering for updates is done by registering an <code>Observer</code> on the <code>ObserverHandler</code>
|
||||
of the realm itself:</p>
|
||||
<pre>
|
||||
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!");
|
||||
}
|
||||
});</pre>
|
||||
|
||||
<br />
|
||||
<p>Observer updates can be suppressed on the transaction by calling <code>tx.setSuppressUpdates()</code></p>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,179 +0,0 @@
|
|||
<!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: Policies</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Policies</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses the Strolch policies</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>Policies are an integral part when writing business logic in Strolch. In many cases it would suffice to write
|
||||
all such logic in <code>Services</code> and <code>Commands</code>, but as soon as behaviour can change,
|
||||
depending on the element being accessed, then this would quickly lead to many if/else blocks.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>Currently there are two ways to reference a policy in Strolch, either via a key which defines a further
|
||||
lookup in the <code>PolicyHandler</code>, or directly as the name of the class to instantiate.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>Policies are implemented by defining an abstract class and extends <code>StrolchPolicy</code>. This abstract
|
||||
class then defines the API of the actual policy. A concrete class then extends this abstract class and
|
||||
implements the concrete methods.</p>
|
||||
|
||||
<p>Policies are registered on Resources, Orders, Activities and Actions. The following shows defining two
|
||||
policies on a Resource, a PlanningPolicy, an ExecutionPolicy in XML:</p>
|
||||
<pre>
|
||||
<Resource ...>
|
||||
...
|
||||
<Policies>
|
||||
<Policy Type="PlanningPolicy" Value="key:SimplePlanning" />
|
||||
<Policy Type="ExecutionPolicy" Value="java:li.strolch.policytest.TestSimulatedExecutionPolicy" />
|
||||
</Policies>
|
||||
</Resource></pre>
|
||||
|
||||
<p><b>Note</b> how the PlanningPolicy has a value of <code>key:SimplePlanning</code> and the ExecutionPolicy
|
||||
defines a reference to an actual class.</p>
|
||||
|
||||
<br />
|
||||
<p>To use the PolicyHandler, it must be configured in the StrolchConfiguration.xml as follows:</p>
|
||||
<pre>
|
||||
<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></pre>
|
||||
|
||||
<br />
|
||||
<p>And this policy handler implementation requires a file where the lookups for the policies is defined,
|
||||
e.g.:</p>
|
||||
<pre>
|
||||
<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></pre>
|
||||
|
||||
<br />
|
||||
<p>Now at runtime we can access the policies:</p>
|
||||
<pre>
|
||||
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(...);
|
||||
}</pre>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,211 +0,0 @@
|
|||
<!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: Privileges</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Privileges</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses Privilege management in Strolch.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<!-- content here -->
|
||||
<p>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.</p>
|
||||
|
||||
<p>As explained in the <a href="documentation-runtime.html#privilegeConfig">Privilege Configuration</a> section,
|
||||
users are defined in the <code>PrivilegeUsers.xml</code> file, and roles are defined in the <code>PrivilegeRoles.xml</code>
|
||||
file.</p>
|
||||
|
||||
<p>Let's assume the following user and role definition:</p>
|
||||
|
||||
<pre>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</pre>
|
||||
|
||||
<p>This configuration contains one user and one role. The user <code>jill</code> has the role
|
||||
<code>AppUser</code> and the role <code>AppUser</code> has three privileges assigned.</p>
|
||||
|
||||
<p>Note how the user's password is configured similar to a unix password definition: Using the dollar sign
|
||||
<code>&</code> first the hashing algorithm is configured (algorithm, iterations, key length), then the
|
||||
salt, followed by the password hash.</p>
|
||||
|
||||
<p><code>Note:</code> 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.</p>
|
||||
|
||||
<p>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 <code>NEW</code>, <code>ENABLED</code>, <code>DISABLED</code>,
|
||||
<code>EXPIRED</code> or <code>SYSTEM</code>. A user can only authenticate/login with the state
|
||||
<code>ENABLED</code>. 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.</p>
|
||||
|
||||
<p>Roles have a name and any number of <code>Privilege</code> 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 <code>policy</code> value
|
||||
defines which policy to use when evaluating the privilege access. The privilege definition is defined in the
|
||||
<code>PrivilegeConfig.xml</code> and is the name of a class to call for privilege validation.</p>
|
||||
|
||||
<p>Further the privilege definitions can have a <code>AllAllowed</code> boolean flag, or any number of <code>Allow</code>
|
||||
or <code>Deny</code> values. How these values are interpreted is defined in the policy implementation. A
|
||||
policy takes three input parameters: </p>
|
||||
<ul>
|
||||
<li><code>PrivilegeContext</code> → supplied by privilege and gives access to the
|
||||
<code>Certificate</code>, thus identifying the user for which privilege access is to be validated.
|
||||
</li>
|
||||
<li><code>IPrivilege</code> → Contains the privilege values: <code>AllAllowed</code>,
|
||||
<code>Allow</code> and <code>Deny</code></li>
|
||||
<li><code>Restrictable</code> → 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.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>The following privilege policies are already implemented:</p>
|
||||
<ul>
|
||||
|
||||
<li>DefaultPrivilege → simple policy where the passed <code>Restrictable</code> is expected to return a
|
||||
String value, which is compared with allow and deny values.
|
||||
</li>
|
||||
|
||||
|
||||
<li style="margin-top: 1.0em"><i>Internal:</i> RoleAccessPrivilege → policy used for the internal
|
||||
privileges <code>PrivilegeGetRole</code>, <code>PrivilegeAddRole</code>,
|
||||
<code>PrivilegeModifyRole</code> or <code>PrivilegeModifyRole</code></li>
|
||||
<li><i>Internal:</i> UserAccessPrivilege → policy used for the internal privileges <code>PrivilegeGetUser</code>,
|
||||
<code>PrivilegeAddUser</code>, <code>PrivilegeRemoveUser</code>, <code>PrivilegeModifyUser</code>,
|
||||
<code>PrivilegeAddRoleToUser</code>, <code>PrivilegeRemoveRoleFromUser</code>, <code>PrivilegeSetUserState</code>,
|
||||
<code>PrivilegeSetUserLocale</code> or <code>PrivilegeSetUserPassword</code></li>
|
||||
<li><i>Internal:</i> UserAccessWithSameOrganisationPrivilege → Same as the
|
||||
<code>UserAccessPrivilege</code> but expects the authenticated user to have a property <code>organisation</code>
|
||||
and validates that the user being modified is in the same organisation.
|
||||
</li>
|
||||
<li><i>Internal:</i> UsernameFromCertificatePrivilege → This policy expects a <code>Restrictable</code>
|
||||
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.
|
||||
</li>
|
||||
<li><i>Internal:</i> UsernameFromCertificateWithSameOrganisationPrivilege → Same as <code>UsernameFromCertificatePrivilege</code>
|
||||
but expects the authenticated user to have a property <code>organisation</code> and
|
||||
validates that the user being modified is in the same organisation.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p><b>Note:</b> As a rule, the sequence is <code>AllAllowed</code> → <code>Allow</code> →
|
||||
<code>Deny</code> → <code>default deny</code></p>
|
||||
|
||||
<p></p>
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,202 +0,0 @@
|
|||
<!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: Queries</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Queries</h1>
|
||||
<p class="lead page-description">This page discusses Strolch Queries.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p class="lead"><b>The Query API is deprecated and the search API should be used instead.</b>
|
||||
</p>
|
||||
|
||||
<p>As is custom for every framework, querying the model must be possible. Strolch queries are implemented using
|
||||
the <code>StrolchQuery</code> interface and one of its concrete implementations: <code>ResourceQuery</code>,
|
||||
<code>OrderQuery</code>, <code>ActivityQuery</code>.</p>
|
||||
|
||||
<p>A Strolch element always has two identifiers: <code>Type</code> and <code>Id</code>. 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
|
||||
<code>Resource</code>, but one of type <code>Car</code> and the other of type <code>House</code>. Both would
|
||||
have different parameters.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>Further input for a StrolchQuery are the selections. These selections get translated into RDBMS
|
||||
<code>WHERE</code> clauses. Selections support boolean operations thus allowing for complex querying.</p>
|
||||
|
||||
<p>StrolchQueries also support Ordering and object transformation. Following classes provide the most used
|
||||
scenarios:</p>
|
||||
<ul>
|
||||
<li><code>OrderById</code></li>
|
||||
<li><code>OrderByName</code></li>
|
||||
<li><code>OrderByParameter</code></li>
|
||||
<li><code>*ToDomVisitor</code></li>
|
||||
<li><code>*ToSaxVisitor</code></li>
|
||||
<li><code>*ToJsonVisitor</code></li>
|
||||
<li><code>*ToFlatJsonVisitor</code></li>
|
||||
</ul>
|
||||
|
||||
<br />
|
||||
<p>Example: Query all resources of type Car:</p>
|
||||
<pre>
|
||||
try (StrolchTransaction tx = openTx()) {
|
||||
ResourceQuery<Resource> query = ResourceQuery.query("Car");
|
||||
query.withAny();
|
||||
List<Resource> cars = tx.doQuery(query);
|
||||
}</pre>
|
||||
|
||||
<br />
|
||||
<p>Example: Query all resources of type Car, order by Name and transform to JSON:</p>
|
||||
<pre>
|
||||
try (StrolchTransaction tx = openTx()) {
|
||||
ResourceQuery<JsonObject> query = ResourceQuery.query("Car", new ResourceToJsonVisitor(),
|
||||
new OrderByName());
|
||||
query.withAny();
|
||||
List<JsonObject> cars = tx.doQuery(query);
|
||||
}</pre>
|
||||
|
||||
<br />
|
||||
<p>the previous example can also be written as follows:</p>
|
||||
<pre>
|
||||
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);
|
||||
}</pre>
|
||||
|
||||
<br />
|
||||
<p>Example: Query all resources of type Car with color blue:</p>
|
||||
<pre>
|
||||
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);
|
||||
}</pre>
|
||||
|
||||
<br />
|
||||
<p>Example: Query all resources of type Car which are not blue:</p>
|
||||
<pre>
|
||||
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);
|
||||
}</pre>
|
||||
|
||||
<br />
|
||||
<p>Example: Query all resources of type Car with color blue or yellow:</p>
|
||||
<pre>
|
||||
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);
|
||||
}</pre>
|
||||
|
||||
<br />
|
||||
<p>Example: Query all resources of type Car with color blue or yellow owned by Jill:</p>
|
||||
<pre>
|
||||
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);
|
||||
}</pre>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,205 +0,0 @@
|
|||
<!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: Realms</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Realms</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses Strolch Realms</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
A realm can run in one of the following modes:
|
||||
<ul>
|
||||
<li>EMPTY <br /> 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.
|
||||
</li>
|
||||
<li>TRANSIENT <br /> 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.
|
||||
</li>
|
||||
<li>CACHED <br /> 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.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Realms are mostly hidden from a developer as a <code>StrolchTransaction</code> 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 <code>defaultRealm</code> can be used,
|
||||
where the developer only is required to configure the mode and any relevant model file.</p>
|
||||
|
||||
<p>If the mode is CACHED, then the <code>PersistenceHandler</code> component is required to be configured, so
|
||||
that the DAOs know how to access the underlying database.</p>
|
||||
|
||||
<p>The configuration in the <code>StrolchConfiguration.xml</code> file is as follows:</p>
|
||||
|
||||
<pre>
|
||||
<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>
|
||||
<!-- if CACHED: -->
|
||||
<!--depends>PersistenceHandler</depends-->
|
||||
<Properties>
|
||||
<dataStoreMode>EMPTY|TRANSIENT|CACHED</dataStoreMode>
|
||||
<dataStoreFile>StrolchModel.xml</dataStoreFile>
|
||||
</Properties>
|
||||
</Component>
|
||||
...
|
||||
</env>
|
||||
</StrolchConfiguration>
|
||||
</pre>
|
||||
|
||||
<p>A multi-realm configuration would be as follows. Note how the <code>defaultRealm</code> is still enabled, and
|
||||
has its configuration as before. Further the PostgreSQL <code>PersistenceHandler</code> is configured to show
|
||||
how the realms are connected to the persistence handler:</p>
|
||||
|
||||
<pre>
|
||||
<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>
|
||||
<depends>PersistenceHandler</depends>
|
||||
<Properties>
|
||||
<realms>defaultRealm, cachedRealm</realms>
|
||||
<dataStoreMode>TRANSIENT</dataStoreMode>
|
||||
<dataStoreFile>DefaultRealm.xml</dataStoreFile>
|
||||
<dataStoreMode.cachedRealm>CACHED</dataStoreMode.cachedRealm>
|
||||
<dataStoreMode.emptyRealm>EMPTY</dataStoreMode.emptyRealm>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<name>PersistenceHandler</name>
|
||||
<api>li.strolch.persistence.api.PersistenceHandler</api>
|
||||
<impl>li.strolch.persistence.postgresql.PostgreSqlPersistenceHandler</impl>
|
||||
<Properties>
|
||||
<allowSchemaCreation>true</allowSchemaCreation>
|
||||
<allowSchemaDrop>true</allowSchemaDrop>
|
||||
|
||||
<db.url.cachedRealm>jdbc:postgresql://localhost/testdb2</db.url.cachedRealm>
|
||||
<db.username.cachedRealm>testuser2</db.username.cachedRealm>
|
||||
<db.password.cachedRealm>test</db.password.cachedRealm>
|
||||
<db.pool.maximumPoolSize.cachedRealm>1</db.pool.maximumPoolSize.cachedRealm>
|
||||
<db.pool.keepaliveTime.cachedRealm>600000</db.pool.keepaliveTime.cachedRealm>
|
||||
</Properties>
|
||||
</Component>
|
||||
...
|
||||
</env>
|
||||
</StrolchConfiguration>
|
||||
</pre>
|
||||
|
||||
<p>Accessing a realm is done in multiple ways. Important is to note, that a user should use the <code>StrolchTransaction</code>
|
||||
object, instead of accessing the Realm directly.</p>
|
||||
|
||||
<p>Opening a transaction is done from a <code>Service</code> by calling one of the <code>openTx()</code>-methods.
|
||||
Nevertheless, the realm can be accessed as follows:</p>
|
||||
|
||||
<pre>
|
||||
ComponentContainer container = getAgent().getContainer();
|
||||
StrolchRealm realm = container.getRealm(StrolchConstants.DEFAULT_REALM);
|
||||
try(StrolchTransaction tx = realm.openTx()) {
|
||||
Resource resource = tx.getResourceBy("TestType", "MyTestResource");
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,367 +0,0 @@
|
|||
<!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: Reports</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Reports</h1>
|
||||
<p class="lead page-description">This page discusses how to create reports in Strolch</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>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 <code>Resource</code> of type
|
||||
<code>Report</code>.</p>
|
||||
|
||||
<p>A report consists of the following parts:</p>
|
||||
<ul>
|
||||
<li>policy definition, thus allowing extensions</li>
|
||||
<li>basic configuration like base object type, order direction, etc.</li>
|
||||
<li>column definitions</li>
|
||||
<li>joins</li>
|
||||
<li>ordering definition</li>
|
||||
<li>filters</li>
|
||||
</ul>
|
||||
|
||||
<p>An example of a report is as follows:</p>
|
||||
<pre>
|
||||
<Resource Id="stockReport" Name="Stock Report" Type="Report">
|
||||
|
||||
<ParameterBag Id="parameters" Name="parameters" Type="Parameters">
|
||||
<Parameter Id="objectType" Index="20" Hidden="false" Name="Object Type" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Player"/>
|
||||
<Parameter Id="descending" Name="Descending order" Type="Boolean" Value="true"/>
|
||||
</ParameterBag>
|
||||
|
||||
<ParameterBag Id="ordering" Name="Ordering" Type="Ordering">
|
||||
<Parameter Id="name" Name="Name" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>
|
||||
</ParameterBag>
|
||||
|
||||
<ParameterBag Id="noTeamFilter" Name="Filter" Type="Filter">
|
||||
<Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/>
|
||||
<Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Slot" Value="Bags/relations/team"/>
|
||||
</ParameterBag>
|
||||
|
||||
<ParameterBag Id="columns" Name="Display Columns" Type="Display">
|
||||
<Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>
|
||||
<Parameter Id="birthDate" Name="Birth date" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Bags/parameters/birthDate"/>
|
||||
<Parameter Id="team" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="$name"/>
|
||||
</ParameterBag>
|
||||
|
||||
<ParameterBag Id="joins" Name="Joins" Type="Joins">
|
||||
<Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/>
|
||||
</ParameterBag>
|
||||
|
||||
<Policies>
|
||||
<Policy Type="ReportPolicy" Value="java:li.strolch.report.policy.GenericReport"/>
|
||||
</Policies>
|
||||
|
||||
</Resource></pre>
|
||||
|
||||
<p>This report</p>
|
||||
<ul>
|
||||
<li>shows all Resources of type player (parameter <code>objectType</code>) → marks the object type to
|
||||
be show in the filter criteria (default), and that its sorting index is at 20.
|
||||
</li>
|
||||
<li>orders the report by player's name (parameter bag <code>ordering</code>)</li>
|
||||
<li>filters out all players with no team assigned (parameter bag <code>noTeamFilter</code>)</li>
|
||||
<li>defines three columns: Player, Birth date, Team (paramger bag <code>columns</code>)</li>
|
||||
<li>joins in the resource of type <code>Team</code></li>
|
||||
<li>Uses the <code>GenericReport</code> class to generate the report</li>
|
||||
</ul>
|
||||
|
||||
<h2>GenericReport</h2>
|
||||
|
||||
The default generic report implemented in Strolch has the following features and options:
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<p>The parameters bag can contain the following parameters:</p>
|
||||
<ul>
|
||||
<li><code>objectType</code> → the base type of object to get the input for the report. This means that
|
||||
the <code>Interpretation</code> is set to one of:
|
||||
<ul>
|
||||
<li>Resource-Ref</li>
|
||||
<li>Order-Ref</li>
|
||||
<li>Activity-Ref</li>
|
||||
</ul>
|
||||
and that the <code>UOM</code> and <code>value</code> of the parameter is set to
|
||||
the type of element with which to retrieve the elements from the strolch model.
|
||||
<br /> <b>Note:</b> that the attributes <code>Hidden</code> and <code>Index</code> define the visibility
|
||||
and sorting index as filter criteria respectively.
|
||||
</li>
|
||||
<li><code>descending</code> → boolean flag to define if sorting is in descending order</li>
|
||||
<li><code>allowMissingColumns</code> → flag to define if no exception should be thrown if a column is
|
||||
missing
|
||||
</li>
|
||||
<li><code>dateRangeSel</code> → defines a lookup parameter to use as a date range selector. This
|
||||
requires input when executing the report
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3>Lookups</h3>
|
||||
<p>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:</p>
|
||||
<ul>
|
||||
<li><code>$id</code> → lookup the ID of the element</li>
|
||||
<li><code>$name</code> → lookup the name of the element</li>
|
||||
<li><code>$type</code> → lookup the type of the element</li>
|
||||
<li><code>$date</code> → lookup the date of the element (only possible on <code>Order</code> and <code>Activity</code>
|
||||
elements)
|
||||
</li>
|
||||
<li><code>$state</code> → lookup the state of the element (only possible on <code>Order</code> and
|
||||
<code>Activity</code> elements)
|
||||
</li>
|
||||
<li><code>Bags/<bag_id>/<param_id></code> →</li>
|
||||
<li><code>$search:<parent_ref_id>:Bags/<bag_id>/<param_id></code> → searches for a
|
||||
parameter with the given
|
||||
bag and parameter, and
|
||||
if it does not exist,
|
||||
looks for the parent
|
||||
with the given <code>parent_ref_id</code>
|
||||
on the element. This
|
||||
allows a recursive
|
||||
search up a tree of
|
||||
elements which all have
|
||||
the same parameter
|
||||
referencing a parent.
|
||||
<code>relations</code> bag
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p><b>Note:</b> these definitions are set as the value of a Parameter, and the <code>Interpretation</code> and
|
||||
<code>UOM</code> of the parameter is used to find the element on which to perform the lookup. I.e. the
|
||||
following definition:</p>
|
||||
<pre><Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/></pre>
|
||||
<p>defines that we want to lookup the name of the resource of type Player.</p>
|
||||
|
||||
<h3>Ordering</h3>
|
||||
<p>Ordering, i.e sorting is done by adding the parameter bag with the id <code>ordering</code> and each
|
||||
parameter defines a column to order by. The sequence of the ordering is defined by the index value assigned
|
||||
to each parameter.</p>
|
||||
|
||||
<h3>Filtering</h3>
|
||||
<p>Filtering use additional Strolch Policies which implement the operator function. I.e. performing an equals,
|
||||
etc. The following <code>ReportFilterPolicy</code> are available and should be added in your <code>StrolchPolicies.xml</code>
|
||||
file:</p>
|
||||
<pre>
|
||||
<StrolchPolicies>
|
||||
...
|
||||
<PolicyType Type="ReportFilterPolicy" Api="li.strolch.report.policy.ReportFilterPolicy">
|
||||
<Policy Key="GreaterThan" Class="li.strolch.report.policy.GreaterThanReportFilter" />
|
||||
<Policy Key="LessThan" Class="li.strolch.report.policy.LessThanReportFilter" />
|
||||
<Policy Key="Equals" Class="li.strolch.report.policy.EqualsReportFilter" />
|
||||
</PolicyType>
|
||||
...
|
||||
<StrolchPolicies></pre>
|
||||
|
||||
<p>From this we can see that we can perform a <code>GreaterThan</code>, <code>LessThan</code> and
|
||||
<code>Equals</code> filtering. These filters can also be negated by prefixing the filter value with an
|
||||
exclamation mark (!).</p>
|
||||
|
||||
<p>A special case for the filter values are filters on dates. If you are filtering on a date, then you can use
|
||||
the special operator <code>now()</code>. This filter will use the current date and time and will add/subtract
|
||||
the ISO8601 period passed as an argument to the operator.</p>
|
||||
|
||||
<p>The following shows examples of these filters:</p>
|
||||
<pre>
|
||||
<ParameterBag Id="minQtyFilter" Name="Filter" Type="Filter">
|
||||
<Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:GreaterThan" Value="10"/>
|
||||
<Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/quantity"/>
|
||||
</ParameterBag>
|
||||
|
||||
<ParameterBag Id="notEmptyFilter" Name="Filter" Type="Filter">
|
||||
<Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/>
|
||||
<Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Bags/relations/team"/>
|
||||
</ParameterBag>
|
||||
|
||||
<ParameterBag Id="threeMonthsAgoFilter" Name="Filter" Type="Filter">
|
||||
<Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:LessThan" Value="now(-P3M)"/>
|
||||
<Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="FromStock" Value="$date"/>
|
||||
</ParameterBag></pre>
|
||||
|
||||
<p><b>Note: </b> One parameter defines which policy gets used and the <code>key:<name></code> value
|
||||
references a policy defined in the <code>StrolchPolicies.xml</code> file. Further the lookup is
|
||||
defined in the <code>fieldRef</code> parameter.</p>
|
||||
|
||||
<h3>Joins</h3>
|
||||
<p>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 <code>joins</code> and then each parameter references an
|
||||
element to join. The joining is done as follows:</p>
|
||||
<ul>
|
||||
<li>The <code>Intepretation</code> and <code>UOM</code> define which object we want to join, i.e. resource
|
||||
of type foo
|
||||
</li>
|
||||
<li>The value of the parameter defines the type of element on which to find the reference</li>
|
||||
<li>The join ordering is not relevant, as the tree is traversed accordingly</li>
|
||||
<li>At least one join must reference the base object type</li>
|
||||
<li>The lookup of the join is done by finding a parameter with any ID, which has the same <code>Interpretation</code>
|
||||
and <code>UOM</code> as the join definition
|
||||
</li>
|
||||
<li>The attributes <code>Hidden</code> and <code>Index</code> define the visibility and sorting index as
|
||||
filter criteria respectively.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>Thus the following:</p>
|
||||
|
||||
<pre>
|
||||
<ParameterBag Id="joins" Name="Joins" Type="Joins">
|
||||
<Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/>
|
||||
<Parameter Id="Country" Index="5" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Country" Value="Team"/>
|
||||
</ParameterBag></pre>
|
||||
|
||||
<p>Performs two joins: First we join a resource of type <code>Team</code> by finding the relevant parameter on
|
||||
the <code>Player</code> resource, and then we lookup a resource of type <code>Country</code> on the
|
||||
previously joined <code>Team</code> resource.</p>
|
||||
|
||||
|
||||
<h3>Execution of Reports</h3>
|
||||
<p>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.:</p>
|
||||
<pre>
|
||||
Stream<JsonObject> jsonObjectStream = new Report(tx, reportId).doReportAsJson();</pre>
|
||||
|
||||
<p>If you prefer a CSV report:</p>
|
||||
<pre>
|
||||
try (CSVPrinter csvP = new CSVPrinter(new OutputStreamWriter(out),
|
||||
CSVFormat.DEFAULT.withHeader(headers).withDelimiter(';'))) {
|
||||
|
||||
// do report without AsJson, and then iterating each row and sending to a CSV writer
|
||||
report.doReport().forEach(row -> {
|
||||
try {
|
||||
csvP.printRecord(row.valueStream().collect(Collectors.toList())); // add to CSV
|
||||
} catch (Exception e) {
|
||||
logger.error("Could not write CSV row", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>Filter Criteria</h3>
|
||||
<p>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.</p>
|
||||
|
||||
<p>To perform these dynamic filterings, one would call the <code>filter()</code> 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 <code>product01</code> Product and
|
||||
<code>location02</code> Location elements:</p>
|
||||
<pre>
|
||||
new Report(tx, "stockReport")
|
||||
.filter("Product", "product01")
|
||||
.filter("Location", "location02")
|
||||
.doReportAsJson()</pre>
|
||||
|
||||
<p>It is possible to find the possible filter criteria dynamically using the
|
||||
<code>generateFilterCriteria()</code> method.</p>
|
||||
|
||||
|
||||
<h3>Date Range Filtering</h3>
|
||||
|
||||
<p>The last option to filter dynamically is using a date range selector. Define the <code>dateRangeSel</code>
|
||||
lookup parameter, and then set the date range on the instantiated report:</p>
|
||||
<pre>
|
||||
<ParameterBag Id="parameters" Name="parameters" Type="Parameters">
|
||||
...
|
||||
<Parameter Id="dateRangeSel" Name="Date Range Selector" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/expirationDate"/>
|
||||
...
|
||||
</ParameterBag>
|
||||
|
||||
Date 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<JsonObject> result = new Report(tx, "stockReport") //
|
||||
.filter("Product", "product01") //
|
||||
.dateRange(dateRange) //
|
||||
.doReportAsJson()
|
||||
</pre>
|
||||
|
||||
<p><b>Note:</b> See the
|
||||
<a href="https://github.com/4treesCH/strolch/blob/develop/li.strolch.service/src/test/java/li/strolch/report/GenericReportTest.java"
|
||||
target="_blank">GenericReportTest</a> for examples.</p>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,395 +0,0 @@
|
|||
<!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: Runtime Configuration</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Runtime</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses the Strolch Runtime configuration.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>A Strolch runtime configuration comprises two parts: a configuration part, and a model part. The
|
||||
configuration are files located in the <code>..config/</code> folder, and the model are files located in the
|
||||
<code>../data</code> folder.</p>
|
||||
|
||||
|
||||
<p>In an absolute minimal configuration, the Strolch runtime requires the following folder structure:</p>
|
||||
<ul>
|
||||
<li><code>../config/</code>
|
||||
<dl>
|
||||
<dt>StrolchConfiguration.xml</dt>
|
||||
<dd>→ configures the Strolch agent</dd>
|
||||
<dt>PrivilegeConfig.xml</dt>
|
||||
<dd>→ configures user management</dd>
|
||||
<dt>PrivilegeUsers.xml</dt>
|
||||
<dd>→ contains the users in an XML based user management file</dd>
|
||||
<dt>PrivilegeRoles.xml</dt>
|
||||
<dd>→ contains the roles and privileges in an XML based user management</dd>
|
||||
</dl>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>StrolchConfiguration.xml</h2>
|
||||
|
||||
<p>The <i>StrolchConfiguration.xml</i> file configures the Strolch agent. The <i>StrolchConfiguration.xml</i>
|
||||
defines the following:</p>
|
||||
<ul>
|
||||
<li><code><StrolchConfiguration></code> root element</li>
|
||||
|
||||
<li><code><env id="xxx"></code> different environments with the possibility of having a global
|
||||
environment for configuration valid in multiple environments.
|
||||
<ul>
|
||||
<li><code><Runtime></code> element which defines the agents name and a few other properties
|
||||
e.g. <code>locale</code> and <code>verbose</code>:
|
||||
<ul>
|
||||
<li><code><applicationName></code> the agent's name</li>
|
||||
|
||||
<li><code><Properties></code></li>
|
||||
|
||||
<li><code><locale></code> the agent's internal locale for log messages etc.</li>
|
||||
<li><code><verbose></code> the logging level for some internal logging. (Logging is
|
||||
mostly done using log4j over slf4j)
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li><code><Component></code> elements for each component used in the agent. A component is configured
|
||||
by defining the following child elements:
|
||||
<ul>
|
||||
<li><code><name></code> 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
|
||||
</li>
|
||||
|
||||
<li><code><api></code> the full class name to the interface of the component. During runtime
|
||||
this interface will be used to access the component e.g.: <code>ServiceHandler
|
||||
svcHandler
|
||||
=
|
||||
agent.getContainer().getComponent(ServiceHandler.class);</code>
|
||||
</li>
|
||||
|
||||
<li><code><impl></code> 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 <code>li.strolch.agent.api.StrolchComponent</code>
|
||||
</li>
|
||||
|
||||
<li><code><depends></code> 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
|
||||
</li>
|
||||
|
||||
<li><code><Properties></code>
|
||||
<ul>
|
||||
<li><code><...></code> any number of properties which the component requires. The
|
||||
element's name will be the key with which the value can be
|
||||
accessed at runtime.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p><b>Note:</b> 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.</p>
|
||||
|
||||
<h2 id="privilegeConfig">Privilege Configuration</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>The <i>PrivilegeConfig.xml</i> defines the following:</p>
|
||||
<ul>
|
||||
<li><code><Privilege></code> root element
|
||||
<ul>
|
||||
<li><code><Container></code> configures the individual Privilege components
|
||||
<ul>
|
||||
<li><code><Parameters></code> base configuration properties for Privilege</li>
|
||||
<li><code><EncryptionHandler></code> configures the hashing algorithms and other
|
||||
encryption specific configuration
|
||||
</li>
|
||||
<li><code><PersistenceHandler></code> configures the persistence of the roles and
|
||||
users
|
||||
</li>
|
||||
<li><code><UserChallengeHandler></code> configures a challenge handler so that a user
|
||||
can reset their password. The default
|
||||
challenge handler is the <code>li.strolch.privilege.handler.MailUserChallengeHandler</code>
|
||||
which sends a challenge to the user's defined
|
||||
e-mail address.
|
||||
</li>
|
||||
<li><code><SsoHandler></code> 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.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code><Policies></code> configures the available privilege policies at runtime, the name
|
||||
is referenced from the model file
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>The <code>PrivilegeUsers.xml</code> and <code>PrivilegeRoles.xml</code> define the users and roles and is
|
||||
used when in <code>PrvilegeConfig.xml</code> the <code>PersistenceHandler</code> is set to <code>ch.eitchnet.privilege.handler.XmlPersistenceHandler</code>:
|
||||
</p>
|
||||
<ul>
|
||||
<li><code><Users></code> configures all users
|
||||
<ul>
|
||||
<li><code><User></code> configures a specific user
|
||||
<ul>
|
||||
<li><code><Firstname></code> configures a user's first name</li>
|
||||
<li><code><Lastname></code> configure a user's last name</li>
|
||||
<li><code><State></code> configures the user's state, see <code>ch.eitchnet.privilege.model.UserState</code>
|
||||
</li>
|
||||
<li><code><Locale></code> configure the user's locale</li>
|
||||
<li><code><Roles></code> configures the user's roles
|
||||
<ul>
|
||||
<li><code><Role></code> adds a role to the user</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code><Properties></code> configures user specific properties. What properties are
|
||||
used is not specified and is dependent on the concrete
|
||||
agent
|
||||
<ul>
|
||||
<li><code><Property></code> defines a single property</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li><code><Roles></code> configures all roles
|
||||
<ul>
|
||||
<li><code><Role></code> configures a specific role
|
||||
<ul>
|
||||
<li><code><Privilege></code> configures a specific privilege for this role
|
||||
<ul>
|
||||
<li><code><AllAllowed></code> if set to true, then defines that all values
|
||||
associated with this privilege are allowed
|
||||
</li>
|
||||
<li><code><Allow></code> defines one allowed value for this privilege
|
||||
</li>
|
||||
<li><code><Deny></code> defines one denied value for this privilege
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Implementing a StrolchComponent</h2>
|
||||
|
||||
<p>Implementing a strolch component requires an interface, which defines the component's API and a concrete
|
||||
class which implements the interface and extends the class <code>StrolchComponent</code>.</p>
|
||||
|
||||
<p>The StrolchComponent class adds the state model to the class, which transitions as follows:<br /> <code>UNDEFINED
|
||||
=>
|
||||
SETUP
|
||||
=>
|
||||
INITIALIZED
|
||||
=>
|
||||
STARTED
|
||||
<=>
|
||||
STOPPED
|
||||
=>
|
||||
DESTROYED</code>
|
||||
</p>
|
||||
|
||||
<p>Components can switch between <code>STARTED</code> and <code>STOPPED</code>, but once <code>DESTROYED</code>
|
||||
no further state change is possible. The component's state is changed by changes to the agent's lifecycle.
|
||||
</p>
|
||||
|
||||
<p>A component'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 <code>initialize()</code>-method is used to get all the
|
||||
configuration properties, and that they should there be evaluated and that the method so return quickly. The
|
||||
<code>start()</code>-method is called after the agent'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.</p>
|
||||
|
||||
<p>The following shows a basic implementation of a component on the basis of a post initializer (a component
|
||||
which performs some actions in its <code>start()</code>-method which should be done after everything else is
|
||||
started in the agent).</p>
|
||||
|
||||
<pre>
|
||||
public 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't worry about
|
||||
// being called to start again now call super, to update state
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The new component would then be registered in the <code>StrolchConfiguration.xml</code> as follows:</p>
|
||||
<pre>
|
||||
<StrolchConfiguration>
|
||||
<env id="...">
|
||||
...
|
||||
<Component>
|
||||
<name>SimplePostInitializer</name>
|
||||
<api>li.strolch.agent.api.PostInitializer</api>
|
||||
<impl>li.strolch.documentation.SimplePostInitializer</impl>
|
||||
</Component>
|
||||
...
|
||||
</env>
|
||||
</StrolchConfiguration>
|
||||
</pre>
|
||||
|
||||
<p>And can be access at runtime using:</p>
|
||||
<pre>
|
||||
PostInitializer postInitializer = getContainer().getComponent(PostInitializer.class);
|
||||
</pre>
|
||||
|
||||
<h2>Starting the agent</h2>
|
||||
|
||||
<p>When a Strolch runtime is started, then the root path to the runtime configuration must be passed. In Java
|
||||
this is done by calling:</p>
|
||||
<pre>
|
||||
StrolchAgent agent = new StrolchAgent();
|
||||
agent.setup(environment, rootPath);
|
||||
agent.initialize();
|
||||
agent.start();
|
||||
</pre>
|
||||
|
||||
<p>In Servlet 3.0 applications one would implement the <code>javax.servlet.ServletContextListener</code>
|
||||
interface, add the <code>@WebListener</code> annotation to the class and in the
|
||||
<code>contextInitialized()</code>-method start Strolch:</p>
|
||||
<pre>
|
||||
String realPath = sce.getServletContext().getRealPath("/WEB-INF");
|
||||
String environment = StrolchEnvironment.getEnvironmentFromEnvProperties(pathF);
|
||||
this.agent = new StrolchAgent();
|
||||
this.agent.setup(environment, new File(realPath));
|
||||
this.agent.initialize();
|
||||
this.agent.start();
|
||||
</pre>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,183 +0,0 @@
|
|||
<!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: Searches</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Searches</h1>
|
||||
<p class="lead page-description">This page discusses Strolch Searches.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>As is custom for every framework, querying, or searching, the model must be possible. Strolch searches are
|
||||
implemented using the <code>StrolchSearch</code> class and one of its concrete implementations: <code>ResourceSearch</code>,
|
||||
<code>OrderSearch</code>, <code>ActivitySearch</code>.</p>
|
||||
|
||||
<p>A Strolch element always has two identifiers: <code>Type</code> and <code>Id</code>. 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
|
||||
<code>Resource</code>, but one of type <code>Car</code> and the other of type <code>House</code>. Both would
|
||||
have different parameters. Thus when searching for objects, the first thing to do is define the type of
|
||||
object being searched.</p>
|
||||
|
||||
<p>The 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 <a href="https://camel.apache.org" target="_blank">Apache Camel</a> project.</p>
|
||||
|
||||
<p>There are four main search classes:</p>
|
||||
<ul>
|
||||
<li><code>RootElementSearch</code> - search for any of <code>Resource</code>, <code>Order</code> or <code>Activity</code>
|
||||
elements
|
||||
</li>
|
||||
<li><code>ResourceSearch</code> - search for <code>Resources</code></li>
|
||||
<li><code>OrderSearch</code> - search for <code>Orders</code></li>
|
||||
<li><code>ActivitySearch</code> - search for <code>Activities</code></li>
|
||||
</ul>
|
||||
|
||||
<p>No search is useful without a <code>where</code> clause, which are called search expressions. When writing a
|
||||
search, there are multiple ways to add such where clauses. Either</p>
|
||||
<ul>
|
||||
<li>override the <code>define()</code> method in your sub class and add the where clauses by calling the
|
||||
<code>where()</code> method, or
|
||||
</li>
|
||||
<li>define special methods on the class e.g. <code>byColor()</code> which also calls the
|
||||
<code>where()</code> method to add a search expression, or
|
||||
</li>
|
||||
<li>directly call the <code>where()</code> method after instantiating a search.</li>
|
||||
</ul>
|
||||
|
||||
<p>When extending the class, then the search expressions are available as methods on the super class, otherwise
|
||||
you can statically import them from
|
||||
<a href="https://github.com/4treesCH/strolch/blob/develop/li.strolch.agent/src/main/java/li/strolch/search/ExpressionsSupport.java"
|
||||
target="_blank">ExpressionsSupport</a>.</p>
|
||||
|
||||
<p>And 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
|
||||
<a href="https://github.com/4treesCH/strolch/blob/develop/li.strolch.agent/src/main/java/li/strolch/search/PredicatesSupport.java"
|
||||
target="_blank">PredicatesSupport</a>.</p>
|
||||
|
||||
<p>Examples of search expressions with search predicates follow:</p>
|
||||
<pre>
|
||||
ResourceSearch search = new ResourceSearch();
|
||||
|
||||
// predicate either as parameter, or chained
|
||||
search.where(id().isEqualTo("myId"));
|
||||
search.where(id(isEqualTo("myId")));
|
||||
|
||||
// negating
|
||||
search.where(id(isEqualTo("myId")).not());
|
||||
|
||||
search.where(param("bagId", "paramId").isIn(Arrays.asList("red", "blue", "green")));
|
||||
|
||||
search.where(paramNull("bagId", "paramId")));
|
||||
|
||||
// boolean operations
|
||||
search.where(id(isEqualTo("myId")) //
|
||||
.or(name(isEqualTo("myName"))));
|
||||
</pre>
|
||||
|
||||
<p>Note how the predicates can be chained to the search expression, or passed as a parameter to the
|
||||
expression.</p>
|
||||
|
||||
<p>In addition to using predefined search search expressions, one can also just pass a lambda expression which
|
||||
performs a custom filter:</p>
|
||||
<pre>
|
||||
personSearch.where(person -> person.getName().length() == 3);</pre>
|
||||
|
||||
<p>See the
|
||||
<a href="https://github.com/4treesCH/strolch/blob/develop/li.strolch.agent/src/test/java/li/strolch/search/StrolchSearchTest.java"
|
||||
target="_blank">StrolchSearchTest</a> for many ways in which you can implement tests.</p>
|
||||
|
||||
<p>Note that strolch searches requires privileges. Thus when you use a strolch search, add it to the role of the
|
||||
user in <code>PrivilegeRoles.xml</code>:</p>
|
||||
<pre>
|
||||
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||
<Allow>internal</Allow> <!-- internal used for when the search is done in an internal service -->
|
||||
<Allow>li.strolch.bookshop.search.BookSearch</Allow>
|
||||
</Privilege></pre>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,213 +0,0 @@
|
|||
<!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: Services and Commands</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Services and Commands</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses Strolch's Services and Commands API</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p><code>Services</code> are written to implement a specific use-case. <code>Commands</code> are written to
|
||||
implemente re-usable parts of a use-case. The use-case can be abstract e.g. <code>AddResourceService</code>
|
||||
or very specific e.g. <code>CreatePatientService</code>.</p>
|
||||
|
||||
<p>Should 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 <code>CreatePatientService</code> would use a <code>CreatePatientResourceCommand</code>
|
||||
and then use an <code>AddResourceCommand</code> in a single transaction, so that the task of creating the
|
||||
actual Patient Resource can be re-used somewhere else.</p>
|
||||
|
||||
<p>Services extend the abstract class <code>AbstractService</code> and then implement the method <code>internalDoService(ServiceArgument)</code>.
|
||||
AbstractService defines generic template arguments with which the concrete service can define a specific
|
||||
input ServiceArgument class and output ServiceResult class.</p>
|
||||
|
||||
<p>The AbstractService class has multiple helper methods:</p>
|
||||
<ul>
|
||||
<li><code>openTx():StrolchTransaction</code> - to open a transaction</li>
|
||||
<li><code>runPrivileged()</code> - to perform a SystemUserAction</li>
|
||||
<li><code>getComponent():V</code> - to retrieve a specific StrolchComponent</li>
|
||||
</ul>
|
||||
<p>there are more - check the JavaDocs</p>
|
||||
|
||||
|
||||
<p>Commands extend the <code>Command</code> class and then implement the method <code>doCommand()</code>.
|
||||
Commands have helper methods:</p>
|
||||
<ul>
|
||||
<li><code>tx()</code> - to get the current transaction</li>
|
||||
<li><code>getPolicy()</code> - to retrieve a policy instance</li>
|
||||
<li><code>runPrivileged()</code> - to perform a SystemUserAction</li>
|
||||
</ul>
|
||||
<p>there are more - check the JavaDocs</p>
|
||||
|
||||
<p>The following code snippets shows how a Service and Command are used to perform the task of adding a new
|
||||
Order. Note how:</p>
|
||||
<ul>
|
||||
<li>the Service opens the transaction</li>
|
||||
<li>adds the command to the TX</li>
|
||||
<li>calls <code>tx.commitOnClose()</code></li>
|
||||
<li>the command validates its input</li>
|
||||
<li>locks the object</li>
|
||||
<li>performs the work</li>
|
||||
<li>and implements an undo</li>
|
||||
</ul>
|
||||
|
||||
<p>AddOrderService:</p>
|
||||
<pre>
|
||||
public class AddOrderService extends AbstractService<AddOrderService.AddOrderArg, ServiceResult> {
|
||||
|
||||
@Override
|
||||
protected ServiceResult getResultInstance() {
|
||||
return new ServiceResult();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServiceResult internalDoService(AddOrderArg arg) {
|
||||
|
||||
try (StrolchTransaction tx = openTx(arg.realm)) {
|
||||
AddOrderCommand command = new AddOrderCommand(tx);
|
||||
command.setOrder(arg.order);
|
||||
tx.addCommand(command);
|
||||
tx.commitOnClose();
|
||||
}
|
||||
|
||||
return ServiceResult.success();
|
||||
}
|
||||
|
||||
public static class AddOrderArg extends ServiceArgument {
|
||||
public Order order;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>AddOrderCommand:</p>
|
||||
<pre>
|
||||
public class AddOrderCommand extends Command {
|
||||
|
||||
private Order order;
|
||||
|
||||
public AddOrderCommand(StrolchTransaction tx) {
|
||||
super(tx);
|
||||
}
|
||||
|
||||
public void setOrder(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
DBC.PRE.assertNotNull("Order may not be null!", 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("The Order {0} already exists!", this.order.getLocator());
|
||||
throw new StrolchException(msg);
|
||||
}
|
||||
|
||||
orderMap.add(tx(), this.order);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo() {
|
||||
if (this.order != null && tx().isRollingBack()) {
|
||||
OrderMap orderMap = tx().getOrderMap();
|
||||
if (orderMap.hasElement(tx(), this.order.getType(), this.order.getId()))
|
||||
orderMap.remove(tx(), this.order);
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,191 +0,0 @@
|
|||
<!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: Transactions</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Transactions</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses Strolch Transactions.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>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 <code>try-with-resources</code> by
|
||||
implementing the <code>AutoCloseable</code> interface. This makes it trivial to understand the scope of a
|
||||
transaction. </p>
|
||||
|
||||
<p>Transactions handle the following:</p>
|
||||
<ul>
|
||||
<li>Opening and closing database connections</li>
|
||||
<li>Releasing locks to strolch elements, if <code>tx.lock(StrolchRootElement)</code> or <code>tx.lock(Locator)</code>
|
||||
was called
|
||||
</li>
|
||||
<li>Performing Commands by executing them in the added order, and validating them first.</li>
|
||||
<li>Exception handling</li>
|
||||
<li>Auditing</li>
|
||||
<li>Updating observers</li>
|
||||
</ul>
|
||||
|
||||
<p>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 <code>tx.commitOnClose()</code>, but
|
||||
only at the end of the work, so that exception handling can properly work if something goes wrong.</p>
|
||||
|
||||
<p><code>StrolchTransaction</code> offers a myriad of methods:</p>
|
||||
<ul>
|
||||
<li>find element by its <code>Locator</code></li>
|
||||
<li>get methods for elements by type and id, or using a <code>StringParameter</code> or <code>StringListParameter</code>
|
||||
references
|
||||
</li>
|
||||
<li>methods to add, update or remove elements</li>
|
||||
<li>assert privilege access</li>
|
||||
<li>get a new element by its template</li>
|
||||
<li>check if an element exists by type and id</li>
|
||||
<li>get streams for elements</li>
|
||||
<li>add commands for execution</li>
|
||||
</ul>
|
||||
|
||||
<p>Transactions are opened by accessing the realm, but there are convenience methods depending on the
|
||||
use-case:</p>
|
||||
<ul>
|
||||
<li>In Services: by calling one of the <code>openTx()</code>-methods</li>
|
||||
<li>In Commands: Transactions are already open, use method <code>tx()</code> to get instance. <b>Note:</b>
|
||||
don't open a new TX inside a TX for the same realm!
|
||||
</li>
|
||||
<li>REST API: <code>RestfulStrolchComponent.openTx()</code></li>
|
||||
</ul>
|
||||
|
||||
<p>Important is to always open the transaction as a try-with-resource block and to define if the TX should
|
||||
commit, or not:</p>
|
||||
<pre>
|
||||
try (StrolchTransaction tx = openTx(...)) {
|
||||
|
||||
// read lock our object
|
||||
Locator ferrariLoc = Resource.locatorFor("Car", "ferrari");
|
||||
tx.lock(ferrariLoc);
|
||||
|
||||
// find a car by locator
|
||||
Resource ferrari = tx.findElement(ferrariLoc);
|
||||
|
||||
// get a car by ID
|
||||
Resource opel = tx.getResourceBy("Car", "opel", true);
|
||||
|
||||
// modify ball
|
||||
opel.setName("Opel Corsa");
|
||||
tx.update(opel);
|
||||
|
||||
// get by string reference
|
||||
StringParameter ownerP = ferrari.getParameter("relations", "owner", true);
|
||||
Resource owner = tx.getResourceBy(ownerP, true);
|
||||
|
||||
// get by string list reference
|
||||
StringListParameter previousOwnersP = opel.getParameter("relations", "previousOwners", true);
|
||||
List<Resource> previousOwners = tx.getResourcesBy(previousOwnersP, true);
|
||||
|
||||
// check resource exists
|
||||
if (tx.hasResource("Car", "audi")) {
|
||||
Resource audi = tx.getResourceBy("Car", "audi", true);
|
||||
|
||||
// assert has privilege to remove a car
|
||||
tx.assertHasPrivilege(Operation.REMOVE, audi);
|
||||
|
||||
// remove the car
|
||||
tx.remove(audi);
|
||||
}
|
||||
|
||||
// iterate all cars
|
||||
tx.streamResources("Car").forEach(car -> {
|
||||
logger.info("Car: " + car.getId());
|
||||
});
|
||||
|
||||
// commit if TX was changed
|
||||
if (tx.needsCommit())
|
||||
tx.commitOnClose();
|
||||
}</pre>
|
||||
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,149 +0,0 @@
|
|||
<!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: Versioning</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation: Versioning</h1>
|
||||
|
||||
<p class="lead page-description">This page discusses the Strolch Versioning</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<p>One of Strolch's features that sets it apart from other frameworks, is that versioning is baked into
|
||||
Strolch'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.</p>
|
||||
|
||||
<p>The feature is enabled for each realm. In the <code>StrolchConfiguration.xml</code> file enable it by adding
|
||||
the <code>enableVersioning</code> propery per realm:</p>
|
||||
<pre>
|
||||
<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>
|
||||
<enableVersioning>true</enableVersioning>
|
||||
<dataStoreMode>TRANSIENT</dataStoreMode>
|
||||
<dataStoreFile>StrolchModel.xml</dataStoreFile>
|
||||
<enableVersioning.otherRealm>true</enableVersioning.otherRealm>
|
||||
<dataStoreMode.otherRealm>TRANSIENT</dataStoreMode.otherRealm>
|
||||
<dataStoreFile.otherRealm>StrolchModel.xml</dataStoreFile.otherRealm>
|
||||
</Properties>
|
||||
</Component>
|
||||
</env>
|
||||
...
|
||||
</StrolchConfiguration></pre>
|
||||
|
||||
<br />
|
||||
<p>Once versioning is enabled, versioning is handled automatically. The API for versioning is implemented on the
|
||||
ElementMaps.</p>
|
||||
|
||||
<p>Example: Revert to previous version of a Resource:</p>
|
||||
<pre>
|
||||
Resource res = tx.getResourceBy("TestType", "MyTestResource");
|
||||
ResourceMap resourceMap = tx.getResourceMap();
|
||||
Resource previousVersion = resourceMap.revertToVersion(tx, res);
|
||||
// or
|
||||
Resource previousVersion = resourceMap.revertToVersion(tx, "TestType", "MyTestResource", 1);</pre>
|
||||
|
||||
<p>Example: Retrieve all versions of a Resource:</p>
|
||||
<pre>
|
||||
List<Resource> versions = resourceMap.getVersionsFor(tx, "TestType", "MyTestResource");</pre>
|
||||
|
||||
<br />
|
||||
<p><b>Note:</b> 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.</p>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,121 +0,0 @@
|
|||
<!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: Documentation</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><a href="api.html">API</a></li>
|
||||
<li class="active"><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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">Documentation</h1>
|
||||
|
||||
<p class="lead page-description">This is the index page to the documentation of Strolch.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>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.</p>
|
||||
|
||||
<p>Currently we have the following topics of discussion:</p>
|
||||
<ul>
|
||||
<li><a href="documentation-architecture.html">Strolch Architecture</a></li>
|
||||
<li><a href="documentation-model.html">Strolch Model</a></li>
|
||||
<li><a href="documentation-do-and-dont.html">Strolch Do and Don’t</a></li>
|
||||
<li><a href="documentation-runtime.html">Strolch Runtime Configuration</a></li>
|
||||
<li><a href="documentation-realms.html">Strolch Realms</a></li>
|
||||
<li><a href="documentation-components.html">Strolch Components</a></li>
|
||||
<li><a href="documentation-services-and-commands.html">Strolch Services and Commands</a></li>
|
||||
<li><a href="documentation-searches.html">Strolch Searches</a></li>
|
||||
<li><a href="documentation-queries.html">Strolch Queries</a></li>
|
||||
<li><a href="documentation-transactions.html">Strolch Transactions</a></li>
|
||||
<li><a href="documentation-policies.html">Strolch Policies</a></li>
|
||||
<li><a href="documentation-observers.html">Strolch Observers</a></li>
|
||||
<li><a href="documentation-versioning.html">Strolch Versioning</a></li>
|
||||
<li><a href="documentation-reports.html">Strolch Reports</a></li>
|
||||
<li><a href="documentation-privileges.html">Strolch Privileges</a></li>
|
||||
<!--
|
||||
<li><a href="documentation-audits.html">Strolch Audits</a></li>
|
||||
-->
|
||||
</ul>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,114 +0,0 @@
|
|||
<!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: Downloads</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Custom styles for this template -->
|
||||
<link href="css/custom.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="css/downloads.css" type="text/css" />
|
||||
|
||||
<!-- 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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</a></li>
|
||||
<li><a href="tutorial.html">Tutorial</a></li>
|
||||
<li class="active"><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">Downloads</h1>
|
||||
|
||||
<p class="lead page-description">This page describes where you can get Strolch.</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
||||
<p>Strolch is on <a href="https://mvnrepository.com/artifact/li.strolch/li.strolch.agent">Maven central</a>, but if the
|
||||
latest version is not there, then build it locally. A guide can be found on the
|
||||
<a href="development.html">development</a> page. </p>
|
||||
|
||||
<p>Strolch is also built on <a href="https://ci.4trees.ch/">Jenkins</a>, so you can see if the latest
|
||||
version passes all tests.</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div class="row"> </div>
|
||||
|
||||
<!-- .footer -->
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
||||
<!-- /.footer -->
|
||||
|
||||
</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>
|
|
@ -1,229 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
|
||||
<font-face units-per-em="1200" ascent="960" descent="-240" />
|
||||
<missing-glyph horiz-adv-x="500" />
|
||||
<glyph />
|
||||
<glyph />
|
||||
<glyph unicode="
" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
|
||||
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="434" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="163" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="72" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode="€" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
|
||||
<glyph unicode="−" d="M200 400h900v300h-900v-300z" />
|
||||
<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" />
|
||||
<glyph unicode="☁" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
|
||||
<glyph unicode="✉" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
|
||||
<glyph unicode="✏" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
|
||||
<glyph unicode="" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
|
||||
<glyph unicode="" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
|
||||
<glyph unicode="" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
|
||||
<glyph unicode="" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
|
||||
<glyph unicode="" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
|
||||
<glyph unicode="" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
|
||||
<glyph unicode="" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
|
||||
<glyph unicode="" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
|
||||
<glyph unicode="" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
|
||||
<glyph unicode="" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
|
||||
<glyph unicode="" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
|
||||
<glyph unicode="" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
|
||||
<glyph unicode="" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
|
||||
<glyph unicode="" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
|
||||
<glyph unicode="" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
|
||||
<glyph unicode="" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
|
||||
<glyph unicode="" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
|
||||
<glyph unicode="" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
|
||||
<glyph unicode="" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
|
||||
<glyph unicode="" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
|
||||
<glyph unicode="" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
|
||||
<glyph unicode="" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
|
||||
<glyph unicode="" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
|
||||
<glyph unicode="" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
|
||||
<glyph unicode="" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
|
||||
<glyph unicode="" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
|
||||
<glyph unicode="" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
|
||||
<glyph unicode="" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
|
||||
<glyph unicode="" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
|
||||
<glyph unicode="" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
|
||||
<glyph unicode="" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
|
||||
<glyph unicode="" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
|
||||
<glyph unicode="" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
|
||||
<glyph unicode="" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
|
||||
<glyph unicode="" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
|
||||
<glyph unicode="" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
|
||||
<glyph unicode="" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
|
||||
<glyph unicode="" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
|
||||
<glyph unicode="" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
|
||||
<glyph unicode="" d="M200 0l900 550l-900 550v-1100z" />
|
||||
<glyph unicode="" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
|
||||
<glyph unicode="" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
|
||||
<glyph unicode="" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
|
||||
<glyph unicode="" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
|
||||
<glyph unicode="" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
|
||||
<glyph unicode="" d="M0 547l600 453v-300h600v-300h-600v-301z" />
|
||||
<glyph unicode="" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
|
||||
<glyph unicode="" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
|
||||
<glyph unicode="" d="M104 600h296v600h300v-600h298l-449 -600z" />
|
||||
<glyph unicode="" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
|
||||
<glyph unicode="" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
|
||||
<glyph unicode="" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
|
||||
<glyph unicode="" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
|
||||
<glyph unicode="" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
|
||||
<glyph unicode="" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
|
||||
<glyph unicode="" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
|
||||
<glyph unicode="" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
|
||||
<glyph unicode="" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
|
||||
<glyph unicode="" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
|
||||
<glyph unicode="" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
|
||||
<glyph unicode="" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
|
||||
<glyph unicode="" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
|
||||
<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
|
||||
<glyph unicode="" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
|
||||
<glyph unicode="" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
|
||||
<glyph unicode="" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
|
||||
<glyph unicode="" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
|
||||
<glyph unicode="" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
|
||||
<glyph unicode="" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
|
||||
<glyph unicode="" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
|
||||
<glyph unicode="" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
|
||||
<glyph unicode="" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
|
||||
<glyph unicode="" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
|
||||
<glyph unicode="" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
|
||||
<glyph unicode="" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
|
||||
<glyph unicode="" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
|
||||
<glyph unicode="" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
|
||||
<glyph unicode="" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
|
||||
<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
|
||||
<glyph unicode="" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
|
||||
<glyph unicode="" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
|
||||
<glyph unicode="" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
|
||||
<glyph unicode="" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
|
||||
<glyph unicode="" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
|
||||
<glyph unicode="" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
|
||||
<glyph unicode="" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
|
||||
<glyph unicode="" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
|
||||
<glyph unicode="" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
|
||||
<glyph unicode="" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
|
||||
<glyph unicode="" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
|
||||
<glyph unicode="" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
|
||||
<glyph unicode="" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
|
||||
<glyph unicode="" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
|
||||
<glyph unicode="" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
|
||||
</font>
|
||||
</defs></svg>
|
Before Width: | Height: | Size: 62 KiB |
|
@ -1 +0,0 @@
|
|||
google-site-verification: google052dbec2d053a4e1.html
|
|
@ -1,265 +0,0 @@
|
|||
<!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: Overview</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 class="active"><a href="index.html">Overview</a></li>
|
||||
<li><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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 Overview</h1>
|
||||
|
||||
<p class="lead page-description">This page describes the Strolch software agent and the motivation behind its
|
||||
development.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<h2>Overview</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>Strolch 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.</p>
|
||||
|
||||
<p>The 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.</p>
|
||||
|
||||
<p>One 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.</p>
|
||||
|
||||
<p>The 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.</p>
|
||||
|
||||
<p>No 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.</p>
|
||||
|
||||
<h2>Motivation</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>The 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.</p>
|
||||
|
||||
<p>Strolch tries a different approach to persistence. Instead of writing pojos/entities, Strolch's model has the
|
||||
concept that each element'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's ID.</p>
|
||||
|
||||
<p>Assigning 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.</p>
|
||||
|
||||
<p>Since 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.</p>
|
||||
|
||||
<p>There 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.</p>
|
||||
|
||||
<p>Currently there is at least one company using Strolch in a commercial project which helps drive Strolch's
|
||||
development and further motivates its existence.</p>
|
||||
|
||||
<p>Strolch is an open source project and licensed under the Apache License 2.0.</p>
|
||||
|
||||
<h2>Technologoy</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h3>Dependencies</h3>
|
||||
|
||||
<p>Strolch strives to use as few external dependencies as possible, so that the Strolch runtime is not bloated
|
||||
unnecessarily. The following list of Strolch dependencies is a summary and was created using mvn
|
||||
dependency:tree on the strolch_minimal project for release 1.6.47.</p>
|
||||
|
||||
<p>Logging</p>
|
||||
<ul>
|
||||
<li>org.slf4j:slf4j-api:jar:1.7.25</li>
|
||||
<li>ch.qos.logback:logback-classic:jar:1.2.3</li>
|
||||
<li>ch.qos.logback:logback-core:jar:1.2.3</li>
|
||||
</ul>
|
||||
|
||||
<p>Utils</p>
|
||||
<ul>
|
||||
<li>javax.mail:javax.mail-api:jar:1.6.0</li>
|
||||
<li>com.sun.mail:javax.mail:jar:1.6.0</li>
|
||||
<li>javax.activation:activation:jar:1.1</li>
|
||||
</ul>
|
||||
|
||||
<p>Testing</p>
|
||||
<ul>
|
||||
<li>junit:junit:jar:4.12</li>
|
||||
<li>org.hamcrest:hamcrest-core:jar:1.3</li>
|
||||
<li>org.hamcrest:hamcrest-library:jar:1.3</li>
|
||||
<li>org.mockito:mockito-core:jar:2.0.8-beta</li>
|
||||
<li>org.objenesis:objenesis:jar:2.1</li>
|
||||
</ul>
|
||||
|
||||
<p>Model</p>
|
||||
<ul>
|
||||
<li>com.google.code.gson:gson:jar:2.8.2</li>
|
||||
</ul>
|
||||
|
||||
<p>SOQL</p>
|
||||
<ul>
|
||||
<li>org.abego.treelayout:org.abego.treelayout.core:jar:1.0.3</li>
|
||||
<li>org.antlr:antlr-runtime:jar:3.5.2</li>
|
||||
<li>org.antlr:antlr4-runtime:jar:4.7</li>
|
||||
<li>org.antlr:antlr4:jar:4.7</li>
|
||||
<li>org.antlr:ST4:jar:4.0.8</li>
|
||||
<li>org.glassfish:javax.json:jar:1.0.4</li>
|
||||
<li>com.ibm.icu:icu4j:jar:58.2</li>
|
||||
</ul>
|
||||
|
||||
<p>PostgreSQL</p>
|
||||
<ul>
|
||||
<li>com.zaxxer:HikariCP:jar:2.7.1</li>
|
||||
<li>org.postgresql:postgresql:jar:42.1.4</li>
|
||||
</ul>
|
||||
|
||||
<p>REST</p>
|
||||
<ul>
|
||||
<li>javax.annotation:javax.annotation-api:jar:1.3.1</li>
|
||||
<li>javax.servlet:javax.servlet-api:jar:3.1.0</li>
|
||||
<li>javax.ws.rs:javax.ws.rs-api:jar:2.1</li>
|
||||
</ul>
|
||||
|
||||
<p>REST testing</p>
|
||||
<ul>
|
||||
<li>javax.validation:validation-api:jar:1.1.0.Final</li>
|
||||
<li>org.glassfish.grizzly:grizzly-framework:jar:2.3.28</li>
|
||||
<li>org.glassfish.grizzly:grizzly-http-server:jar:2.3.28</li>
|
||||
<li>org.glassfish.grizzly:grizzly-http-servlet:jar:2.3.28</li>
|
||||
<li>org.glassfish.grizzly:grizzly-http:jar:2.3.28</li>
|
||||
<li>org.glassfish.hk2:hk2-api:jar:2.5.0-b32</li>
|
||||
<li>org.glassfish.hk2:hk2-locator:jar:2.5.0-b32</li>
|
||||
<li>org.glassfish.hk2:hk2-utils:jar:2.5.0-b32</li>
|
||||
<li>org.glassfish.hk2:osgi-resource-locator:jar:1.0.1</li>
|
||||
<li>org.glassfish.hk2.external:aopalliance-repackaged:jar:2.5.0-b32</li>
|
||||
<li>org.glassfish.hk2.external:javax.inject:jar:2.5.0-b32</li>
|
||||
<li>org.glassfish.jersey.bundles.repackaged:jersey-guava:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.containers:jersey-container-grizzly2-http:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.containers:jersey-container-grizzly2-servlet:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.containers:jersey-container-servlet-core:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.containers:jersey-container-servlet:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.core:jersey-client:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.core:jersey-common:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.core:jersey-server:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.media:jersey-media-jaxb:jar:2.25.1</li>
|
||||
<li>org.glassfish.jersey.test-framework:jersey-test-framework-core:jar:2.25.1</li>
|
||||
<li>org.javassist:javassist:jar:3.20.0-GA</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h4>API</h4>
|
||||
|
||||
<p>Check out the <a href="api.html">API page</a> to see how to use Strolch.</p>
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 20 KiB |
|
@ -1,176 +0,0 @@
|
|||
<!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: Overview</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 class="active"><a href="index.html">Overview</a></li>
|
||||
<li><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</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 Overview</h1>
|
||||
|
||||
<p class="lead page-description">A short introduction to Strolch.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<h2>Strolch in short</h2>
|
||||
|
||||
<p>Strolch is framework for developing Software. It's main features are:</p>
|
||||
<ul>
|
||||
<li>Complete persisted <a href="documentation-model.html">data model</a>:
|
||||
<ul>
|
||||
<li>Parameters and values by time</li>
|
||||
<li>Resources, Orders with arbitrary parameter grouping</li>
|
||||
<li>Activity/Action hierarchy with arbitrary depth</li>
|
||||
<li>Policies for delegation</li>
|
||||
<li>JSON as well as XML transformation</li>
|
||||
<li>Locator API</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="documentation-transactions.html">Transactions with pessimistic locking and optional
|
||||
read-locking</a></li>
|
||||
<li><a href="documentation-searches.html">Search API</a></li>
|
||||
<li>Component based</li>
|
||||
<li><a href="documentation-privileges.html">Deeply integrated privilege handling</a></li>
|
||||
<li>Fully in-memory</li>
|
||||
<li>Persisted auditing, versioning, operations log</li>
|
||||
<li>DAOs for file system or PostgreSQL, easily extended</li>
|
||||
<li>Execution framework</li>
|
||||
<li><a href="documentation-services-and-commands.html">Service / Command oriented</a></li>
|
||||
<li><a href="documentation-reports.html">Reporting API configured by Resource objects</a></li>
|
||||
<li>REST API for data access</li>
|
||||
<li>WebComponents UI for
|
||||
<ul>
|
||||
<li>Inspector</li>
|
||||
<li>Users</li>
|
||||
<li>Roles</li>
|
||||
<li>Operations Log</li>
|
||||
<li>Login Screen</li>
|
||||
<li>Jobs</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>runs on plain old Java SE</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>Strolch Intro</h2>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<h2>API</h2>
|
||||
|
||||
<p>Check out the <a href="api.html">API page</a> to see how to use Strolch.</p>
|
||||
|
||||
<a href="history.html">More to motivation etc.</a>
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,617 +0,0 @@
|
|||
<!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: PLC</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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li class="active"><a href="plc.html">PLC</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 as a software PLC</h1>
|
||||
|
||||
<p class="lead page-description">A soft real time PLC written in Strolch running on Strolch</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
<h2>Overview</h2>
|
||||
|
||||
<p>Using Strolch as a PLC has certain advantages and disadvantages. The following is a list of advantages:</p>
|
||||
<ul>
|
||||
<li>Same programming model and language for server and PLC</li>
|
||||
<li>PLC has the same privilege handling as in Strolch</li>
|
||||
<li>Simulating down to the PLC level is easily possible for easier testing of server logic</li>
|
||||
</ul>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>Checkout the code at <a href="https://github.com/4treesCH/strolch-plc">GitHub</a></p>
|
||||
|
||||
<h2>Architecture</h2>
|
||||
<h3>Overview</h3>
|
||||
<img class="image" src="images/Strolch-PLC-Architecture-Overview.png" alt="Strolch PLC Architecture Overview">
|
||||
<p>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.</p>
|
||||
|
||||
<h3>PLC Architecture</h3>
|
||||
<img class="image" src="images/Strolch-PLC-Architecture.png" alt="Strolch PLC Architecture">
|
||||
<p>On the agent side the two main classes are the <code>PlcGwServerHandler</code> and the
|
||||
<code>PlcGwService</code></p>
|
||||
<p>The <code>PlcGwServerHandler</code> handles connections from remote PLCs over WebSockets and sends the
|
||||
requests to these PLCs. A <code>PlcGwService</code> instance will be notified and can then decide on an
|
||||
action. In an execution model with <code>Activities</code>, the <code>PlcNotificationListener</code>
|
||||
interface can be implemented, or the <code>PlcExecutionPolicy</code> can be directly extended.</p>
|
||||
|
||||
<p>On the PLC side, the <code>PlcGwClientHandler</code> is optional if no agent is required. The <code>PlcHandler</code>
|
||||
initializes the model and connections. The <code>Plc</code> class is Strolch agnostic and manages the
|
||||
connections and notifies <code>PlcListener</code> instances on changes coming from the underlying
|
||||
connections. The <code>PlcService</code> implementations implement business logic, and can also be notified
|
||||
on updates from connections.</p>
|
||||
|
||||
<h2>Example set up</h2>
|
||||
<p>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.</p>
|
||||
<p>Further at each conveyor location is a barcode reader to read the ID of a container.</p>
|
||||
<p>The 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.</p>
|
||||
|
||||
<img class="image" src="images/Strolch-Plc-Example.png" alt="Strolch PLC Conveyor Example" />
|
||||
|
||||
<h2>New Project</h2>
|
||||
<ol>
|
||||
<li>First create a new Strolch Web project using the <a href="development.html">Strolch Maven archetype</a>
|
||||
</li>
|
||||
<li>Now add the following Maven dependencies:
|
||||
<pre>
|
||||
<properties>
|
||||
<strolch.version>1.6.0-SNAPSHOT</strolch.version>
|
||||
<strolch.plc.version>0.1.0-SNAPSHOT</strolch.plc.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.bom</artifactId>
|
||||
<type>pom</type>
|
||||
<version>${strolch.version}</version>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-bom</artifactId>
|
||||
<type>pom</type>
|
||||
<version>${strolch.plc.version}</version>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- PLC -->
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-plc-gw-client</artifactId>
|
||||
</dependency>
|
||||
</dependencies></pre>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Add a bower dependency: <code>"strolch-wc-plc": "strolch-li/strolch-wc-plc#^0.3.4"</code> to <code>src/main/webapp/bower.json</code>
|
||||
</p>
|
||||
<p>After adding the dependeny, run <code>gulp</code> in the webapp directory. Gulp should have been
|
||||
installed through the instructions from the <a href="development.html">development page</a>.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Now we need to add the PLC web views to our new project. This is added in the <code>src/main/webapp/app/src/c-app.html</code>
|
||||
file. Add the following:</p>
|
||||
|
||||
<pre>
|
||||
<!-- HTML Imports -->
|
||||
<link rel="import" href="../bower_components/strolch-wc-plc/strolch-wc-plc-connections.html">
|
||||
<link rel="import" href="../bower_components/strolch-wc-plc/strolch-wc-plc-logical-devices.html">
|
||||
|
||||
<!-- Change default-page to plcLogicalDevices -->
|
||||
<c-app-routing id="appRouting"
|
||||
login-page="login"
|
||||
default-page="plcLogicalDevices"
|
||||
auth-valid="[[authTokenValid]]"
|
||||
page="{{page}}"
|
||||
route-tail="{{routeTail}}"
|
||||
use-hash-as-path></c-app-routing>
|
||||
|
||||
<!-- Add the new pages in the iron-pages element: -->
|
||||
<template is="dom-if" if="[[equal(page, 'plcConnections')]]" restamp>
|
||||
<strolch-wc-plc-connections id="plcConnections"
|
||||
base-path="../"
|
||||
base-rest-path="[[baseRestPath]]"
|
||||
route="{{subroute}}"></strolch-wc-plc-connections>
|
||||
</template>
|
||||
<template is="dom-if" if="[[equal(page, 'plcLogicalDevices')]]" restamp>
|
||||
<strolch-wc-plc-logical-devices id="plcLogicalDevices"
|
||||
base-path="../"
|
||||
base-rest-path="[[baseRestPath]]"
|
||||
base-ws-path="[[baseWsPath]]"
|
||||
route="{{subroute}}"></strolch-wc-plc-logical-devices>
|
||||
</template>
|
||||
|
||||
// add a new property to the WebSocket path for observing changes on the PLC
|
||||
wsObserverPath: {
|
||||
type: String,
|
||||
value: function () {
|
||||
return CustomWeb.baseWsPath + "/plc/observer";
|
||||
}
|
||||
}</pre>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Don't forget to add the PLC Rest classes to your <code>ResourceConfig</code></p>
|
||||
<pre>
|
||||
@ApplicationPath("rest")
|
||||
public class RestfulApplication extends ResourceConfig {
|
||||
|
||||
public RestfulApplication() {
|
||||
|
||||
...
|
||||
|
||||
// strolch plc services
|
||||
packages(PlcConnectionsResource.class.getPackage().getName());
|
||||
|
||||
...
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Now we need to configure the PLC's runtime by modifying <code>runtime/StrolchConfiguration.xml</code>
|
||||
and adding the following:</p>
|
||||
<pre>
|
||||
<!--
|
||||
This component configures the PlcHandler by
|
||||
loading the PlcConnections, PlcAddresses and PlcTelegrams
|
||||
-->
|
||||
<Component>
|
||||
<name>PlcHandler</name>
|
||||
<api>li.strolch.plc.core.PlcHandler</api>
|
||||
<impl>li.strolch.plc.core.DefaultPlcHandler</impl>
|
||||
<depends>RealmHandler</depends>
|
||||
<Properties>
|
||||
<!-- The component handling the low level connections -->
|
||||
<plcClass>li.strolch.plc.core.hw.DefaultPlc</plcClass>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<!--
|
||||
This component handles registrations of the PlcServices, i.e. your PLC business logic
|
||||
-->
|
||||
<Component>
|
||||
<name>PlcServiceInitializer</name>
|
||||
<api>li.strolch.plc.core.PlcServiceInitializer</api>
|
||||
<impl>li.strolch.plc.example.CustomPlcServiceInitializer</impl>
|
||||
<depends>PlcHandler</depends>
|
||||
<Properties>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
<Component>
|
||||
<name>PlcGwClientHandler</name>
|
||||
<api>li.strolch.plc.gw.client.PlcGwClientHandler</api>
|
||||
<impl>li.strolch.plc.gw.client.PlcGwClientHandler</impl>
|
||||
<depends>PlcHandler</depends>
|
||||
<depends>PlcServiceInitializer</depends>
|
||||
<Properties>
|
||||
<plcId>plc-01</plcId>
|
||||
<gwUsername>plc-01</gwUsername>
|
||||
<gwPassword>plc-01</gwPassword>
|
||||
<gwServerUrl>ws://localhost:8080/agent/websocket/strolch/plc</gwServerUrl>
|
||||
</Properties>
|
||||
</Component></pre>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Now we add the custom classes we just declared.</p>
|
||||
|
||||
<p><b>PlcServiceInitializer</b></p>
|
||||
<pre>
|
||||
import 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<PlcService> getPlcServices(PlcHandler plcHandler) {
|
||||
ArrayList<PlcService> plcServices = new ArrayList<>();
|
||||
|
||||
StartupPlcService startupPlcService = new StartupPlcService(plcHandler);
|
||||
ConveyorPlcService conveyorPlcService = new ConveyorPlcService(plcHandler);
|
||||
|
||||
plcServices.add(conveyorPlcService);
|
||||
plcServices.add(startupPlcService);
|
||||
|
||||
return plcServices;
|
||||
}
|
||||
}</pre>
|
||||
|
||||
<p><b>PlcPostInitializer</b></p>
|
||||
<pre>
|
||||
import 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
|
||||
}</pre>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>In the <code>CustomPlcServiceInitializer</code> we added two PlcServices, for which the code is
|
||||
missing. The following are simple examples:</p>
|
||||
|
||||
<p><b>StartupPlcService</b></p>
|
||||
<pre>
|
||||
import 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 = "PLC";
|
||||
public static final String STARTED = "Started";
|
||||
public static final String STOPPED = "Stopped";
|
||||
|
||||
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();
|
||||
}
|
||||
}</pre>
|
||||
|
||||
<p><b>ConveyorPlcService</b></p>
|
||||
<pre>
|
||||
import 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 = "Conveyor01";
|
||||
private static final String A_START_BUTTON = "StartButton";
|
||||
private static final String T_MOTOR_ON = "MotorOn";
|
||||
private static final String T_MOTOR_OFF = "MotorOff";
|
||||
private static final String A_BOX_DETECTED = "BoxDetected";
|
||||
|
||||
private boolean motorOn;
|
||||
private ScheduledFuture<?> 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("Conveyor01"))
|
||||
throw new IllegalStateException("Unexpected resource " + resource);
|
||||
|
||||
boolean active = (boolean) value;
|
||||
|
||||
if (action.equals(A_START_BUTTON)) {
|
||||
|
||||
if (active) {
|
||||
logger.info("Start button pressed. Starting motors...");
|
||||
send(R_CONVEYOR_01, T_MOTOR_ON);
|
||||
this.motorOn = true;
|
||||
scheduleStopTask();
|
||||
}
|
||||
|
||||
} else if (action.equals(A_BOX_DETECTED)) {
|
||||
|
||||
if (active && this.motorOn) {
|
||||
logger.info("Container detected, refreshing stop task...");
|
||||
scheduleStopTask();
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.info("Unhandled notification " + 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();
|
||||
}
|
||||
}</pre>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>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 <code>Resources</code></p>
|
||||
<p>In 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 <code>PlcAddressGenerator</code> to generate and validate the model.</p>
|
||||
<p>For this purpose in this example, we will use one conveyor with 2 inputs and 1 output. The CSV file
|
||||
should have the following content:</p>
|
||||
<pre>
|
||||
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</pre>
|
||||
|
||||
<p>The CSV headers are as follows:</p>
|
||||
<ul>
|
||||
<li>Description → a simple description for this PlcAddress</li>
|
||||
<li>Type →
|
||||
<ul>
|
||||
<li>Group → Must be the first line and generates a PlcLogicalDevice, all succeeding
|
||||
lines are grouped to this device. Add additional to group further devices
|
||||
</li>
|
||||
<li>Input → defines a boolean input</li>
|
||||
<li>Output → defines a boolean output</li>
|
||||
<li>Virtual → defines a virtual address which has no corresponding hardware connection.
|
||||
Used for internal communication.
|
||||
</li>
|
||||
<li>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.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>SubType →
|
||||
<ul>
|
||||
<li>For Input and Output types →
|
||||
<ul>
|
||||
<li>DevPin, DevPin0 → Generates the address as <code><Connection>.<Device>.<Pin></code>.
|
||||
DevPin0 decrements the Device and Pin values by one.
|
||||
</li>
|
||||
<li>Pin → Generates the address as <code><Connection>.<Pin></code>.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>For Virtual types →
|
||||
<ul>
|
||||
<li>Boolean</li>
|
||||
<li>String</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Device → Device number</li>
|
||||
<li>Pin → The pin number on the device</li>
|
||||
<li>Resource → The resource ID with which to notify the agent</li>
|
||||
<li>Action1 → The action ID</li>
|
||||
<li>Action2 → The second action ID if required</li>
|
||||
<li>Connection → The ID of the PlcConnection with which this I/O is attached</li>
|
||||
<li>DeviceId → For type Group: Set the ID of this PlcLogicalDevice being generated</li>
|
||||
</ul>
|
||||
|
||||
<p>When you use this file as input for the <code>PlcAddressGenerator</code>, then it will generate
|
||||
PlcLogicalDevice, PlcAddress and PlcTelegram elements:</p>
|
||||
<pre>
|
||||
<Resource Id="D_MaterialFlow" Name="MaterialFlow" Type="PlcLogicalDevice">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="description" Name="Description" Type="String" Value="Material Flow"/>
|
||||
<Parameter Id="group" Name="Group" Type="String" Value="01 Material Flow"/>
|
||||
<Parameter Id="index" Name="Index" Type="Integer" Value="10"/>
|
||||
</ParameterBag>
|
||||
<ParameterBag Id="relations" Name="Relations" Type="Relations">
|
||||
<Parameter Id="addresses" Name="Addresses" Type="StringList" Interpretation="Resource-Ref" Uom="PlcAddress" Value="A_Conveyor-Occupied, A_Conveyor-BoxDetected, A_Conveyor-MotorOn"/>
|
||||
<Parameter Id="telegrams" Name="Telegrams" Type="StringList" Interpretation="Resource-Ref" Uom="PlcTelegram" Value="T_Conveyor-MotorOn, T_Conveyor-MotorOff"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
|
||||
<Resource Id="A_Conveyor-Occupied" Name="Conveyor - Occupied" Type="PlcAddress">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="description" Name="Description" Type="String" Index="5" Value="Conveyor 1"/>
|
||||
<Parameter Id="address" Name="HW Address" Type="String" Interpretation="PlcConnection" Index="10" Value="raspiBcmGpioInput.4"/>
|
||||
<Parameter Id="resource" Name="Resource ID for PlcAddress" Type="String" Index="20" Value="Conveyor"/>
|
||||
<Parameter Id="action" Name="Action ID for PlcAddress" Type="String" Index="30" Value="Occupied"/>
|
||||
<Parameter Id="index" Name="Index" Type="Integer" Index="40" Value="10"/>
|
||||
<Parameter Id="value" Name="Value" Type="Boolean" Index="100" Value="false"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
|
||||
<Resource Id="T_Conveyor-MotorOn" Name="Conveyor - MotorOn" Type="PlcTelegram">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="description" Name="Description" Type="String" Index="5" Value="Conveyor 1"/>
|
||||
<Parameter Id="address" Name="HW Address" Type="String" Interpretation="PlcConnection" Index="10" Value="raspiBcmGpioOutput.18"/>
|
||||
<Parameter Id="resource" Name="Resource ID for PlcAddress" Type="String" Index="20" Value="Conveyor"/>
|
||||
<Parameter Id="action" Name="Action ID for PlcAddress" Type="String" Index="30" Value="MotorOn"/>
|
||||
<Parameter Id="index" Name="Index" Type="Integer" Index="40" Value="10"/>
|
||||
<Parameter Id="value" Name="Value" Type="Boolean" Index="100" Value="true"/>
|
||||
</ParameterBag>
|
||||
</Resource></pre>
|
||||
|
||||
<p>The PlcLogicalDevice references the PlcAddress and PlcTelegram objects, and is then used in the UI
|
||||
for grouping.</p>
|
||||
<p>The PlcAddress is used to store the current value and defines the keys with which the agent will be
|
||||
notified</p>
|
||||
<p>The PlcTelegram is used to store default values to send, for specific keys. E.g. The action
|
||||
<code>On</code> would send true, and <code>Off</code> would send false. This is semantics, and is
|
||||
defined in each project depending on the hardware.</p>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<p>Copy the file <a href="https://github.com/4treesCH/strolch-plc/blob/develop/example/plc-state.xml">plc-state.xml</a>
|
||||
to your runtime and reference it by use of a <code><IncludeFile file="plc-state.xml" /></code>
|
||||
element. Modify the PlcId to be the same as the one you defined in the
|
||||
<code>StrolchConfiguration.xml</code>. </p>
|
||||
</li>
|
||||
|
||||
<li>Now that we have a model, the PlcConnections are to be defined. In the previous example we used a
|
||||
Raspberry Pi's GPIOs. This needs to be defined as a PlcConnection:
|
||||
<pre>
|
||||
<Resource Id="raspiBcmGpioOutput" Name="Raspi BCM GPIO Output" Type="PlcConnection">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="className" Name="Connection Class" Type="String" Value="li.strolch.plc.core.hw.gpio.RaspiBcmGpioOutputConnection"/>
|
||||
<Parameter Id="state" Name="Connection State" Type="String" Interpretation="Enumeration" Uom="ConnectionState" Value="Disconnected"/>
|
||||
<Parameter Id="stateMsg" Name="Connection State Msg" Type="String" Interpretation="Enumeration" Uom="ConnectionState"
|
||||
Value=""/>
|
||||
<Parameter Id="inverted" Name="Inverted" Type="Boolean" Value="false"/>
|
||||
<Parameter Id="bcmOutputPins" Name="BCM Output Pins" Type="IntegerList" Value="27"/>
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
<Resource Id="raspiBcmGpioInput" Name="Raspi BCM GPIO Input" Type="PlcConnection">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="className" Name="Connection Class" Type="String" Value="li.strolch.plc.core.hw.gpio.RaspiBcmGpioInputConnection"/>
|
||||
<Parameter Id="state" Name="Connection State" Type="String" Interpretation="Enumeration" Uom="ConnectionState" Value="Disconnected"/>
|
||||
<Parameter Id="stateMsg" Name="Connection State Msg" Type="String" Interpretation="Enumeration" Uom="ConnectionState"
|
||||
Value=""/>
|
||||
<Parameter Id="inverted" Name="Inverted" Type="Boolean" Value="true"/>
|
||||
<Parameter Id="bcmInputPins" Name="BCM Input Pins" Type="IntegerList" Value="4"/>
|
||||
</ParameterBag>
|
||||
</Resource></pre>
|
||||
<p>See
|
||||
<a href="https://github.com/4treesCH/strolch-plc/blob/develop/example/strolch-plc-example-connections.xml">strolch-plc-example-connections.xml</a>
|
||||
for further examples. </p>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,893 +0,0 @@
|
|||
<!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: Tutorial</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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</a></li>
|
||||
<li class="active"><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">Tutorial: Configuration</h1>
|
||||
|
||||
<p class="lead page-description">Configure the runtime</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<a href="tutorial.html" class="pull-left">Previous: Start</a><a href="tutorial-model.html" class="pull-right">Next:
|
||||
Model</a>
|
||||
<br><br>
|
||||
|
||||
<p>Let's start by creating a new Apache Maven project. We'll need a POM with the proper dependencies. We expect
|
||||
you to be familiar with Apache Maven, so we'll just show you a working POM file:</p>
|
||||
|
||||
<b>pom.xml</b>
|
||||
<pre class="pre-scrollable">
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>strolch-bookshop</artifactId>
|
||||
<version>0.1.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>strolch-bookshop</name>
|
||||
<description>Bookshop built on Strolch</description>
|
||||
<inceptionYear>2017</inceptionYear>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.build.timestamp.format>yyyy-MM-dd HH:mm:ss</maven.build.timestamp.format>
|
||||
<buildTimestamp>${maven.build.timestamp}</buildTimestamp>
|
||||
|
||||
<jdk.version>1.8</jdk.version>
|
||||
|
||||
<jersey.version>2.25.1</jersey.version>
|
||||
<slf4j.version>1.7.25</slf4j.version>
|
||||
<logback.version>1.2.3</logback.version>
|
||||
<petitparser.version>2.1.0</petitparser.version>
|
||||
<hikaricp.version>2.7.1</hikaricp.version>
|
||||
<postgresql.version>42.1.4</postgresql.version>
|
||||
<gson.version>2.8.2</gson.version>
|
||||
<annotation.version>1.3.1</annotation.version>
|
||||
<javaxmail.version>1.6.0</javaxmail.version>
|
||||
<serverlet.version>3.1.0</serverlet.version>
|
||||
<jaxrs.api.version>2.1</jaxrs.api.version>
|
||||
|
||||
<junit.version>4.12</junit.version>
|
||||
<hamcrest.version>1.3</hamcrest.version>
|
||||
<mockito.version>2.0.8-beta</mockito.version>
|
||||
|
||||
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
|
||||
<maven-source-plugin.version>3.0.1</maven-source-plugin.version>
|
||||
<maven-jar-plugin.version>3.0.2</maven-jar-plugin.version>
|
||||
<maven-war-plugin.version>3.1.0</maven-war-plugin.version>
|
||||
|
||||
<strolch.version>1.6.0-SNAPSHOT</strolch.version>
|
||||
|
||||
<warFinalName>bookshop</warFinalName>
|
||||
<m2eclipse.wtp.contextRoot>${warFinalName}</m2eclipse.wtp.contextRoot>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- base -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>${slf4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>${logback.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- strolch -->
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.utils</artifactId>
|
||||
<version>${strolch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.privilege</artifactId>
|
||||
<version>${strolch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.model</artifactId>
|
||||
<version>${strolch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.agent</artifactId>
|
||||
<version>${strolch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.rest</artifactId>
|
||||
<version>${strolch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.service</artifactId>
|
||||
<version>${strolch.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>li.strolch</groupId>
|
||||
<artifactId>li.strolch.testbase</artifactId>
|
||||
<version>${strolch.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- utils -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>${gson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- web -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${serverlet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>javax.ws.rs-api</artifactId>
|
||||
<version>${jaxrs.api.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-common</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
<artifactId>jersey-server</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.containers</groupId>
|
||||
<artifactId>jersey-container-servlet</artifactId>
|
||||
<version>${jersey.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- testing -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<version>${hamcrest.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-library</artifactId>
|
||||
<version>${hamcrest.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<resources>
|
||||
<!-- filter properties files, and copy the rest -->
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
<includes>
|
||||
<include>**/*.properties</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>false</filtering>
|
||||
<excludes>
|
||||
<exclude>**/*.properties</exclude>
|
||||
</excludes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven-compiler-plugin.version}</version>
|
||||
<configuration>
|
||||
<source>${jdk.version}</source>
|
||||
<target>${jdk.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>${maven-war-plugin.version}</version>
|
||||
<configuration>
|
||||
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||
<warName>${warFinalName}</warName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<!-- active when building on eitch's machines -->
|
||||
<profile>
|
||||
<id>m2e.eitchpc</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>user.name</name>
|
||||
<value>eitch</value>
|
||||
</property>
|
||||
<os>
|
||||
<family>unix</family>
|
||||
</os>
|
||||
</activation>
|
||||
<properties>
|
||||
<strolch.env>dev.eitchpc</strolch.env>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
</pre>
|
||||
|
||||
<p>Now we need the rest of the directory structure:</p>
|
||||
|
||||
<pre class="pre-scrollable">
|
||||
../strolch-bookshop/
|
||||
- src/main/java/
|
||||
- li/strolch/bookshop/
|
||||
- <!-- java classes -->
|
||||
- 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/
|
||||
</pre>
|
||||
|
||||
<p>A few notes to the resource files:</p>
|
||||
<ul>
|
||||
<li>The <code>ENV.properties</code> file is filtered by maven and the environment to load is written in it
|
||||
using the environment variable strolch.env.
|
||||
</li>
|
||||
<li>The <code>appVersion.properties</code> file is also filtered by maven and allows to reflect on the
|
||||
version of this app at runtime.
|
||||
</li>
|
||||
<li>The <code>logback.xml</code> file configures logging using SLF4j and Logback.</li>
|
||||
</ul>
|
||||
|
||||
<p>The <code>StrolchBootstrap.xml</code> file is used to configure Strolch's environment and root directory. For
|
||||
a webapp it can be annoying to store Strolch'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:</p>
|
||||
<pre class="pre-scrollable">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<StrolchBootstrap>
|
||||
<env id="dev.eitchpc" default="true">
|
||||
<root>/home/eitch/src/git/strolch-bookshop/runtime</root>
|
||||
<environment>dev</environment>
|
||||
</env>
|
||||
</StrolchBootstrap>
|
||||
</pre>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>In this next step we'll create Strolch's configuration at the location we defined in the StrolchBootstrap.xml
|
||||
file. Strolch's configuration contains of three directories: config, data and temp. config contains static
|
||||
files which usually aren't changed, data contains model files in XML format and temp is used at runtime for
|
||||
any temporary files, e.g. storing active sessions.</p>
|
||||
|
||||
<p>The configuration as well as the model has been described on Strolch's documentation web page, we'll just
|
||||
provide you with the files for the bookshop:</p>
|
||||
|
||||
<b>PrivilegeConfig.xml</b>
|
||||
<pre class="pre-scrollable">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Privilege>
|
||||
<Container>
|
||||
<Parameters>
|
||||
<!-- parameters for the container itself -->
|
||||
<Parameter name="secretKey" value="45f251ce-d51f-4624-990a-8dcd5b181f0e"/>
|
||||
<Parameter name="secretSalt" value="4770a32d-1512-4891-9a63-362504932500"/>
|
||||
<Parameter name="persistSessions" value="true"/>
|
||||
<Parameter name="autoPersistOnUserChangesData" value="false"/>
|
||||
<Parameter name="privilegeConflictResolution" value="MERGE"/>
|
||||
</Parameters>
|
||||
<EncryptionHandler class="li.strolch.privilege.handler.DefaultEncryptionHandler">
|
||||
<Parameters>
|
||||
<Parameter name="hashAlgorithm" value="PBKDF2WithHmacSHA512"/>
|
||||
<Parameter name="hashIterations" value="10000"/>
|
||||
<Parameter name="hashKeyLength" value="256"/>
|
||||
</Parameters>
|
||||
</EncryptionHandler>
|
||||
<PersistenceHandler class="li.strolch.privilege.handler.XmlPersistenceHandler">
|
||||
<Parameters>
|
||||
<Parameter name="usersXmlFile" value="PrivilegeUsers.xml"/>
|
||||
<Parameter name="rolesXmlFile" value="PrivilegeRoles.xml"/>
|
||||
</Parameters>
|
||||
</PersistenceHandler>
|
||||
<UserChallengeHandler class="li.strolch.privilege.handler.MailUserChallengeHandler">
|
||||
</UserChallengeHandler>
|
||||
</Container>
|
||||
<Policies>
|
||||
<Policy name="DefaultPrivilege" class="li.strolch.privilege.policy.DefaultPrivilege"/>
|
||||
<Policy name="RoleAccessPrivilege" class="li.strolch.privilege.policy.RoleAccessPrivilege"/>
|
||||
<Policy name="UserAccessPrivilege" class="li.strolch.privilege.policy.UserAccessPrivilege"/>
|
||||
<Policy name="UserSessionAccessPrivilege" class="li.strolch.privilege.policy.UsernameFromCertificatePrivilege"/>
|
||||
</Policies>
|
||||
</Privilege>
|
||||
</pre>
|
||||
|
||||
<b>PrivilegeRoles.xml</b>
|
||||
<pre class="pre-scrollable">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Roles>
|
||||
<Role name="User">
|
||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||
</Privilege>
|
||||
|
||||
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
<Allow>li.strolch.bookshop.search.BookSearch</Allow>
|
||||
</Privilege>
|
||||
|
||||
<Privilege name="GetResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="GetOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="GetActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
</Role>
|
||||
<Role name="UserPrivileges">
|
||||
<Privilege name="PrivilegeSetUserLocale" policy="UserAccessPrivilege" />
|
||||
<Privilege name="PrivilegeSetUserPassword" policy="UserAccessPrivilege" />
|
||||
</Role>
|
||||
|
||||
<!--
|
||||
Internal
|
||||
-->
|
||||
<Role name="StrolchAdmin">
|
||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
|
||||
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
|
||||
<Privilege name="GetResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="GetOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="GetActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
|
||||
<Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="PrivilegeSetUserPassword" policy="UserAccessPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
</Role>
|
||||
|
||||
<Role name="agent">
|
||||
<Privilege name="li.strolch.privilege.handler.SystemAction" policy="DefaultPrivilege">
|
||||
<Allow>li.strolch.runtime.privilege.StrolchSystemAction</Allow>
|
||||
<Allow>li.strolch.runtime.privilege.StrolchSystemActionWithResult</Allow>
|
||||
<Allow>li.strolch.persistence.postgresql.PostgreSqlSchemaInitializer</Allow>
|
||||
</Privilege>
|
||||
|
||||
<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>
|
||||
|
||||
<Privilege name="GetResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="GetOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="GetActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="AddActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="UpdateActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveResource" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveOrder" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="RemoveActivity" policy="ModelPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
|
||||
<Privilege name="PrivilegeAction" policy="DefaultPrivilege">
|
||||
<Allow>Persist</Allow>
|
||||
<Allow>PersistSessions</Allow>
|
||||
<Allow>GetCertificates</Allow>
|
||||
</Privilege>
|
||||
<Privilege name="PrivilegeAddUser" policy="UserAccessPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="PrivilegeModifyUser" policy="UserAccessPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
<Privilege name="PrivilegeGetUser" policy="UserAccessPrivilege">
|
||||
<AllAllowed>true</AllAllowed>
|
||||
</Privilege>
|
||||
</Role>
|
||||
|
||||
</Roles>
|
||||
</pre>
|
||||
|
||||
<b>PrivilegeUsers.xml</b>
|
||||
<pre class="pre-scrollable">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Users>
|
||||
<User userId="U10" username="jill" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918">
|
||||
<Firstname>Jill</Firstname>
|
||||
<Lastname>Someone</Lastname>
|
||||
<State>ENABLED</State>
|
||||
<Locale>en-GB</Locale>
|
||||
<Roles>
|
||||
<Role>User</Role>
|
||||
<Role>UserPrivileges</Role>
|
||||
</Roles>
|
||||
<Properties>
|
||||
<Property name="email" value="eitch+jill@eitchnet.ch" />
|
||||
</Properties>
|
||||
</User>
|
||||
|
||||
<User userId="U01" username="admin" password="8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918">
|
||||
<Firstname>Jill</Firstname>
|
||||
<Lastname>Someone</Lastname>
|
||||
<State>ENABLED</State>
|
||||
<Locale>en-GB</Locale>
|
||||
<Roles>
|
||||
<Role>StrolchAdmin</Role>
|
||||
<Role>UserPrivileges</Role>
|
||||
</Roles>
|
||||
<Properties>
|
||||
<Property name="email" value="eitch+admin@eitchnet.ch" />
|
||||
</Properties>
|
||||
</User>
|
||||
|
||||
<!--
|
||||
Internal
|
||||
-->
|
||||
<User userId="S01" username="agent">
|
||||
<State>SYSTEM</State>
|
||||
<Roles>
|
||||
<Role>agent</Role>
|
||||
</Roles>
|
||||
</User>
|
||||
|
||||
</Users>
|
||||
</pre>
|
||||
|
||||
<b>StrolchConfiguration.xml</b>
|
||||
<pre class="pre-scrollable">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<StrolchConfiguration>
|
||||
<env id="global">
|
||||
<Runtime>
|
||||
<applicationName>Bookshop</applicationName>
|
||||
<Properties>
|
||||
<locale>en</locale>
|
||||
<verbose>true</verbose>
|
||||
</Properties>
|
||||
</Runtime>
|
||||
|
||||
<Component>
|
||||
<name>PrivilegeHandler</name>
|
||||
<api>li.strolch.runtime.privilege.PrivilegeHandler</api>
|
||||
<impl>li.strolch.runtime.privilege.DefaultStrolchPrivilegeHandler</impl>
|
||||
<Properties>
|
||||
<privilegeConfigFile>PrivilegeConfig.xml</privilegeConfigFile>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<name>RealmHandler</name>
|
||||
<api>li.strolch.agent.api.RealmHandler</api>
|
||||
<impl>li.strolch.agent.impl.DefaultRealmHandler</impl>
|
||||
<depends>PrivilegeHandler</depends>
|
||||
<Properties>
|
||||
<realms>defaultRealm</realms>
|
||||
|
||||
<dataStoreMode>TRANSIENT</dataStoreMode>
|
||||
<dataStoreFile>defaultModel.xml</dataStoreFile>
|
||||
<enableObserverUpdates>true</enableObserverUpdates>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<name>ServiceHandler</name>
|
||||
<api>li.strolch.service.api.ServiceHandler</api>
|
||||
<impl>li.strolch.service.api.DefaultServiceHandler</impl>
|
||||
<depends>RealmHandler</depends>
|
||||
<depends>PrivilegeHandler</depends>
|
||||
<Properties>
|
||||
<verbose>true</verbose>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<name>PolicyHandler</name>
|
||||
<api>li.strolch.policy.PolicyHandler</api>
|
||||
<impl>li.strolch.policy.DefaultPolicyHandler</impl>
|
||||
<Properties>
|
||||
<readPolicyFile>true</readPolicyFile>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<name>ExecutionHandler</name>
|
||||
<api>li.strolch.execution.ExecutionHandler</api>
|
||||
<impl>li.strolch.execution.EventBasedExecutionHandler</impl>
|
||||
<depends>RealmHandler</depends>
|
||||
<depends>PrivilegeHandler</depends>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<name>RestfulHandler</name>
|
||||
<api>li.strolch.rest.RestfulStrolchComponent</api>
|
||||
<impl>li.strolch.rest.RestfulStrolchComponent</impl>
|
||||
<depends>SessionHandler</depends>
|
||||
<Properties>
|
||||
<secureCookie>false</secureCookie>
|
||||
<restLogging>false</restLogging>
|
||||
<restLoggingEntity>false</restLoggingEntity>
|
||||
<restTracing>ALL</restTracing>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component>
|
||||
<name>SessionHandler</name>
|
||||
<api>li.strolch.rest.StrolchSessionHandler</api>
|
||||
<impl>li.strolch.rest.DefaultStrolchSessionHandler</impl>
|
||||
<depends>PrivilegeHandler</depends>
|
||||
<Properties>
|
||||
<session.ttl.minutes>30</session.ttl.minutes>
|
||||
<session.reload>true</session.reload>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
<Component>
|
||||
<name>MailHandler</name>
|
||||
<api>li.strolch.handler.mail.MailHandler</api>
|
||||
<impl>li.strolch.handler.mail.SmtpMailHandler</impl>
|
||||
<Properties>
|
||||
<fromAddr>relayer@eitchnet.ch</fromAddr>
|
||||
<fromName>Susi</fromName>
|
||||
<overrideRecipients>IPSC Test <eitch@eitchnet.ch></overrideRecipients>
|
||||
<recipientWhitelist>eitch@eitchnet.ch</recipientWhitelist>
|
||||
<username>test</username>
|
||||
<password>test</password>
|
||||
<auth>true</auth>
|
||||
<startTls>true</startTls>
|
||||
<host>smtp.gmail.com</host>
|
||||
<port>587</port>
|
||||
</Properties>
|
||||
</Component>
|
||||
|
||||
</env>
|
||||
|
||||
<env id="dev">
|
||||
<!-- overrides go here -->
|
||||
</env>
|
||||
|
||||
</StrolchConfiguration>
|
||||
</pre>
|
||||
|
||||
<b>StrolchPolicies.xml</b>
|
||||
<pre class="pre-scrollable">
|
||||
<StrolchPolicies>
|
||||
<PolicyType Type="ExecutionPolicy" Api="li.strolch.execution.policy.ExecutionPolicy">
|
||||
<Policy Key="DurationExecution" Class="li.strolch.execution.policy.DurationExecution" />
|
||||
<Policy Key="ReservationExection" Class="li.strolch.execution.policy.ReservationExection" />
|
||||
</PolicyType>
|
||||
<PolicyType Type="ConfirmationPolicy" Api="li.strolch.execution.policy.ConfirmationPolicy">
|
||||
<Policy Key="DefaultConfirmation" Class="li.strolch.execution.policy.ConfirmationPolicy" />
|
||||
</PolicyType>
|
||||
<PolicyType Type="ActivityArchivalPolicy" Api="li.strolch.execution.policy.ActivityArchivalPolicy">
|
||||
<Policy Key="DefaultActivityArchival" Class="li.strolch.execution.policy.ActivityArchivalPolicy" />
|
||||
</PolicyType>
|
||||
</StrolchPolicies>
|
||||
</pre>
|
||||
|
||||
<p>A few notes on the configuration:</p>
|
||||
<ul>
|
||||
<li>Note how there are three users. Jill is a user with currently no privileges as it's role definition is
|
||||
empty. Admin can do everything, and the agent user is a system user which can also do everything.
|
||||
</li>
|
||||
<li>There is one realm defined in the <code>RealmHandler</code> component which references the <code>defaultModel.xml</code>
|
||||
file in the data directory. This file then includes the currently still empty <code>templates.xml</code>
|
||||
file.
|
||||
</li>
|
||||
<li>We have defined a global environment, but are using the dev environment. The dev environment includes
|
||||
the definitions in the global environment.
|
||||
</li>
|
||||
<li>In <code>PrivilegeConfig.xml</code> we have enabled persistence of sessions, so you will be needing the
|
||||
unlimited JCE libraries for your JVM and when you restart the server, you don't need to log back in, if
|
||||
your session is still alive.
|
||||
</li>
|
||||
<li>In <code>PrivilegeRoles.xml</code> 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't see, so that privilege acces is
|
||||
clearly seen.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
Now 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:</p>
|
||||
|
||||
<b>StartupListener.java</b>
|
||||
<pre class="pre-scrollable">
|
||||
package 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 = "Bookshop";
|
||||
|
||||
private StrolchAgent agent;
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
|
||||
logger.info("Starting " + APP_NAME + "...");
|
||||
long start = System.currentTimeMillis();
|
||||
try {
|
||||
String boostrapFileName = "/WEB-INF/" + 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("Failed to start " + APP_NAME + " due to: " + e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
long took = System.currentTimeMillis() - start;
|
||||
logger.info("Started " + APP_NAME + " in " + (StringHelper.formatMillisecondsDuration(took)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
if (this.agent != null) {
|
||||
logger.info("Destroying " + APP_NAME + "...");
|
||||
try {
|
||||
this.agent.stop();
|
||||
this.agent.destroy();
|
||||
} catch (Throwable e) {
|
||||
logger.error("Failed to stop " + APP_NAME + " due to: " + e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
logger.info("Destroyed " + APP_NAME);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Now configure your IDE to start the web project, and then once it has started, you should see the following
|
||||
in the logs:</p>
|
||||
<pre class="pre-scrollable">
|
||||
Bookshop:dev All 8 Strolch Components started. Took 44ms. Strolch is now ready to be used. Have fun =))
|
||||
</pre>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>This concludes the initial setup of a new Strolch project. We can now go ahead and start building the
|
||||
business logic.</p>
|
||||
|
||||
<!-- content here -->
|
||||
|
||||
<a href="tutorial.html" class="pull-left">Previous: Start</a><a href="tutorial-model.html" class="pull-right">Next:
|
||||
Model</a>
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,623 +0,0 @@
|
|||
<!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: Tutorial CRUD Book</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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</a></li>
|
||||
<li class="active"><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">Tutorial: CRUD Book</h1>
|
||||
|
||||
<p class="lead page-description">Writing the CRUD services for books.</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<a href="tutorial-model.html" class="pull-left">Previous: Model</a> <br><br>
|
||||
|
||||
<h3>Preparation</h3>
|
||||
|
||||
<p>Since Books are central to the bookshop, we'll first create the <a target="_blank"
|
||||
href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a>
|
||||
REST API for them. The API will be as follows:</p>
|
||||
|
||||
<pre>
|
||||
GET ../rest/books?query=,offset=,limit=
|
||||
GET ../rest/books/{id}
|
||||
POST ../rest/books
|
||||
PUT ../rest/books/{id}
|
||||
DELETE ../rest/books/{id}
|
||||
</pre>
|
||||
|
||||
<p>Thus corresponding with querying, getting, creating, updating and removing of books. So let's go ahead and
|
||||
add these REST APIs to our project.</p>
|
||||
|
||||
<p>Our 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:</p>
|
||||
<pre class="pre-scrollable">
|
||||
@ApplicationPath("rest")
|
||||
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, "ALL");
|
||||
property(ServerProperties.TRACING_THRESHOLD, "TRACE");
|
||||
}
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
<p>As we add new resources they will be automatically since we register the entire package.</p>
|
||||
|
||||
<p>Now add the books resource class:</p>
|
||||
<pre>
|
||||
@Path("books")
|
||||
public class BooksResource {
|
||||
|
||||
}
|
||||
</pre>
|
||||
|
||||
<h3>Search</h3>
|
||||
|
||||
<p>The first service we'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:</p>
|
||||
|
||||
<pre>
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response query(@Context HttpServletRequest request, @QueryParam("query") String queryS,
|
||||
@QueryParam("offset") String offsetS, @QueryParam("limit") String limitS) {
|
||||
|
||||
// TODO
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>To fill this method we need a few things. First let's define a constants class where we keep String constants
|
||||
which we used in the model file:</p>
|
||||
<pre>
|
||||
public class BookShopConstants {
|
||||
|
||||
public static final String TYPE_BOOK = "Book";
|
||||
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p>In 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 <code>ResourceSearch</code>. The search is for
|
||||
Resources of type Book thus the resulting search looks as follows:</p>
|
||||
<pre>
|
||||
public class BooksSearch<U> extends ResourceSearch<U> {
|
||||
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(" ");
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>Note how we added a special method <code>stringQuery(String)</code> - this method defines where a search
|
||||
string entered by the user will be used to match a book. In this case for <code>ID</code>, <code>name</code>
|
||||
and the <code>description</code> parameter.</p>
|
||||
|
||||
<p>So 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 <code>PrivilegeRoles.xml</code> file as follows:</p>
|
||||
|
||||
<pre>
|
||||
...
|
||||
<Role name="User">
|
||||
<Privilege name="li.strolch.search.StrolchSearch" policy="DefaultPrivilege">
|
||||
<Allow>internal</Allow>
|
||||
<Allow>li.strolch.bookshop.search.BookSearch</Allow>
|
||||
</Privilege>
|
||||
</Role>
|
||||
...
|
||||
</pre>
|
||||
|
||||
<p><b>Note:</b> The <code>internal</code> 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. </p>
|
||||
|
||||
<p>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:</p>
|
||||
<pre class="pre-scrollable">
|
||||
@Path("books")
|
||||
public class BooksResource {
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response query(@Context HttpServletRequest request, @QueryParam("query") String queryS,
|
||||
@QueryParam("offset") String offsetS, @QueryParam("limit") 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.valueOf(offsetS) : 0;
|
||||
int limit = StringHelper.isNotEmpty(limitS) ? Integer.valueOf(limitS) : 0;
|
||||
|
||||
// open the TX with the certificate, using this class as context
|
||||
Paging<Resource> 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<JsonObject> visitor = new StrolchRootElementToJsonVisitor().flat().asResourceVisitor();
|
||||
return ResponseUtil.toResponse(paging, e -> e.accept(visitor));
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p><b>Note:</b> We automatically transform the Resource objects to JSON using the <code>StrolchElementToJsonVisitor</code>.
|
||||
By calling the method <code>.flat()</code> we have a more compact JSON format. Paging is handled
|
||||
by a util class.</p>
|
||||
|
||||
<p>The helper class <code>ResponseUtil</code> takes care of creating the JsonObject and the proper page. As a
|
||||
rule we use the format where we return two fields: <code>msg</code> is a dash if all is ok, otherwise an
|
||||
error message will be present. Data is always in the <code>data</code> field. This is just a personal taste,
|
||||
and can be changed to one's own taste.</p>
|
||||
|
||||
<h3>Get</h3>
|
||||
|
||||
We have all we need now to implement the GET method:
|
||||
|
||||
<pre class="pre-scrollable">
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response get(@Context HttpServletRequest request, @PathParam("id") 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, "Book " + id + " does not exist!");
|
||||
|
||||
// transform to JSON
|
||||
JsonObject bookJ = book.accept(new StrolchRootElementToJsonVisitor().flat());
|
||||
|
||||
// return
|
||||
return ResponseUtil.toResponse(StrolchRestfulConstants.DATA, bookJ);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>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 <code>StrolchTransaction</code>. There are methods to retrieve elements, and also perform
|
||||
queries. We will use more of these methods later.</p>
|
||||
|
||||
<p>Further it can be noted that a simple retrieval isn't validated against the user's privileges, the user is
|
||||
authenticated, which is enough for the moment.</p>
|
||||
|
||||
<h3>Create</h3>
|
||||
|
||||
To create a new book we need to implement a <code>Service</code>. This service will be called <code>CreateBookService</code>.
|
||||
A Service always has a <code>ServiceArgument</code> and a <code>ServiceResult</code>. Our service will use the
|
||||
<code>JsonServiceArgument</code> and the <code>JsonServiceResult</code>. The implementation of the POST method
|
||||
is as follows:
|
||||
|
||||
<pre class="pre-scrollable">
|
||||
@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);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p><b>Note:</b> We return the created object again as JSON in its own data field.</p>
|
||||
|
||||
The service is implemented as follows:
|
||||
<pre class="pre-scrollable">
|
||||
public class CreateBookService extends AbstractService<JsonServiceArgument, JsonServiceResult> {
|
||||
|
||||
@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 "instance" 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);
|
||||
}
|
||||
}
|
||||
|
||||
</pre>
|
||||
|
||||
<p><b>Note:</b> For the authenticated user to be able to perform this service, we must add it to their
|
||||
privileges:</p>
|
||||
<pre>
|
||||
...
|
||||
<Role name="User">
|
||||
...
|
||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||
<Allow>li.strolch.bookshop.service.CreateBookService</Allow>
|
||||
</Privilege>
|
||||
...
|
||||
</Role>
|
||||
...
|
||||
</pre>
|
||||
|
||||
<h3>Update</h3>
|
||||
|
||||
<p>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.</p>
|
||||
|
||||
<p><b>PUT Method:</b></p>
|
||||
<pre class="pre-scrollable">
|
||||
@PUT
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response update(@Context HttpServletRequest request, @PathParam("id") 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);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p><b>Update Service:</b></p>
|
||||
<pre class="pre-scrollable">
|
||||
public class UpdateBookService extends AbstractService<JsonServiceArgument, JsonServiceResult> {
|
||||
|
||||
@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("ObjectId and given Id must be same!", 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);
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p><b>Privilege:</b></p>
|
||||
<pre>
|
||||
...
|
||||
<Role name="User">
|
||||
...
|
||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||
...
|
||||
<Allow>li.strolch.bookshop.service.UpdateBookService</Allow>
|
||||
...
|
||||
</Privilege>
|
||||
...
|
||||
</Role>
|
||||
...
|
||||
</pre>
|
||||
|
||||
|
||||
<h3>Remove</h3>
|
||||
|
||||
<p>To remove a book, we need a DELETE method, a remove service and the associated privilege.</p>
|
||||
|
||||
|
||||
<p><b>DELETE Method:</b></p>
|
||||
<pre class="pre-scrollable">
|
||||
@DELETE
|
||||
@Path("{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response update(@Context HttpServletRequest request, @PathParam("id") 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);
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p><b>Remove Service:</b></p>
|
||||
<pre class="pre-scrollable">
|
||||
public class RemoveBookService extends AbstractService<StringServiceArgument, ServiceResult> {
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p><b>Privilege:</b></p>
|
||||
<pre>
|
||||
...
|
||||
<Role name="User">
|
||||
...
|
||||
<Privilege name="li.strolch.service.api.Service" policy="DefaultPrivilege">
|
||||
...
|
||||
<Allow>li.strolch.bookshop.service.RemoveBookService</Allow>
|
||||
...
|
||||
</Privilege>
|
||||
...
|
||||
</Role>
|
||||
...
|
||||
</pre>
|
||||
|
||||
<h3>Notes:</h3>
|
||||
<p>One should now see a pattern emerge:</p>
|
||||
<ul>
|
||||
<li>The REST API delegates to the Services, or Searches, with the exception of the retrieval of a single
|
||||
object by id.
|
||||
</li>
|
||||
<li>Services should do initial validation of the input. Not much validation was done here, but more could be
|
||||
done.
|
||||
</li>
|
||||
<li>Commands are reusable objects to perform recurring work.</li>
|
||||
<li>Searches and Services are privileged actions for which a user must have the privilege to perform the
|
||||
action.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>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).</p>
|
||||
|
||||
<p>This concludes the CRUD of books.</p>
|
||||
|
||||
<a href="tutorial-model.html" class="pull-left">Previous: Model</a>
|
||||
<!-- content here -->
|
||||
|
||||
<a href="tutorial-model.html"></a>
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,276 +0,0 @@
|
|||
<!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: Tutorial Model</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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</a></li>
|
||||
<li class="active"><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">Tutorial: Model</h1>
|
||||
|
||||
<p class="lead page-description">Defining the model for the bookshop</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<a href="tutorial-configuration.html" class="pull-left">Previous:
|
||||
Configuration</a><a href="tutorial-crud-book.html"
|
||||
class="pull-right">Next: CRUD
|
||||
Books</a>
|
||||
<br><br>
|
||||
|
||||
<p>Looking back at our functionality, we can list the following entities that need to be modelled (We'll go into
|
||||
detail further down):</p>
|
||||
<ul>
|
||||
<li>Book → books can be orderd</li>
|
||||
<li>UserCart → we want to store the cart of the user</li>
|
||||
<li>Account → we need to know where to send the orders</li>
|
||||
<li>PurchaseOrder → we need to know what was ordered and keep track of its state</li>
|
||||
<li>FromStock → we want to use activities to implement the process of an order</li>
|
||||
</ul>
|
||||
|
||||
<p>In Strolch we model entities by defining the element as a template. Thus in the <code>templates.xml</code>
|
||||
file we can add the templates with the following content:</p>
|
||||
|
||||
<b>Book</b>
|
||||
<pre class="pre-scrollable">
|
||||
<Resource Id="Book" Name="Book Template" Type="Template">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="description" Name="Description" Type="String" Value="" />
|
||||
<Parameter Id="quantity" Name="Quantity in Stock" Type="Integer" Value="0" />
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
</pre>
|
||||
|
||||
<b>Account</b>
|
||||
<pre class="pre-scrollable">
|
||||
<Resource Id="Account" Name="Account Template" Type="Template">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="user" Name="User" Type="String" Value="" />
|
||||
<Parameter Id="firstName" Name="First Name" Type="String" Value="" />
|
||||
<Parameter Id="lastName" Name="Last Name" Type="String" Value="" />
|
||||
<Parameter Id="email" Name="E-Mail" Type="String" Value="" />
|
||||
</ParameterBag>
|
||||
<ParameterBag Name="Address" Id="address" Type="Address">
|
||||
<Parameter Id="phone" Name="Telephone Number" Type="String" Value="" />
|
||||
<Parameter Id="street" Name="Street" Type="String" Value="" />
|
||||
<Parameter Id="city" Name="City" Type="String" Value="" />
|
||||
<Parameter Id="zip" Name="Postal Code" Type="String" Value="" />
|
||||
<Parameter Id="country" Name="Country" Type="String" Value="" />
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
</pre>
|
||||
|
||||
<b>UserCart</b>
|
||||
<pre class="pre-scrollable">
|
||||
<Resource Id="UserCart" Name="UserCart Template" Type="Template">
|
||||
<ParameterBag Id="books" Name="Books" Type="Book">
|
||||
<!-- Parameter Id="bookId" Name="Book reference" Type="Float" Value="0" / -->
|
||||
</ParameterBag>
|
||||
<ParameterBag Id="relations" Name="Relations" Type="Parameters">
|
||||
<Parameter Id="account" Name="Account" Type="String" Interpretation="Resource-Ref" Uom="Account" Value="" />
|
||||
</ParameterBag>
|
||||
</Resource>
|
||||
</pre>
|
||||
|
||||
<b>PurchaseOrder</b>
|
||||
<pre class="pre-scrollable">
|
||||
<Order Id="PurchaseOrder" Name="PurchaseOrder Template" Type="Template" State="Created">
|
||||
<ParameterBag Id="books" Name="Books" Type="Book">
|
||||
<!-- Parameter Id="bookId" Name="Book reference" Type="Float" Value="0" / -->
|
||||
</ParameterBag>
|
||||
<ParameterBag Id="relations" Name="Relations" Type="Parameters">
|
||||
<Parameter Id="account" Name="Account" Type="String" Interpretation="Resource-Ref" Uom="Account" Value="" />
|
||||
</ParameterBag>
|
||||
</Order>
|
||||
</pre>
|
||||
|
||||
<b>FromStock</b>
|
||||
<pre class="pre-scrollable">
|
||||
<Activity Id="FromStock" Name="From Stock Template" Type="FromStock" TimeOrdering="Series">
|
||||
<ParameterBag Name="objectives" Id="Objectives" Type="Objectives">
|
||||
<Parameter Name="Duration" Id="duration" Value="PT1MS" Type="Duration" />
|
||||
</ParameterBag>
|
||||
|
||||
<Action Id="validate" Name="Validation of order" Type="Use" ResourceType="Validation" ResourceId="validation" />
|
||||
|
||||
<!-- for each book we do a consume, i.e. reduce the stock quantity -->
|
||||
<Action Id="Consume" Name="Consume Template for book" Type="Template">
|
||||
<ParameterBag Id="parameters" Name="Parameters" Type="Parameters">
|
||||
<Parameter Id="quantity" Name="Quantity" Type="Float" Value="0" />
|
||||
</ParameterBag>
|
||||
</Action>
|
||||
|
||||
<Action Id="package" Name="Packaging of PurchaseOrder" Type="Use" ResourceType="Packaging" ResourceId="packaging" />
|
||||
<Action Id="send" Name="Sending of package" Type="Use" ResourceType="Sending" ResourceId="sending" />
|
||||
|
||||
</Activity>
|
||||
</pre>
|
||||
|
||||
<p>Let's explain a few things:</p>
|
||||
<ul>
|
||||
<li>The <code>Book</code> entity is a <code>Resource</code> object and only contains the description and the
|
||||
current quantity in stock.
|
||||
</li>
|
||||
<li>The <code>Account</code> entity is a Resource and contains the address and further details of the user,
|
||||
and with the <code>user</code> parameter the username is defined, thus referencing the real user.
|
||||
</li>
|
||||
<li>The <code>UserCart</code> entity is a Resource and has a reference to the account Resource. Note how the
|
||||
reference is done using a StringParameter, where Interpretation, UOM and the value is set in a specific
|
||||
manner.
|
||||
</li>
|
||||
<li>The <code>UserCart</code> entity is a Resource and references books using a special
|
||||
<code>ParameterBag</code> with the type set to <code>Book</code>, the actual type of the book entity.
|
||||
Each Parameter is of type <code>Float</code> 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. There will only be one cart per
|
||||
user/account.
|
||||
</li>
|
||||
<li>The <code>PurchaseOrder</code> entity is an <code>Order</code> 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.
|
||||
</li>
|
||||
<li>The <code>FromStock</code> entity is an <code>Activity</code> object and defines the process we will go
|
||||
through when delivering a purchase to a user. Note how the activity has a ParameterBag
|
||||
<code>objectives</code> with a <code>duration</code> parameter. This defines globally for this activity
|
||||
how long each <code>Action</code> should execute. This can be overridden in each Action and can help to
|
||||
plan how much effort goes into the delivering of each PurchaseOrder.
|
||||
</li>
|
||||
<li>Further note how the activity has three special actions (<code>validate</code>, <code>package</code> and
|
||||
<code>send</code>) on which a <code>ResourceType</code> and <code>ResourceId</code> are defined. Actions
|
||||
are always performed on a Resource, as the referenced Resource defines the behaviour of the action
|
||||
through defined <code>Policy</code> objects.
|
||||
</li>
|
||||
<li>For each book which will be purchased, an Action will be created of type <code>Consume</code>. In the
|
||||
template this is defined by a template Action with the id <code>Consume</code> and will later be changed
|
||||
accordingly.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>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 <code>defaultModel.xml</code> file:</p>
|
||||
<pre>
|
||||
<Resource Id="validation" Name="Validation Resource" Type="Validation">
|
||||
<Policies>
|
||||
<Policy Type="ExecutionPolicy" Value="key:ValidationExecution" />
|
||||
<Policy Type="ConfirmationPolicy" Value="key:DefaultConfirmation" />
|
||||
</Policies>
|
||||
</Resource>
|
||||
|
||||
<Resource Id="packaging" Name="Packaging Resource" Type="Packaging">
|
||||
<Policies>
|
||||
<Policy Type="ExecutionPolicy" Value="key:PackagingExecution" />
|
||||
<Policy Type="ConfirmationPolicy" Value="key:DefaultConfirmation" />
|
||||
</Policies>
|
||||
</Resource>
|
||||
|
||||
<Resource Id="sending" Name="Sending Resource" Type="Sending">
|
||||
<Policies>
|
||||
<Policy Type="ExecutionPolicy" Value="key:SendingExecution" />
|
||||
<Policy Type="ConfirmationPolicy" Value="key:DefaultConfirmation" />
|
||||
</Policies>
|
||||
</Resource>
|
||||
</pre>
|
||||
|
||||
<p>What should now be noted by these three new Resources is that they have Policy definitions:</p>
|
||||
<ul>
|
||||
<li><code>ExecutionPolicy</code> → defines how an action on this resource is executed by
|
||||
referencing an ExecutionPolicy implementation.
|
||||
</li>
|
||||
<li><code>ConfirmationPolicy</code> → defines behaviour to be performed on every state change of
|
||||
an action being performed on this resource by referencing an
|
||||
ConfirmationPolicy implementation.
|
||||
</li>
|
||||
</ul>
|
||||
<p>Currently these resources reference policies which don't exist. We will resolve this issue later, when we
|
||||
implement the execution of the activity.</p>
|
||||
|
||||
<p>This concludes the model definition. In the next step we'll start creating services and commands for our
|
||||
model.</p>
|
||||
|
||||
<a href="tutorial-configuration.html" class="pull-left">Previous:
|
||||
Configuration</a><a href="tutorial-crud-book.html"
|
||||
class="pull-right">Next: CRUD
|
||||
Books</a>
|
||||
<!-- content here -->
|
||||
|
||||
<a href="tutorial-model.html"></a>
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,122 +0,0 @@
|
|||
<!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: Tutorial</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><a href="api.html">API</a></li>
|
||||
<li><a href="documentation.html">Documentation</a></li>
|
||||
<li><a href="plc.html">PLC</a></li>
|
||||
<li class="active"><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">Tutorial</h1>
|
||||
|
||||
<p class="lead page-description">Let's build a bookshop!</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<p>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.</p>
|
||||
|
||||
<p>The book store will have the following features:</p>
|
||||
<ul>
|
||||
<li>The store owner can add, update and remove books</li>
|
||||
<li>The store owner can edit the stock quantity</li>
|
||||
<li>Users can view a list of books</li>
|
||||
<li>Users can add books to a virtual cart</li>
|
||||
<li>Users can create and verify an account using an e-mail address</li>
|
||||
<li>Users can submit an order for the books in their cart</li>
|
||||
<li>The store owner can see the orders by state (pending, preparing, sent)</li>
|
||||
<li>The store owner can update the state of an order (preparing, sent)</li>
|
||||
<li>Notify the user when the order is sent</li>
|
||||
</ul>
|
||||
|
||||
<p>Navigation:</p>
|
||||
<ul>
|
||||
<li><a href="tutorial-configuration.html">Configuration</a></li>
|
||||
<li><a href="tutorial-model.html">Model</a></li>
|
||||
</ul>
|
||||
|
||||
<p>The code to the book can be downloaded from <a href="https://github.com/4treesCH/strolch-bookshop">GitHub</a>
|
||||
and will be updated as this tutorial is updated.</p>
|
||||
|
||||
<a href="tutorial-configuration.html" class="pull-right">Next: Configuration</a>
|
||||
|
||||
</div>
|
||||
<!-- /.content -->
|
||||
|
||||
<div id="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted">© 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>
|
|
@ -1,201 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="https://strolch.li/xsd/StrolchModel-1.6.xsd"
|
||||
xmlns="https://strolch.li/xsd/StrolchModel-1.6.xsd"
|
||||
elementFormDefault="qualified" attributeFormDefault="unqualified">
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>This is Version 1.6.x of the StrolchModel XSD.</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:element name="StrolchModel" type="StrolchModelType"/>
|
||||
|
||||
<xs:complexType name="StrolchModelType">
|
||||
<xs:sequence maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:choice>
|
||||
<xs:element type="IncludeFileType" name="IncludeFile" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="OrderType" name="Order" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="ResourceType" name="Resource" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="ActivityType" name="Activity" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="IncludeFileType">
|
||||
<xs:attribute type="xs:string" name="file"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="VersionType">
|
||||
<xs:attribute type="xs:int" name="Version" use="required"/>
|
||||
<xs:attribute type="xs:string" name="CreatedBy" use="required"/>
|
||||
<xs:attribute type="xs:string" name="UpdatedBy" use="required"/>
|
||||
<xs:attribute type="xs:dateTime" name="Created" use="required"/>
|
||||
<xs:attribute type="xs:dateTime" name="Updated" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Deleted" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="OrderType">
|
||||
<xs:sequence>
|
||||
<xs:element type="VersionType" name="Version" maxOccurs="1" minOccurs="0"/>
|
||||
<xs:element type="ParameterBagType" name="ParameterBag" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="PoliciesType" name="Policies" maxOccurs="1" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="Id" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Type" use="required"/>
|
||||
<xs:attribute type="xs:dateTime" name="Date" use="optional"/>
|
||||
<xs:attribute type="StateType" name="State" use="optional"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ResourceType">
|
||||
<xs:sequence>
|
||||
<xs:element type="VersionType" name="Version" maxOccurs="1" minOccurs="0"/>
|
||||
<xs:element type="ParameterBagType" name="ParameterBag" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="TimedStateType" name="TimedState" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="PoliciesType" name="Policies" maxOccurs="1" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="Id" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Type" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ActivityType">
|
||||
<xs:sequence>
|
||||
<xs:element type="VersionType" name="Version" maxOccurs="1" minOccurs="0"/>
|
||||
<xs:element type="ParameterBagType" name="ParameterBag" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:sequence maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:choice>
|
||||
<xs:element type="ActionType" name="Action" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="ActivityType" name="Activity" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:element type="PoliciesType" name="Policies" maxOccurs="1" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="Id" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Type" use="required"/>
|
||||
<xs:attribute type="TimeOrderingType" name="TimeOrdering" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ActionType">
|
||||
<xs:sequence>
|
||||
<xs:element type="ParameterBagType" name="ParameterBag" maxOccurs="unbounded" minOccurs="0"/>
|
||||
<xs:element type="PoliciesType" name="Policies" maxOccurs="1" minOccurs="0"/>
|
||||
<xs:element type="ValueChangeType" name="ValueChange" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="Id" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="ResourceId" use="optional"/>
|
||||
<xs:attribute type="xs:string" name="ResourceType" use="optional"/>
|
||||
<xs:attribute type="StateType" name="State" use="optional"/>
|
||||
<xs:attribute type="xs:string" name="Type" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ParameterBagType">
|
||||
<xs:sequence>
|
||||
<xs:element type="ParameterType" name="Parameter" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="Id" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Type" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ParameterType">
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute type="xs:string" name="Id" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Name" use="required"/>
|
||||
<xs:attribute type="ParameterValueType" name="Type" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Value" use="optional"/>
|
||||
<xs:attribute type="xs:string" name="Interpretation" use="optional"/>
|
||||
<xs:attribute type="xs:string" name="Uom" use="optional"/>
|
||||
<xs:attribute type="xs:boolean" name="Hidden" use="optional"/>
|
||||
<xs:attribute type="xs:int" name="Index" use="optional"/>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="PoliciesType">
|
||||
<xs:sequence>
|
||||
<xs:element type="PolicyType" name="Policy" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="PolicyType">
|
||||
<xs:attribute type="xs:string" name="Type" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Value" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="TimedStateType">
|
||||
<xs:sequence>
|
||||
<xs:element type="ValueType" name="Value" maxOccurs="unbounded" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="Id" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Name" use="required"/>
|
||||
<xs:attribute type="TimedStateTypeType" name="Type" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Interpretation" use="optional"/>
|
||||
<xs:attribute type="xs:string" name="Uom" use="optional"/>
|
||||
<xs:attribute type="xs:boolean" name="Hidden" use="optional"/>
|
||||
<xs:attribute type="xs:int" name="Index" use="optional"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ValueType">
|
||||
<xs:attribute type="xs:dateTime" name="Time" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Value" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="ValueChangeType">
|
||||
<xs:attribute type="xs:string" name="StateId" use="optional"/>
|
||||
<xs:attribute type="xs:dateTime" name="Time" use="required"/>
|
||||
<xs:attribute type="xs:string" name="Value" use="required"/>
|
||||
<xs:attribute type="TimedStateTypeType" name="Type" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="StateType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Created"/>
|
||||
<xs:enumeration value="Planning"/>
|
||||
<xs:enumeration value="Planned"/>
|
||||
<xs:enumeration value="Execution"/>
|
||||
<xs:enumeration value="Stopped"/>
|
||||
<xs:enumeration value="Warning"/>
|
||||
<xs:enumeration value="Error"/>
|
||||
<xs:enumeration value="Executed"/>
|
||||
<xs:enumeration value="Closed"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="TimeOrderingType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Series"/>
|
||||
<xs:enumeration value="Parallel"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="ParameterValueType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Boolean"/>
|
||||
<xs:enumeration value="String"/>
|
||||
<xs:enumeration value="Text"/>
|
||||
<xs:enumeration value="Integer"/>
|
||||
<xs:enumeration value="Long"/>
|
||||
<xs:enumeration value="Float"/>
|
||||
<xs:enumeration value="Date"/>
|
||||
<xs:enumeration value="Duration"/>
|
||||
<xs:enumeration value="StringList"/>
|
||||
<xs:enumeration value="IntegerList"/>
|
||||
<xs:enumeration value="FloatList"/>
|
||||
<xs:enumeration value="LongList"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:simpleType name="TimedStateTypeType">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Boolean"/>
|
||||
<xs:enumeration value="Integer"/>
|
||||
<xs:enumeration value="Float"/>
|
||||
<xs:enumeration value="Long"/>
|
||||
<xs:enumeration value="FloatList"/>
|
||||
<xs:enumeration value="StringSet"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|