Overview

The Strolch API revolves around the StrolchTransaction object. The main concept is to implement your use cases in Service implementations. You open a transaction using the openTx(String)-method and then perform the use case by adding your Command instances to the transaction.

Transactions are opened on a StrolchRealm. The realms are used to separate mandates in a single runtime instance of Strolch. Each realm has its own ResourceMap, OrderMap, ActivityMap instances from which the TX retrieves the elements.

Model

The Strolch model is implemented in the project li.strolch.model.

The Strolch model consists of three root level elements: Resource, Order and Activity. Each element has at least the following attributes:

Each root element can have any number of ParameterBag instances on it, which in turn can have any number of Parameters on it. Accessing these objects is always done by their IDs. Strolch root elements are always stored in the respective ElementMaps in their Strolch realm. Thus accessing a certain parameter from a Resource would look like this:

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);
}
XML Presentation of Strolch's top level elements of Resources:
<!-- Resource instance -->
<Resource Id="MyTestResource" Name="Test Name" Type="TestType">
  <ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
    <Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
    <Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
    <Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
  </ParameterBag>
  <ParameterBag Id="@bag02" Name="Test Bag" Type="TestBag">
    <Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
    <Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
    <Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
    <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
  </ParameterBag>
  <TimedState Id="@integerState" Name="Integer State" Type="IntegerState">
    <Value Time="0" Value="1" />
    <Value Time="1" Value="2" />
    <Value Time="2" Value="3" />
  <TimedState>
</Resource>
XML Presentation of Strolch's top level elements of Orders:
<!-- Order instance -->
<Order Id="MyTestOrder" Name="Test Name" Type="TestType" Date="2013-11-20T07:42:57.699Z" State="CREATED">
  <ParameterBag Id="@bag01" Name="Test Bag" Type="TestBag">
    <Parameter Id="@param7" Name="StringList Param" Type="StringList" Value="Hello;World" />
    <Parameter Id="@param6" Name="Date Param" Type="Date" Value="2012-11-30T18:12:05.628+01:00" />
    <Parameter Id="@param5" Name="String Param" Type="String" Value="Strolch" />
  </ParameterBag>
  <ParameterBag Id="@bag02" Name="Test Bag" Type="TestBag">
    <Parameter Id="@param4" Name="Long Param" Type="Long" Value="4453234566" />
    <Parameter Id="@param3" Name="Integer Param" Type="Integer" Value="77" />
    <Parameter Id="@param2" Name="Float Param" Type="Float" Value="44.3" />
    <Parameter Id="@param1" Name="Boolean Param" Type="Boolean" Value="true" />
  </ParameterBag>
</Order>
XML Presentation of Strolch's top level elements of Activities:
<!-- Activity instance -->
<Activity Id="bicycleProduction" Name="Bicycle Production" Type="Series">

  <Activity Id="componentProduction" Name="Production of components" Type="Series">

    <Action Id="consumeGears" Name="Gears"
                    ResourceId="gears" ResourceType="Article" Type="Consume">
      <ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
        <Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
        <Parameter Id="duration" Name="Duration" Type="Duration" Value="PT0S" />
      </ParameterBag>
    </Action>

    <Activity Id="frameProduction" Name="Production frame" Type="Series">
      <Action Id="produce" Name="Production frame"
                    ResourceId="frameProduction" ResourceType="Machine" Type="Use">
        <ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
          <Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
          <Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" />
        </ParameterBag>
      </Action>
      <Action Id="toStock" Name="Frame ToStock"
                    ResourceId="frame" ResourceType="Article" Type="Produce">
        <ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
          <Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
          <Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" />
        </ParameterBag>
      </Action>
    </Activity>

    <Activity Id="brakeProduction" Type="Series" Name="Herstellen Bremsen" TimeOrdering="Series">
      <Action Id="produce" Name="Production saddle"
                    ResourceId="saddleProduction" ResourceType="Machine" Type="Use">
        <ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
          <Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
          <Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" />
        </ParameterBag>
      </Action>
      <Action Id="toStock" Name="Saddle ToStock"
                    ResourceId="frame" ResourceType="Article" Type="Produce">
        <ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
          <Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
          <Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" />
        </ParameterBag>
      </Action>
    </Activity>

  </Activity>

  <Action Id="assembly" Name="Bicycle assemble"
                    ResourceId="bicycleAssembly" ResourceType="Assembly" Type="Use">
    <ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
      <Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
      <Parameter Id="duration" Name="Duration" Type="Duration" Value="PT5M" />
    </ParameterBag>
  </Action>

  <Action Id="toStock" Name="Bicycle to stock"
                    ResourceId="bicycle" ResourceType="Product" Type="Produce">
    <ParameterBag Id="objectives" Name="Production goals" Type="Objectives">
      <Parameter Id="quantity" Name="Quantity" Type="Float" Value="1" />
      <Parameter Id="duration" Name="Duration" Type="Duration" Value="PT1M" />
    </ParameterBag>
  </Action>
