Skip to content

Commit 7c37705

Browse files
committed
Implement custom model resolver as an external command.
1 parent 3a2380a commit 7c37705

22 files changed

+383
-31
lines changed

documentation/developer-guide/modules/tooling-guide/pages/bamm-cli.adoc

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,38 @@ The available options and their meaning can also be seen in the help text of the
3838
| help | Get overview of all commands | `bamm help`
3939
| help <subcommand> | Get help for a specific subcommand | `bamm help aspect`
4040
| aspect help <aspect subcommand> | Get help for `aspect` subcommands | `bamm aspect help validate`
41-
| aspect <model> validate | Validate Aspect Model | `bamm aspect AspectModel.ttl validate`
41+
.2+| aspect <model> validate | Validate Aspect Model | `bamm aspect AspectModel.ttl validate`
42+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements | `bamm aspect AspectModel.ttl validate --custom-resolver myresolver.sh`
4243
.2+| aspect <model> prettyprint | Pretty-print Aspect Model | `bamm aspect AspectModel.ttl prettyprint`
43-
| _--output, -o_ : the output will be saved to the given file | `bamm aspect AspectModel.ttl prettyprint -o c:\Results\PrettyPrinted.ttl`
44+
| _--output, -o_ : the output will be saved to the given file | `bamm aspect AspectModel.ttl prettyprint -o c:\Results\PrettyPrinted.ttl`
4445
.2+| aspect <model> migrate | Migrate Aspect Model to the latest BAMM version | `bamm aspect AspectModel.ttl migrate AspectModel.ttl`
45-
| _--output, -o_ : the output will be saved to the given file | `bamm aspect AspectModel.ttl migrate AspectModel.ttl -o c:\Results\MigratedModel.ttl`
46-
.4+| aspect <model> to html | Generate HTML documentation for an Aspect Model | `bamm aspect AspectModel.ttl to html`
47-
| _--output, -o_ : the output will be saved to the given file | `bamm aspect AspectModel.ttl to html -o c:\Model.html`
48-
| _--css, -c_ : CSS file with custom styles to be included in the generated HTML documentation | `bamm aspect AspectModel.ttl to html -c c:\styles.css`
49-
| _--language, -l_ : The language from the model for which the HTML should be generated (default: en) | `bamm aspect AspectModel.ttl to html -l de`
50-
.3+| aspect <model> to png | Generate PNG diagram for Aspect Model | `bamm aspect AspectModel.ttl to png`
51-
| _--output, -o_ : output file path (default: stdout); as PNG is a binary format, it is strongly recommended to output the result to a file by using the -o option or the console redirection operator '>')|
46+
| _--output, -o_ : the output will be saved to the given file | `bamm aspect AspectModel.ttl migrate AspectModel.ttl -o c:\Results\MigratedModel.ttl`
47+
.5+| aspect <model> to html | Generate HTML documentation for an Aspect Model | `bamm aspect AspectModel.ttl to html`
48+
| _--output, -o_ : the output will be saved to the given file | `bamm aspect AspectModel.ttl to html -o c:\Model.html`
49+
| _--css, -c_ : CSS file with custom styles to be included in the generated HTML documentation | `bamm aspect AspectModel.ttl to html -c c:\styles.css`
50+
| _--language, -l_ : The language from the model for which the HTML should be generated (default: en) | `bamm aspect AspectModel.ttl to html -l de`
51+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements | `bamm aspect AspectModel.ttl to html --custom-resolver myresolver.bat`
52+
.4+| aspect <model> to png | Generate PNG diagram for Aspect Model | `bamm aspect AspectModel.ttl to png`
53+
| _--output, -o_ : output file path (default: stdout); as PNG is a binary format, it is strongly recommended to output the result to a file by using the -o option or the console redirection operator '>')|
5254
| _--language, -l_ : the language from the model for which the diagram should be generated (default: en)|
53-
.3+| aspect <model> to svg | Generate SVG diagram for Aspect Model | `bamm aspect AspectModel.ttl to svg`
55+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements | `bamm aspect AspectModel.ttl to png --custom-resolver resolver.jar`
56+
.4+| aspect <model> to svg | Generate SVG diagram for Aspect Model | `bamm aspect AspectModel.ttl to svg`
5457
| _--output, -o_ : the output will be saved to the given file |
5558
| _--language, -l_ : the language from the model for which the diagram should be generated (default: en)|
56-
.3+| aspect <model> to dot | Generate https://graphviz.org/doc/info/lang.html[DOT] diagram for Aspect Model | `bamm aspect AspectModel.ttl to dot`
59+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements | `bamm aspect AspectModel.ttl to svg --custom-resolver "java -jar resolver.jar"`
60+
.4+| aspect <model> to dot | Generate https://graphviz.org/doc/info/lang.html[DOT] diagram for Aspect Model | `bamm aspect AspectModel.ttl to dot`
5761
| _--output, -o_ : output file path (default: stdout) |
5862
| _--language, -l_ : the language from the model for which the diagram should be generated (default: en)|
59-
.7+| aspect <model> to java | Generate Java classes from an Aspect Model | `bamm aspect AspectModel.ttl to java`
63+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements |
64+
.8+| aspect <model> to java | Generate Java classes from an Aspect Model | `bamm aspect AspectModel.ttl to java`
6065
| _--output-directory, -d_ : output directory to write files to (default: current directory)|
6166
| _--package-name, -pn_ : package to use for generated Java classes| `bamm aspect AspectModel.ttl to java -pn org.company.product`
6267
| _--no-jackson, -nj_ : disable https://github.com/FasterXML/jackson[Jackson] annotation generation in generated Java classes|
6368
| _--template-library-file, -tlf_ : the path and name of the https://velocity.apache.org/[Velocity] template file containing the macro library|
6469
| _--execute-library-macros, -elm_ : Execute the macros provided in the https://velocity.apache.org/[Velocity] macro library|
6570
| _--static, -s_ : generate Java domain classes for a Static Meta Model|
66-
.12+| aspect <model> to openapi | Generate https://spec.openapis.org/oas/v3.0.3[OpenAPI] specification for an Aspect Model | `bamm aspect AspectModel.ttl to openapi -j`
71+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements |
72+
.13+| aspect <model> to openapi | Generate https://spec.openapis.org/oas/v3.0.3[OpenAPI] specification for an Aspect Model | `bamm aspect AspectModel.ttl to openapi -j`
6773
| _--output, -o_ : output file path (default: stdout) |
6874
| _--api-base-url, -b_ : the base url for the Aspect API used in the https://spec.openapis.org/oas/v3.0.3[OpenAPI] specification| `bamm aspect AspectModel.ttl to openapi -j -b \http://mysite.de`
6975
| _--json, -j_ : generate a JSON specification for an Aspect Model (default format is YAML)|
@@ -75,13 +81,17 @@ The available options and their meaning can also be seen in the help text of the
7581
| _--paging-cursor-based, -pc_ : in case there is more than one paging possibility, it has to be cursor based paging|
7682
| _--paging-offset-based, -po_ : in case there is more than one paging possibility, it has to be offset based paging|
7783
| _--paging-time-based, -pt_ : in case there is more than one paging possibility, it has to be time based paging|
78-
.2+| aspect <model> to json | Generate OpenAPI JSON specification for an Aspect Model | `bamm aspect AspectModel.ttl to json`
84+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements |
85+
.3+| aspect <model> to json | Generate OpenAPI JSON specification for an Aspect Model | `bamm aspect AspectModel.ttl to json`
7986
| _--output, -o_ : output file path (default: stdout) |
80-
.2+| aspect <model> to schema | Generate JSON schema for an Aspect Model | `bamm aspect AspectModel.ttl to schema`
87+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements |
88+
.3+| aspect <model> to schema | Generate JSON schema for an Aspect Model | `bamm aspect AspectModel.ttl to schema`
8189
| _--output, -o_ : output file path (default: stdout) |
82-
.3+| aspect <model> to aas | Generate Asset Administration Shell (AAS) submodel template for an Aspect Model | `bamm aspect AspectModel.ttl to aas`
90+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements |
91+
.4+| aspect <model> to aas | Generate Asset Administration Shell (AAS) submodel template for an Aspect Model | `bamm aspect AspectModel.ttl to aas`
8392
| _--output, -o_ : output file path (default: stdout) |
8493
| _--format, -f_ : output file format (xml or aasx, default: xml)|
94+
| _--custom-resolver_ : use an external resolver for the resolution of the model elements |
8595
|===
8696

