Getting Started with Java APIs for Aspect Models
General Considerations
In this section, the Java APIs for working with Aspect Models are described. All of the components described in the
following subsections can either be included in your project using a dedicated dependency (as described in the
respective subsection), or you can use the esmf-aspect-model-starter artifact that aggregates all necessary dependencies:
- Maven
-
<dependency> <groupId>org.eclipse.esmf</groupId> <artifactId>esmf-aspect-model-starter</artifactId> <version>2.12.0</version> </dependency> - Gradle Groovy DSL
-
implementation 'org.eclipse.esmf:esmf-aspect-model-starter:2.12.0' - Gradle Kotlin DSL
-
implementation("org.eclipse.esmf:esmf-aspect-model-starter:2.12.0")
Alternatively, you can use the esmf-sdk-parent artifact as a
Maven
BOM (Bill of Materials) to enable declaring dependencies to specific modules without an explicit
version. Add the following section to the Maven pom.xml in your project:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.eclipse.esmf</groupId>
<artifactId>esmf-sdk-parent</artifactId>
<version>2.12.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Getting the right version
The tooling can be made available in two different versions: release and milestone. The release version represents a stable version of the product
and can be referenced in your projects in the usual way from the Maven Central repository. There is also the possibility to have the intermediate builds
made available, these are called milestone builds. Instead of Maven Central, these are released via
GitHub Packages mechanism.
To be able to use the artifacts released in this way in your projects, first the right repository has to be added to your pom.xml file:
<repositories>
<repository>
<id>github</id>
<name>ESMF SDK</name>
<url>https://maven.pkg.github.com/eclipse-esmf/esmf-sdk</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
Then the desired dependencies can be referenced in the usual way. For an example, please refer to Github - Installing a package.
JDK requirements
The esmf-sdk components are built with Java 21 and require a JDK >= 21 such as Adoptium Temurin.
The esmf-sdk can also be used with a Java 21-compatible GraalVM JDK.
|
When using esmf-sdk with a GraalVM JDK and you target
|
Parsing Aspect Model URNs
The aspect-model-urn artifact provides a way to parse and validate Aspect model element URNs as described in the
specification.
Show used imports
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
final AspectModelUrn urn = AspectModelUrn.fromUrn( "urn:samm:com.example:1.0.0#Example" );
final String namespace = urn.getNamespaceMainPart(); // com.example
final String name = urn.getName(); // Example
final String version = urn.getVersion(); // 1.0.0
final String urnPrefix = urn.getUrnPrefix(); // urn:samm:com.example:1.0.0#
To include the artifact, use the following dependencies:
- Maven
-
<dependency> <groupId>org.eclipse.esmf</groupId> <artifactId>esmf-aspect-model-urn</artifactId> <version>2.12.0</version> </dependency> - Gradle Groovy DSL
-
implementation 'org.eclipse.esmf:esmf-aspect-model-urn:2.12.0' - Gradle Kotlin DSL
-
implementation("org.eclipse.esmf:esmf-aspect-model-urn:2.12.0")
Loading and Saving Aspect Models
Aspect models are, like the Semantic Aspect Meta Model, described using the Resource Description Format (RDF, [rdf11]) and the Terse RDF Triple Language syntax (TTL, [turtle]). When an Aspect Model is loaded, there are two ways of working with it: Either on the abstraction level of the underlying RDF/Turtle serialization, or on the native Java Aspect Model level, where the model is represented as a Java object graph. Both approaches have different use cases and advantages and both are supported by the Aspect Model Java tooling:
| Working on the RDF level | Using the Java Aspect model |
|---|---|
|
|
As a rule of thumb, if your use case mostly consists of consuming Aspect models, you should prefer the Java Aspect model, if you create or edit Aspect models, or build code interfacing with other RDF vocabularies, this is better done using the RDF API.
Loading an Aspect Model to work on the Java Aspect Model Level
To load an AspectModel, you use the org.eclipse.esmf.aspectmodel.loader.AspectModelLoader class. An instance
of AspectModelLoader provides load() methods to load an Aspect Model from an InputStream, one or multiple
Files, or a number of AspectModelUrn (where you can delegate finding out where a model element is defined
to the AspectModelLoader - details on that are explained in the next section).
The AspectModelLoader loads Aspect Models based on the most recent version of the Semantic Aspect Meta Model
and previous versions: Models based on older meta model versions are automatically translated to models
corresponding to the latest meta model version.
The resulting AspectModel object contains information about the loaded model elements, their
namespaces and the files they were defined in. Details about the structure of these objects can be
found in the
Decision
Record 0007. The AspectModel provides the methods elements(), files() and namespaces() as
well as the convenience methods aspects() (which returns all Aspect elements in the model) and
aspect(), which returns the single Aspect element if one exists.
Show used imports
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
import org.eclipse.esmf.metamodel.AspectModel;
import org.eclipse.esmf.metamodel.ModelElement;
final AspectModel aspectModel = new AspectModelLoader().load(
// a File, InputStream or AspectModelUrn
);
// Do something with the elements
for ( final ModelElement element : aspectModel.elements() ) {
System.out.printf( "Model element: %s has URN %s%n", element.getName(), element.urn() );
}
// Directly work with the Aspect, if the AspectModel happens to contain
// exactly one Aspect. If there are 0 or >1 Aspects, this will throw an exception.
System.out.println( "Aspect URN: " + aspectModel.aspect().urn() );
To include the Java Aspect Model artifact, use the following dependency:
- Maven
-
<dependency> <groupId>org.eclipse.esmf</groupId> <artifactId>esmf-aspect-meta-model-java</artifactId> <version>2.12.0</version> </dependency> - Gradle Groovy DSL
-
implementation 'org.eclipse.esmf:esmf-aspect-meta-model-java:2.12.0' - Gradle Kotlin DSL
-
implementation("org.eclipse.esmf:esmf-aspect-meta-model-java:2.12.0")
Understanding Model Resolution
The example in the last section showed how a self-contained Aspect Model file can be loaded (i.e., a
file that does not refer to model element definitions in other files or namespaces). Whenever models
are loaded that could contain such references, or you want to load a model element by URN, the
AspectModelLoader relies on so-called resolution strategies. A resolution strategy is a function
that takes a model element identifier (a model element URN) as an input and returns the content of
the file that contains the corresponding model element definition. Note that this is not necessarily
a file in the local filesystem, but it could also be a remote file or even exist only virtually as a
collection of statements in an RDF triple store. Several ResolutionStrategys are provided
that can be instantiated and passed to the AspectModelLoader constructor to enable it to find
model element definitions:
-
The
FileSystemStrategyresolves elements from files in the file system which are either structured in the models directory structure or exist as flat list of files in one directory (by usingFileSystemStrategywith aFlatModelsRoot). -
The
ClassPathStrategyresolves model elements from resources in the Java class path. -
The
FromLoadedFileStrategyresolves model elements from anAspectModelFilethat already resides in memory. -
The
EitherStrategycan be used to chain two or more differentResolutionStrategys. -
The
ExternalResolverStrategydelegates resolution to an external command such as a script; it is used in the implementation of the--custom-resolveroption of the samm-cli. -
The
GitHubStrategyresolves model elements from repositories hosted on GitHub.
In addition, custom resolution strategies can be provided by implementing the ResolutionStrategy interface.
The following example demonstrates how to pass a custom instance of a ResolutionStrategy and resolve a model
element by URN:
Show used imports
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
import org.eclipse.esmf.aspectmodel.resolver.FileSystemStrategy;
import org.eclipse.esmf.aspectmodel.resolver.ResolutionStrategy;
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
import org.eclipse.esmf.metamodel.AspectModel;
import org.eclipse.esmf.metamodel.vocabulary.SammNs;
import org.apache.jena.vocabulary.RDF;
// The directory containing the models folder structure,
// see models directory structure
final Path modelsRoot = Paths.get( "aspect-models" );
final ResolutionStrategy fileSystemStrategy = new FileSystemStrategy( modelsRoot );
final AspectModelUrn urn = AspectModelUrn.fromUrn( "urn:samm:org.eclipse.esmf.examples.movement:1.0.0#Movement" );
final AspectModel result = new AspectModelLoader( fileSystemStrategy ).load( urn );
Loading an Aspect Model to work on the RDF Level
The following example shows how to use the AspectModelLoad to load an Aspect Model.
Show used imports
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.eclipse.esmf.aspectmodel.loader.AspectModelLoader;
import org.eclipse.esmf.aspectmodel.resolver.FileSystemStrategy;
import org.eclipse.esmf.aspectmodel.resolver.ResolutionStrategy;
import org.eclipse.esmf.aspectmodel.urn.AspectModelUrn;
import org.eclipse.esmf.metamodel.AspectModel;
import org.eclipse.esmf.metamodel.vocabulary.SammNs;
import org.apache.jena.vocabulary.RDF;
final AspectModel aspectModel = new AspectModelLoader().load(
// a File, InputStream or AspectModelUrn
);
// Do something with the Aspect Model on the RDF level.
// Example: List the URNs of all samm:Entitys
aspectModel.mergedModel().listStatements( null, RDF.type, SammNs.SAMM.Entity() )
.forEachRemaining( statement -> System.out.println( statement.getSubject().getURI() ) );
After loading an AspectModel using the AspectModelLoader, you can access the mergedModel() on the
AspectModel, which represents the merged RDF graph of all source files that were loaded in the model and
therefore contains all transitively referenced model element definitions. Alternatively, you can also access
the sourceModel() of each Aspect Model file given by files(), which will return only the RDF graph of this
file.
Accessing the SAMM programmatically
In order to access the source RDF files that describe the SAMM vocabulary, shared Characteristics and Entities
as well as Units, you can add a dependency to the esmf-aspect-meta-model artifact. Note that this artifact
does not provide Java classes that represent the meta model.
- Maven
-
<dependency> <groupId>org.eclipse.esmf</groupId> <artifactId>esmf-semantic-aspect-meta-model</artifactId> <version>2.2.0</version> </dependency> - Gradle Groovy DSL
-
implementation 'org.eclipse.esmf:esmf-semantic-aspect-meta-model:2.2.0' - Gradle Kotlin DSL
-
implementation("org.eclipse.esmf:esmf-semantic-aspect-meta-model:2.2.0")
In order to access the files via java.lang.Class#getResourceAsStream, you can refer to the following directory
structure that is present in the artifact:
.
├── characteristic
│ └── 2.2.0
│ ├── characteristic-definitions.ttl
│ ├── characteristic-instances.ttl
│ ├── characteristic-shapes.ttl
│ └── characteristic-validations.js
├── entity
│ └── 2.2.0
│ ├── FileResource.ttl
│ ├── ThreeDimensionalPosition.ttl
│ └── TimeSeriesEntity.ttl
├── meta-model
│ └── 2.2.0
│ ├── aspect-meta-model-definitions.ttl
│ ├── aspect-meta-model-shapes.ttl
│ ├── prefix-declarations.ttl
│ └── type-conversions.ttl
└── unit
└── 2.2.0
└── units.ttl
You can use the MetaModelFile enumeration provided by the esmf-aspect-meta-model-java module to
access object representations of the meta model files, for example:
|
Show used imports
import org.apache.jena.rdf.model.Model;
import org.apache.jena.vocabulary.RDF;
import org.eclipse.esmf.aspectmodel.resolver.modelfile.MetaModelFile;
import org.eclipse.esmf.metamodel.vocabulary.SammNs;
import org.eclipse.esmf.samm.KnownVersion;
final KnownVersion metaModelVersion = KnownVersion.getLatest();
final Model characteristicDefinitions = MetaModelFile.CHARACTERISTIC_DEFINITIONS.sourceModel();
// Do something with the org.apache.jena.org.rdf.model.Model
final int numberOfCharacteristicInstances =
characteristicDefinitions.listStatements( null, RDF.type, SammNs.SAMM.Characteristic() ).toList().size();
final String result = String.format( "Meta Model Version " + metaModelVersion.toVersionString()
+ " defines " + numberOfCharacteristicInstances + " Characteristic instances" );
Next Steps
For more advanced topics, see the dedicated pages:
-
Creating and Modifying Models - Learn how to programmatically create and modify Aspect Models
-
Validation - Validate your Aspect Models and handle validation results
-
Code Generation - Generate Java POJOs, static classes, and SQL from your models
-
Documentation Generation - Create diagrams, HTML docs, JSON schemas, and API specifications
-
AAS Integration - Map between SAMM Aspect Models and Asset Administration Shell submodels