</Activity>

Realms

Strolch realms implement the multi-client capability which is thus baked right into the Strolch runtime. When configuring a Strolch runtime, realms are configured and for each realm the data store mode is set. Each realm has its own persistence configuration and can thus run in one of the 4 modes that the Strolch agent implements:

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:

ComponentContainer container = getAgent().getContainer();
StrolchRealm realm = container.getRealm(StrolchConstants.DEFAULT_REALM);
try(StrolchTransaction tx = realm.openTx()) {
  Resource resource = tx.getResourceBy("TestType", "MyTestResource");
  ...
}
In a Service implementation there is a convenience method, so that this is as simple as calling openTx(String).

Services and Commands

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.

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.

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.

A typical Service and Command implementation would look as follows:

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(getContainer(), tx);
      
      // set the arguments
      command.setParameter(parameter);
      command.setName(arg.name);
      command.setInterpretation(arg.interpretation);
      command.setUom(arg.uom);
      command.setHidden(arg.hidden);
      command.setIndex(arg.index);
      command.setValueAsString(arg.valueAsString);

      // add the command to the transaction
      tx.addCommand(command);

      // only now do we say we want to commit so that a rollback works nicely
      tx.commitOnClose();
    }

    // return the execution result of the service
    return ServiceResult.success();
  }

  /**
   * The argument class for this service
   */
  public static class SetParameterArg extends ServiceArgument {
    public static final long serialVersionUID = 1L;
    public Locator locator;

    public String name;
    public String interpretation;
    public String uom;
    public Boolean hidden;
    public Integer index;

    public String valueAsString;
  }

  @Override
  protected ServiceResult getResultInstance() {
    return new ServiceResult();
  }
}
                
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(ComponentContainer container, StrolchTransaction tx) {
    super(container, tx);
  }

  // setters for input ...
  // getters for output ...

  @Override
  public void validate() {
    DBC.PRE.assertNotNull("Parameter may not be null!", this.parameter);
  }

  @Override
  public void doCommand() {

    // lock the element to be modified
    StrolchRootElement rootElement = this.parameter.getRootElement();
    tx().lock(rootElement);

    // perform changes
    if (this.valueAsString != null) {
      this.oldValueAsString = this.parameter.getValueAsString();
      SetParameterValueVisitor visitor = new SetParameterValueVisitor();
      visitor.setValue(this.parameter, this.valueAsString);
    }

    // update root element
    if (hasChanges()) {
      replacedElement = new UpdateElementVisitor(tx()).update(rootElement);
    }
  }

  private boolean hasChanges() {
    return this.oldValueAsString != null || this.oldName != null || this.oldInterpretation != null
        || this.oldUom != null || this.oldHidden != null || this.oldIndex != null;
  }

  @Override
  public void undo() {

      // undo changes
      if (this.oldValueAsString != null) {
        SetParameterValueVisitor visitor = new SetParameterValueVisitor();
        visitor.setValue(this.parameter, this.oldValueAsString);
      }

      // update root element
      if (hasChanges()
                    && this.replacedElement != null
                    && this.replacedElement != this.parameter.getRootElement()) {
        new UpdateElementVisitor(tx()).update(replacedElement);
      }
    }
  }
}

Code

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 4trees CI Server.

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.

Strolch on GitHub

Main Strolch components

Additional components

Meta projects

Example projects

Development

To start getting involved with Strolch Development, or create your own applications using Strolch, then see the development page