forked from eclipse-basyx/basyx-java-server-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide a generic submodel service component
- Loading branch information
Showing
40 changed files
with
2,130 additions
and
0 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
basyx.submodelservice/basyx.submodelservice.component/Dockerfile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
FROM amazoncorretto:17 | ||
USER nobody | ||
WORKDIR /application | ||
ARG JAR_FILE=target/*-exec.jar | ||
COPY ${JAR_FILE} basyxExecutable.jar | ||
ARG AAS4J_JAR_FILE=target/libs/aas4j-model-1.0.2.jar | ||
COPY ${AAS4J_JAR_FILE} libs/aas4j-model-1.0.2.jar | ||
ARG PORT=8081 | ||
ENV SERVER_PORT=${PORT} | ||
ARG CONTEXT_PATH=/ | ||
ENV SERVER_SERVLET_CONTEXT_PATH=${CONTEXT_PATH} | ||
EXPOSE ${SERVER_PORT} | ||
HEALTHCHECK --interval=10s --timeout=3s --retries=10 --start-period=5s CMD curl --fail http://localhost:${SERVER_PORT}${SERVER_SERVLET_CONTEXT_PATH%/}/actuator/health || exit 1 | ||
ENTRYPOINT ["java", "-jar", "basyxExecutable.jar"] |
91 changes: 91 additions & 0 deletions
91
basyx.submodelservice/basyx.submodelservice.component/Readme.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# Eclipse BaSyx - Standalone Submodel Service Component | ||
|
||
This project provides a generic Submodel Service component. With this component, you can deploy your own Submodel Services without the need to build a Spring Boot application or create and deploy your own Dockerfiles. | ||
|
||
*Invoke* calls to the Submodel element *Operation* are delegated in the generic component to Java classes that can either be precompiled or provided as source code. | ||
|
||
These methods can be provided as simple Java classes and do not require additional dependencies for the project. At runtime, the classes from the *aas4j-model* module are already available. Additional libraries can be included via an environment variable. | ||
|
||
## Configuration | ||
|
||
The [example folder](./example/) contains sample settings. For assigning `idShortPaths` to executable Java classes, configuration should be done using Properties in the form of a Properties file or YAML. Environment variables are not processed correctly by Spring when mapping. | ||
|
||
When running the container, the execution folder is /application. | ||
|
||
**Performance Note**: The startup time of the service can be significantly reduced if the sources are already compiled and placed as JAR files or class files in the /application directory. Pre-compiling your code before building the Docker image can improve performance and reduce initialization time. | ||
|
||
### System Properties | ||
|
||
Below are the individual properties used when starting the application. Except for `basyx.submodel.file`, all properties are optional. | ||
|
||
| Property | Example | Explanation | | ||
|-------------------------------------------------------|--------------------------|-----------------------------------------------------------------------------------------------------------------------------------| | ||
| `basyx.submodel.file` | `submodel.json` | Path to the file describing the Submodel. | | ||
| `basyx.operation.java.sourcesPath` | `sources` | Source directory where the Java classes to be compiled are located. These classes can be specified to be loaded at runtime to perform operations. | | ||
| `basyx.operation.java.classesPath` | `classes` | Target directory for compilation. Used in the classpath when loading classes. | | ||
| `basyx.operation.java.additionalClasspath` | `jars/HelloWorld.jar,jars/` | Comma-separated list of additional libraries used during source compilation and loading of executable classes. | | ||
| `basyx.operation.invokation.mappings[SquareOperation]` | `org.example.SquareOp` | Example of a mapping assignment. The `idShortPath` of an operation is assigned to the class `org.example.SquareOp`. | | ||
| `basyx.operation.invokation.mappings[BasicOperations.AddOperation]` | `org.basic.AddOperation` | Another example of mapping an operation to a Java class. | | ||
| `basyx.operation.invokation.defaultMapping` | `org.example.MyOperation` | Specifies the operation to be called if no mapping is found. | | ||
|
||
The example project contains an [application.yml](./example/application.yml) that demonstrates how configuration can be specified clearly using YAML. | ||
|
||
### Structure of Java Classes | ||
|
||
The Java classes that execute operations do not require dependencies and do not need to extend interfaces or implement classes. It is important that they include a method with the following signature: | ||
|
||
```java | ||
public OperationVariable[] invoke(String path, Operation op, OperationVariable[] in) | ||
``` | ||
|
||
Alternatively, the following method can be used if not all arguments are needed: | ||
|
||
```java | ||
public OperationVariable[] invoke(OperationVariable[] in) | ||
``` | ||
|
||
Here is a simple example: | ||
|
||
```java | ||
package org.basic; | ||
|
||
import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.Operation; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.Property; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; | ||
|
||
public class AddOperation { | ||
|
||
public OperationVariable[] invoke(String path, Operation op, OperationVariable[] in) { | ||
Property first = (Property) in[0].getValue(); | ||
Property second = (Property) in[1].getValue(); | ||
int iFirst = Integer.parseInt(first.getValue()); | ||
int iSecond = Integer.parseInt(second.getValue()); | ||
int result = iFirst + iSecond; | ||
Property prop = new DefaultProperty.Builder() | ||
.value(String.valueOf(result)) | ||
.valueType(DataTypeDefXsd.INT) | ||
.build(); | ||
return new OperationVariable[] { new DefaultOperationVariable.Builder().value(prop).build() }; | ||
} | ||
} | ||
``` | ||
|
||
The execution is stateless. A new instance is created each time. | ||
|
||
### Creating a Custom Image | ||
|
||
This setup allows for quick deployment of Submodel Services. However, if you want to avoid configuration and Docker volume binding, you can also create your own images quickly: | ||
|
||
```dockerfile | ||
FROM eclipsebasyx/submodel-service:0.2.0-SNAPSHOT | ||
COPY sources/ /application/sources | ||
COPY jars/ /application/jars | ||
COPY submodel.json /application/submodel.json | ||
COPY application.yml /application/config/application.yml | ||
``` | ||
|
||
The operations can also be precompiled and placed as JARs in the `/application/jars` folder, which is referenced in the `application.yml`. Alternatively, you can place Java classes of the correct syntax in the source folder and reference them in the properties via a mapping. | ||
|
5 changes: 5 additions & 0 deletions
5
basyx.submodelservice/basyx.submodelservice.component/example/Dockerfile.standalone-example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FROM eclipsebasyx/submodel-service:0.2.0-SNAPSHOT | ||
COPY sources/ /application/sources | ||
COPY jars/ /application/jars | ||
COPY submodel.json /application/submodel.json | ||
COPY application.yml /application/config/application.yml |
19 changes: 19 additions & 0 deletions
19
basyx.submodelservice/basyx.submodelservice.component/example/Readme.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
## Submodel Service Component Example | ||
|
||
This example describes a potential setup for the Submodel Service using Docker Compose. | ||
|
||
## Configuration | ||
|
||
The [docker-compose.yml](docker-compose.yml) file provides a basic setup for starting the service. The Docker image used is initially built based on the [Dockerfile located in the parent directory](../Dockerfile). | ||
|
||
Volumes are used to provide the Submodel and the executable source code to the container, which are referenced in the environment section. The mapping of `idShortPath` to Java classes is also referenced there and loaded via [application-mappings.yml](application-mappings.yml). Alternatively, you can simplify the setup by configuring everything directly in [application.yml](application.yml). | ||
|
||
**Performance Note:** For faster startup times, it's recommended to pre-compile your code and provide it as JAR files or class files. The [aas4j](https://github.com/eclipse-aas4j/aas4j) model classes are available at runtime and do not need to be added to the classpath. | ||
|
||
### Test Script | ||
|
||
Please review the [start-container.sh](start-container.sh) shell script and execute it. The script first builds the executable JAR using Maven, if necessary, which is then copied into the Docker image. After that, the Docker Compose stack is started, and test cases are executed. | ||
|
||
## Standalone Image | ||
|
||
The [Dockerfile.standalone-example](Dockerfile.standalone-example) provides an example of how a Dockerfile for a standalone service might look. When using standalone images, mounting additional volumes or setting environment variables is not strictly necessary. For standalone images, it's recommended to provide precompiled classes or a JAR file to expedite the service startup. The `aas4j` model classes are available at runtime and do not need to be added to the classpath. |
7 changes: 7 additions & 0 deletions
7
basyx.submodelservice/basyx.submodelservice.component/example/application-mappings.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
basyx: | ||
operation: | ||
invokation: | ||
mappings: | ||
SquareOperation: SquareOperation | ||
BasicOperations.AddOperation: basic.ops.AddOperation | ||
BasicOperations.HelloOperation: HelloOperation |
14 changes: 14 additions & 0 deletions
14
basyx.submodelservice/basyx.submodelservice.component/example/application.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
basyx: | ||
submodel: | ||
file: submodel.json | ||
operation: | ||
java: | ||
sourcesPath: sources | ||
classesPath: classes | ||
additionalClasspath: | ||
- jars/HelloWorld.jar | ||
invokation: | ||
mappings: | ||
SquareOperation: SquareOperation | ||
"BasicOperations.AddOperation": basic.ops.AddOperation | ||
BasicOperations.HelloOperation: HelloOperation |
24 changes: 24 additions & 0 deletions
24
basyx.submodelservice/basyx.submodelservice.component/example/docker-compose.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
version: "3.9" | ||
services: | ||
submodel-service: | ||
image: eclipsebasyx/submodel-service:test | ||
build: | ||
context: .. | ||
container_name: submodel-service | ||
environment: | ||
BASYX_SUBMODEL_FILE: submodel.json | ||
BASYX_OPERATION_JAVA_SOURCESPATH: sources | ||
BASYX_OPERATION_JAVA_CLASSESPATH: classes | ||
BASYX_OPERATION_JAVA_ADDITIONALCLASSPATH: jars/HelloWorld.jar | ||
# we should use properties or yml files for operation mappings | ||
# Environment variables could not be parsed properly | ||
# dots of the idShortPath are not handled properly by spring and also the lower-case conversion of spring is problematic | ||
# so either we use config file for mappings or define everything in a .properties or .yml file | ||
SPRING_PROFILES_ACTIVE: mappings | ||
ports: | ||
- 8111:8081 | ||
volumes: | ||
- ./submodel.json:/application/submodel.json:ro | ||
- ./sources/:/application/sources/:ro | ||
- ./jars/:/application/jars/:ro | ||
- ./application-mappings.yml:/application/config/application-mappings.yml/:ro |
Binary file added
BIN
+675 Bytes
basyx.submodelservice/basyx.submodelservice.component/example/jars/HelloWorld.jar
Binary file not shown.
15 changes: 15 additions & 0 deletions
15
basyx.submodelservice/basyx.submodelservice.component/example/sources/HelloOperation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.Property; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; | ||
|
||
public class HelloOperation { | ||
|
||
public OperationVariable[] invoke(OperationVariable[] in) { | ||
String value = new HelloWorld().sayHello(); | ||
Property prop = new DefaultProperty.Builder().value(value).valueType(DataTypeDefXsd.STRING).build(); | ||
return new OperationVariable[] {new DefaultOperationVariable.Builder().value(prop).build()}; | ||
} | ||
|
||
} |
17 changes: 17 additions & 0 deletions
17
basyx.submodelservice/basyx.submodelservice.component/example/sources/SquareOperation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.Property; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; | ||
|
||
public class SquareOperation { | ||
|
||
public OperationVariable[] invoke(OperationVariable[] vars) { | ||
Property prop = (Property) vars[0].getValue(); | ||
int value = Integer.parseInt(prop.getValue()); | ||
int result = value * value; | ||
Property toReturn = new DefaultProperty.Builder().value(String.valueOf(result)).valueType(DataTypeDefXsd.INT).build(); | ||
return new OperationVariable[] {new DefaultOperationVariable.Builder().value(toReturn).build()}; | ||
} | ||
|
||
} |
24 changes: 24 additions & 0 deletions
24
...bmodelservice/basyx.submodelservice.component/example/sources/basic/ops/AddOperation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package basic.ops; | ||
|
||
import java.util.List; | ||
|
||
import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.Operation; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.Property; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultOperationVariable; | ||
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultProperty; | ||
|
||
public class AddOperation { | ||
|
||
public OperationVariable[] invoke(String path, Operation op, OperationVariable[] in) { | ||
Property first = (Property) in[0].getValue(); | ||
Property second = (Property) in[1].getValue(); | ||
int iFirst = Integer.parseInt(first.getValue()); | ||
int iSecond = Integer.parseInt(second.getValue()); | ||
int result = iFirst + iSecond; | ||
Property prop = new DefaultProperty.Builder().value(String.valueOf(result)).valueType(DataTypeDefXsd.INT).build(); | ||
return new OperationVariable[] {new DefaultOperationVariable.Builder().value(prop).build()}; | ||
} | ||
|
||
} |
96 changes: 96 additions & 0 deletions
96
basyx.submodelservice/basyx.submodelservice.component/example/start-container.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
#!/bin/bash | ||
|
||
project_was_built() { | ||
if find ../target -type f -name "basyx.submodelservice.component*-exec.jar" | grep -q "^"; then | ||
return 0 | ||
else | ||
return 1 | ||
fi | ||
} | ||
|
||
build_maven() { | ||
echo building maven artifacts ... | ||
mvn -f ../../../pom.xml clean install -DskipTests | ||
} | ||
|
||
run_square() { | ||
echo "Please enter an integer value:" | ||
read -r int_value | ||
response=$(curl http://localhost:8111/submodel/submodel-elements/SquareOperation/invoke \ | ||
-H "Content-Type: application/json" \ | ||
-H "Accept: application/json" \ | ||
-d "{ \"inputArguments\" : [{ \"value\" : { \"modelType\" : \"Property\", \"value\" : \"${int_value}\" }}]}" \ | ||
-s | jq '.outputArguments[0].value.value') | ||
echo result: $response | ||
} | ||
|
||
|
||
run_add() { | ||
echo "Please enter the first integer value:" | ||
read -r int_value1 | ||
echo "Please enter the second integer value:" | ||
read -r int_value2 | ||
response=$(curl http://localhost:8111/submodel/submodel-elements/BasicOperations.AddOperation/invoke \ | ||
-H "Content-Type: application/json" \ | ||
-H "Accept: application/json" \ | ||
-d "{ \"inputArguments\" : [{ \"value\" : { \"modelType\" : \"Property\", \"value\" : \"${int_value1}\" }},{ \"value\" : { \"modelType\" : \"Property\", \"value\" : \"${int_value2}\" }}]}" \ | ||
-s | jq '.outputArguments[0].value.value') | ||
echo result: $response | ||
} | ||
|
||
run_hello() { | ||
response=$(curl http://localhost:8111/submodel/submodel-elements/BasicOperations.HelloOperation/invoke \ | ||
-H "Content-Type: application/json" \ | ||
-H "Accept: application/json" \ | ||
-d "{ }" \ | ||
-s | jq '.outputArguments[0].value.value') | ||
echo result: $response | ||
} | ||
|
||
run_tests() { | ||
echo We are now running tests with curl | ||
|
||
echo The following operations can be invoked: | ||
echo "'s': square operation" | ||
echo "'h': hello world operation" | ||
echo "'a': add operation" | ||
echo "'e': exit" | ||
while true; do | ||
# Nutzer nach Eingabe fragen | ||
echo "Please enter 's', 'h', 'a' or 'e' to exit:" | ||
read -r user_input | ||
|
||
# Prüfen, welche Eingabe gemacht wurde | ||
if [[ "$user_input" == "s" ]]; then | ||
run_square | ||
elif [[ "$user_input" == "h" ]]; then | ||
run_hello | ||
elif [[ "$user_input" == "a" ]]; then | ||
run_add | ||
elif [[ "$user_input" == "e" ]]; then | ||
echo Exit... | ||
break | ||
else | ||
echo "Input not valid. Please enter 's', 'h', 'a' or 'e'." | ||
fi | ||
done | ||
} | ||
|
||
if ! project_was_built; then | ||
read -p "The maven artifact does not exist. Do you want to build all maven artifacts now? [Y/n]: " build_maven | ||
build_maven=${build_maven:-Y} | ||
if [[ "$build_maven" =~ ^[Yy]$ ]]; then | ||
build_maven; | ||
else | ||
echo abort | ||
return; | ||
fi | ||
fi | ||
|
||
docker-compose up --build --force-recreate --detach --wait | ||
echo service started: http://localhost:8111/submodel | ||
|
||
run_tests; | ||
|
||
echo "Shutting down containers" | ||
docker-compose down |
Oops, something went wrong.