8797
== Using the CLI to create a JSON OpenAPI Specification

tools/bamm-cli/pom.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,28 @@
215215
<forceJavacCompilerUse>true</forceJavacCompilerUse>
216216
</configuration>
217217
</plugin>
218+
219+
<!-- for testing of the command executor, we need a test JAR file -->
220+
<plugin>
221+
<groupId>org.apache.maven.plugins</groupId>
222+
<artifactId>maven-jar-plugin</artifactId>
223+
<version>3.2.2</version>
224+
<configuration>
225+
<archive>
226+
<manifest>
227+
<mainClass>io.openmanufacturing.sds.JavaCmdLine</mainClass>
228+
</manifest>
229+
</archive>
230+
</configuration>
231+
<executions>
232+
<execution>
233+
<goals>
234+
<goal>test-jar</goal>
235+
</goals>
236+
</execution>
237+
</executions>
238+
</plugin>
239+
218240
</plugins>
219241
</build>
220242
</project>

tools/bamm-cli/src/main/java/io/openmanufacturing/sds/AbstractCommand.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ protected Try<VersionedModel> loadAndResolveModel( final File input ) {
5858
new AspectModelResolver().resolveAspectModel( new FileSystemStrategy( modelsRoot ), urn ) );
5959
}
6060

61+
protected Try<VersionedModel> loadAndResolveModel( final File input, final ExternalResolverMixin resolverConfig ) {
62+
if ( resolverConfig.commandLine.isBlank() ) {
63+
return loadAndResolveModel( input );
64+
}
65+
final File inputFile = input.getAbsoluteFile();
66+
final AspectModelUrn urn = fileToUrn( inputFile );
67+
return new AspectModelResolver().resolveAspectModel( new ExternalResolverStrategy( resolverConfig.commandLine ), urn );
68+
}
69+
6170
protected Try<Path> getModelRoot( final File inputFile ) {
6271
return Option.of( Paths.get( inputFile.getParent(), "..", ".." ) )
6372
.map( Path::toFile )
@@ -98,9 +107,9 @@ protected Try<VersionedModel> loadButNotResolveModel( final File inputFile ) {
98107
}
99108
}
100109

