Skip to content

Commit 2f374eb

Browse files
geso02FriedJannik
andauthored
Automatic Submodel Service Registry Integration (#664)
* Add submodel-service registristry integration feature * Trigger build * Try to fix javadoc * Add proper submodel service descriptor href generation * Try to fix javadoc * Register relevant CORS patterns * Remove duplicate entry with submodel-service-http with no classifier * Add newlines and update License version * Apply suggestions from code review Co-authored-by: Jannik Fried <Jannik.Fried@iese.fraunhofer.de> --------- Co-authored-by: Jannik Fried <Jannik.Fried@iese.fraunhofer.de>
1 parent bce6078 commit 2f374eb

24 files changed

Lines changed: 1204 additions & 24 deletions

File tree

basyx.submodelregistry/basyx.submodelregistry-client-native/src/main/java/org/eclipse/digitaltwin/basyx/submodelregistry/client/factory/SubmodelDescriptorFactory.java

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ public class SubmodelDescriptorFactory {
5353
private static final String SUBMODEL_INTERFACE = "SUBMODEL-3.0";
5454
private static final String SUBMODEL_REPOSITORY_PATH = "submodels";
5555

56-
private final List<String> submodelRepositoryURLs;
56+
private final List<String> submodelBaseURLs;
5757
private static AttributeMapper attributeMapper;
5858

59-
public SubmodelDescriptorFactory(List<String> submodelRepositoryBaseURLs, AttributeMapper attributeMapper) {
60-
this.submodelRepositoryURLs = createSubmodelRepositoryUrls(submodelRepositoryBaseURLs);
59+
public SubmodelDescriptorFactory(List<String> submodelBaseURLs, AttributeMapper attributeMapper) {
60+
this.submodelBaseURLs = createSubmodelServerUrls(submodelBaseURLs);
6161
SubmodelDescriptorFactory.attributeMapper = attributeMapper;
6262
}
63-
63+
6464
/**
6565
* Creates {@link SubmodelDescriptor}
6666
*
@@ -74,7 +74,7 @@ public SubmodelDescriptor create(Submodel submodel) {
7474

7575
setIdShort(submodel.getIdShort(), descriptor);
7676

77-
setEndpointItem(submodel.getId(), descriptor, submodelRepositoryURLs);
77+
setEndpointItem(submodel.getId(), descriptor, submodelBaseURLs);
7878

7979
setDescription(submodel.getDescription(), descriptor);
8080

@@ -91,68 +91,68 @@ public SubmodelDescriptor create(Submodel submodel) {
9191
return descriptor;
9292
}
9393

94-
private static void setDescription(List<LangStringTextType> descriptions, SubmodelDescriptor descriptor) {
94+
private void setDescription(List<LangStringTextType> descriptions, SubmodelDescriptor descriptor) {
9595

9696
if (descriptions == null || descriptions.isEmpty())
9797
return;
9898

9999
descriptor.setDescription(attributeMapper.mapDescription(descriptions));
100100
}
101101

102-
private static void setDisplayName(List<LangStringNameType> displayNames, SubmodelDescriptor descriptor) {
102+
private void setDisplayName(List<LangStringNameType> displayNames, SubmodelDescriptor descriptor) {
103103

104104
if (displayNames == null || displayNames.isEmpty())
105105
return;
106106

107107
descriptor.setDisplayName(attributeMapper.mapDisplayName(displayNames));
108108
}
109109

110-
private static void setExtensions(List<Extension> extensions, SubmodelDescriptor descriptor) {
110+
private void setExtensions(List<Extension> extensions, SubmodelDescriptor descriptor) {
111111

112112
if (extensions == null || extensions.isEmpty())
113113
return;
114114

115115
descriptor.setExtensions(attributeMapper.mapExtensions(extensions));
116116
}
117117

118-
private static void setAdministration(AdministrativeInformation administration, SubmodelDescriptor descriptor) {
118+
private void setAdministration(AdministrativeInformation administration, SubmodelDescriptor descriptor) {
119119

120120
if (administration == null)
121121
return;
122122

123123
descriptor.setAdministration(attributeMapper.mapAdministration(administration));
124124
}
125125

126-
private static void setSemanticId(Reference reference, SubmodelDescriptor descriptor) {
126+
private void setSemanticId(Reference reference, SubmodelDescriptor descriptor) {
127127

128128
if (reference == null)
129129
return;
130130

131131
descriptor.setSemanticId(attributeMapper.mapSemanticId(reference));
132132
}
133133

134-
private static void setSupplementalSemanticId(List<Reference> supplementalSemanticIds, SubmodelDescriptor descriptor) {
134+
private void setSupplementalSemanticId(List<Reference> supplementalSemanticIds, SubmodelDescriptor descriptor) {
135135

136136
if (supplementalSemanticIds == null || supplementalSemanticIds.isEmpty())
137137
return;
138138

139139
descriptor.setSupplementalSemanticId(attributeMapper.mapSupplementalSemanticId(supplementalSemanticIds));
140140
}
141141

142-
private static void setEndpointItem(String shellId, SubmodelDescriptor descriptor, List<String> submodelRepositoryURLs) {
142+
private void setEndpointItem(String submodelId, SubmodelDescriptor descriptor, List<String> submodelServerURLs) {
143143

144-
for (String eachUrl : submodelRepositoryURLs) {
144+
for (String eachUrl : submodelServerURLs) {
145145
Endpoint endpoint = new Endpoint();
146146
endpoint.setInterface(SUBMODEL_INTERFACE);
147-
ProtocolInformation protocolInformation = createProtocolInformation(shellId, eachUrl);
147+
ProtocolInformation protocolInformation = createProtocolInformation(submodelId, eachUrl);
148148
endpoint.setProtocolInformation(protocolInformation);
149149

150150
descriptor.addEndpointsItem(endpoint);
151151
}
152152
}
153153

154-
private static ProtocolInformation createProtocolInformation(String shellId, String url) {
155-
String href = String.format("%s/%s", url, Base64UrlEncodedIdentifier.encodeIdentifier(shellId));
154+
private ProtocolInformation createProtocolInformation(String submodelId, String url) {
155+
String href = createHref(submodelId, url);
156156

157157
ProtocolInformation protocolInformation = new ProtocolInformation();
158158
protocolInformation.endpointProtocol(getProtocol(href));
@@ -161,27 +161,36 @@ private static ProtocolInformation createProtocolInformation(String shellId, Str
161161
return protocolInformation;
162162
}
163163

164-
private static void setIdShort(String idShort, SubmodelDescriptor descriptor) {
164+
protected String createHref(String submodelId, String url) {
165+
return String.format("%s/%s", url, Base64UrlEncodedIdentifier.encodeIdentifier(submodelId));
166+
}
167+
168+
169+
private void setIdShort(String idShort, SubmodelDescriptor descriptor) {
165170
descriptor.setIdShort(idShort);
166171
}
167172

168-
private static void setId(String shellId, SubmodelDescriptor descriptor) {
173+
private void setId(String shellId, SubmodelDescriptor descriptor) {
169174
descriptor.setId(shellId);
170175
}
171176

172-
private static String getProtocol(String endpoint) {
177+
private String getProtocol(String endpoint) {
173178
try {
174179
return new URL(endpoint).getProtocol();
175180
} catch (MalformedURLException e) {
176181
throw new RuntimeException();
177182
}
178183
}
179184

180-
private static List<String> createSubmodelRepositoryUrls(List<String> submodelRepositoryBaseURLs) {
185+
private List<String> createSubmodelServerUrls(List<String> submodelRepositoryBaseURLs) {
181186
List<String> toReturn = new ArrayList<>(submodelRepositoryBaseURLs.size());
182187
for (String eachUrl : submodelRepositoryBaseURLs) {
183-
toReturn.add(RepositoryUrlHelper.createRepositoryUrl(eachUrl, SUBMODEL_REPOSITORY_PATH));
188+
toReturn.add(RepositoryUrlHelper.createRepositoryUrl(eachUrl, getSubmodelPathPrefix()));
184189
}
185190
return toReturn;
186191
}
192+
193+
protected String getSubmodelPathPrefix() {
194+
return SUBMODEL_REPOSITORY_PATH;
195+
}
187196
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*******************************************************************************
2+
* Copyright (C) 2025 the Eclipse BaSyx Authors
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining
5+
* a copy of this software and associated documentation files (the
6+
* "Software"), to deal in the Software without restriction, including
7+
* without limitation the rights to use, copy, modify, merge, publish,
8+
* distribute, sublicense, and/or sell copies of the Software, and to
9+
* permit persons to whom the Software is furnished to do so, subject to
10+
* the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be
13+
* included in all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19+
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21+
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22+
*
23+
* SPDX-License-Identifier: MIT
24+
******************************************************************************/
25+
26+
package org.eclipse.digitaltwin.basyx.submodelregistry.client.factory;
27+
28+
import java.util.List;
29+
30+
import org.eclipse.digitaltwin.basyx.submodelregistry.client.mapper.AttributeMapper;
31+
import org.eclipse.digitaltwin.basyx.submodelregistry.client.model.SubmodelDescriptor;
32+
33+
/**
34+
* Factory for creating the SubmodelDescriptor for submodel services
35+
*
36+
* @author Gerhard Sonnenberg (DFKI GmbH)
37+
*/
38+
public class SubmodelServiceDescriptorFactory extends SubmodelDescriptorFactory {
39+
40+
private static final String SUBMODEL_SERVICE_PATH = "submodel";
41+
42+
public SubmodelServiceDescriptorFactory(List<String> submodelBaseURLs, AttributeMapper attributeMapper) {
43+
super(submodelBaseURLs, attributeMapper);
44+
}
45+
46+
@Override
47+
protected String getSubmodelPathPrefix() {
48+
return SUBMODEL_SERVICE_PATH;
49+
}
50+
51+
@Override
52+
protected String createHref(String submodelId, String url) {
53+
// the submodelId is not relevant for a submodel service
54+
// the href just ends with /submodel
55+
return url;
56+
}
57+
}

basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ public interface SubmodelServiceFactory {
5151
* @return the created SubmodelService
5252
*/
5353
public SubmodelService create(String submodelId);
54+
5455
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
2+
# SubmodelService Registry Integration
3+
4+
This feature automatically registers the Submodel Descriptor with the configured Submodel Registry upon startup and automatically deregisters it upon shutdown of the Submodel Service.
5+
6+
## Configuration
7+
8+
To enable the integration feature, configure the following properties in your application:
9+
10+
```properties
11+
basyx.submodelservice.feature.registryintegration={SM-Registry-Base-Url}
12+
basyx.externalurl={AAS-External-Service-Base-Url}
13+
```
14+
15+
**Example:**
16+
```properties
17+
basyx.submodelservice.feature.registryintegration=http://localhost:8060
18+
basyx.externalurl=http://localhost:8081
19+
```
20+
21+
## Authorization Support
22+
23+
If the target Submodel Registry requires authorization, enable and configure it as follows:
24+
25+
```properties
26+
basyx.submodelservice.feature.registryintegration.authorization.enabled=true
27+
basyx.submodelservice.feature.registryintegration.authorization.client-id=<client-id>
28+
basyx.submodelservice.feature.registryintegration.authorization.client-secret=<client-secret>
29+
basyx.submodelservice.feature.registryintegration.authorization.token-endpoint=http://localhost:9090/oauth/token
30+
```
31+
32+
If the OAuth2 grant type is **password**, additionally specify:
33+
34+
```properties
35+
basyx.submodelservice.feature.registryintegration.authorization.username=<username>
36+
basyx.submodelservice.feature.registryintegration.authorization.password=<password>
37+
```
38+
39+
### Example Configuration
40+
41+
A typical setup without authorization:
42+
43+
```properties
44+
basyx.submodelservice.feature.registryintegration=http://localhost:8060
45+
basyx.externalurl=http://localhost:8081
46+
```
47+
48+
A setup **with authorization** enabled might look like:
49+
50+
```properties
51+
basyx.submodelservice.feature.registryintegration=http://localhost:8060
52+
basyx.externalurl=http://localhost:8081
53+
54+
basyx.submodelservice.feature.registryintegration.authorization.enabled=true
55+
basyx.submodelservice.feature.registryintegration.authorization.token-endpoint=http://auth-server/token
56+
basyx.submodelservice.feature.registryintegration.authorization.client-id=myClientId
57+
basyx.submodelservice.feature.registryintegration.authorization.client-secret=mySecret
58+
basyx.submodelservice.feature.registryintegration.authorization.username=user
59+
basyx.submodelservice.feature.registryintegration.authorization.password=pass
60+
```
61+
62+
**Note:**
63+
- The feature requires `basyx.externalurl` to be set explicitly to provide the correct external reference URL for the Submodel Service.
64+
- If both properties are configured correctly, the integration is automatically activated.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
6+
<parent>
7+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
8+
<artifactId>basyx.submodelservice</artifactId>
9+
<version>${revision}</version>
10+
</parent>
11+
12+
<artifactId>basyx.submodelservice-feature-registry-integration</artifactId>
13+
<name>BaSyx Submodel Service feature-registry-integration</name>
14+
<description>BaSyx Submodel Service feature-registry-integration</description>
15+
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
19+
<artifactId>basyx.submodelregistry-client-native</artifactId>
20+
</dependency>
21+
<dependency>
22+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
23+
<artifactId>basyx.submodelservice-core</artifactId>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
27+
<artifactId>basyx.http</artifactId>
28+
<scope>test</scope>
29+
<classifier>tests</classifier>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
33+
<artifactId>basyx.http</artifactId>
34+
</dependency>
35+
<dependency>
36+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
37+
<artifactId>basyx.submodelservice-backend</artifactId>
38+
<scope>test</scope>
39+
</dependency>
40+
<dependency>
41+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
42+
<artifactId>basyx.submodelregistry-client-native</artifactId>
43+
<classifier>tests</classifier>
44+
<scope>test</scope>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
48+
<artifactId>basyx.submodelservice-http</artifactId>
49+
<classifier>tests</classifier>
50+
<scope>test</scope>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
54+
<artifactId>basyx.filerepository-backend-inmemory</artifactId>
55+
<scope>test</scope>
56+
</dependency>
57+
<dependency>
58+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
59+
<artifactId>basyx.submodelservice-backend-inmemory</artifactId>
60+
<scope>test</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>org.eclipse.digitaltwin.basyx</groupId>
64+
<artifactId>basyx.submodelservice-backend-inmemory</artifactId>
65+
<scope>test</scope>
66+
</dependency>
67+
<dependency>
68+
<groupId>org.apache.httpcomponents.client5</groupId>
69+
<artifactId>httpclient5</artifactId>
70+
<scope>test</scope>
71+
</dependency>
72+
<dependency>
73+
<groupId>org.mockito</groupId>
74+
<artifactId>mockito-core</artifactId>
75+
<scope>test</scope>
76+
</dependency>
77+
<dependency>
78+
<groupId>org.springframework.boot</groupId>
79+
<artifactId>spring-boot-starter-test</artifactId>
80+
<scope>test</scope>
81+
</dependency>
82+
</dependencies>
83+
</project>

0 commit comments

Comments
 (0)