101-
protected VersionedModel loadModelOrFail( final String modelFileName ) {
110+
protected VersionedModel loadModelOrFail( final String modelFileName, final ExternalResolverMixin resolverConfig ) {
102111
final File inputFile = new File( modelFileName );
103-
final Try<VersionedModel> versionedModel = loadAndResolveModel( inputFile );
112+
final Try<VersionedModel> versionedModel = loadAndResolveModel( inputFile, resolverConfig );
104113
return versionedModel.recover( throwable -> {
105114
// Model can not be loaded, root cause e.g. File not found
106115
if ( throwable instanceof IllegalArgumentException ) {
@@ -124,8 +133,8 @@ protected VersionedModel loadModelOrFail( final String modelFileName ) {
124133
}
125134

126135
protected void generateDiagram( final String inputFileName, final AspectModelDiagramGenerator.Format targetFormat, final String outputFileName,
127-
final String languageTag ) throws IOException {
128-
final VersionedModel model = loadModelOrFail( inputFileName );
136+
final String languageTag, final ExternalResolverMixin resolverConfig ) throws IOException {
137+
final VersionedModel model = loadModelOrFail( inputFileName, resolverConfig );
129138
final AspectModelDiagramGenerator generator = new AspectModelDiagramGenerator( model );
130139
final Set<AspectModelDiagramGenerator.Format> targetFormats = new HashSet<>();
131140
targetFormats.add( targetFormat );
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
3+
*
4+
* See the AUTHORS file(s) distributed with this work for additional
5+
* information regarding authorship.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
10+
*
11+
* SPDX-License-Identifier: MPL-2.0
12+
*/
13+
14+
package io.openmanufacturing.sds;
15+
16+
import java.io.IOException;
17+
import java.util.Scanner;
18+
import java.util.StringTokenizer;
19+
20+
import io.openmanufacturing.sds.exception.CommandException;
21+
22+
public class CommandExecutor {
23+
24+
public static String executeCommand( String command ) {
25+
// convenience: if just the name of the jar is given, expand to the proper java invocation command
26+
if ( isJarInvocation( command ) ) {
27+
command = "java -jar " + command;
28+
}
29+
30+
try {
31+
final Process p = Runtime.getRuntime().exec( command );
32+
final int result = p.waitFor();
33+
final Scanner s = new Scanner( p.getInputStream() ).useDelimiter( "\\A" );
34+
final String output = s.hasNext() ? s.next() : "";
35+
if ( result != 0 ) {
36+
throw new CommandException( output );
37+
}
38+
return output;
39+
} catch ( final IOException | InterruptedException e ) {
40+
throw new CommandException( e );
41+
}
42+
}
43+
44+
private static boolean isJarInvocation( final String command ) {
45+
final StringTokenizer st = new StringTokenizer( command, " " );
46+
if ( st.hasMoreTokens() ) {
47+
return st.nextToken().toUpperCase().endsWith( ".JAR" );
48+
}
49+
return false;
50+
}
51+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
3+
*
4+
* See the AUTHORS file(s) distributed with this work for additional
5+
* information regarding authorship.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
10+
*
11+
* SPDX-License-Identifier: MPL-2.0
12+
*/
13+
14+
package io.openmanufacturing.sds;
15+
16+
import picocli.CommandLine;
17+
18+
// Configuration of the external command that can be executed to custom resolve a model. The command will be executed
19+
// as given, with the model URN to resolve added as the last parameter automatically by the program logic.
20+
public class ExternalResolverMixin {
21+
@CommandLine.Option( names = { "--custom-resolver" }, description = "External command to execute to produce the custom model resolution." )
22+
protected String commandLine = "";
23+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2022 Robert Bosch Manufacturing Solutions GmbH
3+
*
4+
* See the AUTHORS file(s) distributed with this work for additional
5+
* information regarding authorship.
6+
*
7+
* This Source Code Form is subject to the terms of the Mozilla Public
8+
* License, v. 2.0. If a copy of the MPL was not distributed with this
9+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
10+
*
11+
* SPDX-License-Identifier: MPL-2.0
12+
*/
13+
14+
package io.openmanufacturing.sds;
15+
16+
import java.io.ByteArrayInputStream;
17+
import java.nio.charset.StandardCharsets;
18+
19+
import org.apache.jena.rdf.model.Model;
20+
21+
import io.openmanufacturing.sds.aspectmodel.resolver.ResolutionStrategy;
22+
import io.openmanufacturing.sds.aspectmodel.resolver.services.TurtleLoader;
23+
import io.openmanufacturing.sds.aspectmodel.urn.AspectModelUrn;
24+
import io.vavr.control.Try;
25+
26+
class ExternalResolverStrategy implements ResolutionStrategy {
27+
28+
private final String command;
29+
30+
ExternalResolverStrategy( final String command ) {
31+
this.command = command;
32+
}
33+
34+
@Override
35+
public Try<Model> apply( final AspectModelUrn aspectModelUrn ) {
36+
final String commandWithParameters = command + " " + aspectModelUrn.toString();
37+
final String result = CommandExecutor.executeCommand( commandWithParameters );
38+
if ( null == result ) {
39+
return Try.failure( new Exception() );
40+
}
41+
// TODO encodings
42+
return TurtleLoader.loadTurtle( new ByteArrayInputStream( result.getBytes( StandardCharsets.UTF_8 ) ) );
43+
}
44+
}

tools/bamm-cli/src/main/java/io/openmanufacturing/sds/aspect/AspectValidateCommand.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.topbraid.shacl.util.FailureLog;
1919

2020
import io.openmanufacturing.sds.AbstractCommand;
21+
import io.openmanufacturing.sds.ExternalResolverMixin;
2122
import io.openmanufacturing.sds.aspectmodel.resolver.services.VersionedModel;
2223
import io.openmanufacturing.sds.aspectmodel.validation.report.ValidationReport;
2324
import io.openmanufacturing.sds.aspectmodel.validation.services.AspectModelValidator;
@@ -39,6 +40,9 @@ public class AspectValidateCommand extends AbstractCommand {
3940
@CommandLine.ParentCommand
4041
private AspectCommand parentCommand;
4142

43+
@CommandLine.Mixin
44+
private ExternalResolverMixin customResolver;
45+
4246
@Override
4347
public void run() {
4448
FailureLog.set( new FailureLog() {
@@ -48,7 +52,7 @@ public void logFailure( final String message ) {
4852
}
4953
} );
5054

51-
final Try<VersionedModel> versionedModel = loadAndResolveModel( new File( parentCommand.getInput() ) );
55+
final Try<VersionedModel> versionedModel = loadAndResolveModel( new File( parentCommand.getInput() ), customResolver );
5256
final AspectModelValidator validator = new AspectModelValidator();
5357
final ValidationReport report = validator.validate( versionedModel );
5458

tools/bamm-cli/src/main/java/io/openmanufacturing/sds/aspect/to/AspectToAasCommand.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.io.IOException;
1717

1818
import io.openmanufacturing.sds.AbstractCommand;
19+
import io.openmanufacturing.sds.ExternalResolverMixin;
1920
import io.openmanufacturing.sds.aspect.AspectToCommand;
2021
import io.openmanufacturing.sds.aspectmodel.aas.AspectModelAASGenerator;
2122
import io.openmanufacturing.sds.exception.CommandException;
@@ -56,12 +57,15 @@ public class AspectToAasCommand extends AbstractCommand {
5657
@CommandLine.ParentCommand
5758
private AspectToCommand parentCommand;
5859

60+
@CommandLine.Mixin
61+
private ExternalResolverMixin customResolver;
62+
5963
@Override
6064
public void run() {
6165
final AspectModelAASGenerator generator = new AspectModelAASGenerator();
6266
final Aspect aspect =
6367
AspectModelLoader.fromVersionedModelUnchecked(
64-
loadModelOrFail( parentCommand.parentCommand.getInput() ) );
68+
loadModelOrFail( parentCommand.parentCommand.getInput(), customResolver ) );
6569
try {
6670
// we intentionally override the name of the generated artifact here to the name explicitly
6771
// desired by the user (outputFilePath), as opposed to what the model thinks it should be

0 commit comments

Comments
 (0)