Skip to content

Commit 650c7bc

Browse files
CMRD24CMRDuerr
andauthored
[Extensions] refactoring (#78)
* refactoring (parsing, http, requests, logging) * [bugfix] wrong http request method * bugfixes after refactoring --------- Co-authored-by: Claude Dürr <claude.duerr@iosb.fraunhofer.de>
1 parent 27ff22b commit 650c7bc

16 files changed

Lines changed: 839 additions & 585 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2026 Fraunhofer IOSB, eine rechtlich nicht selbstaendige
3+
* Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten
4+
* Forschung e.V.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.fraunhofer.iosb.ilt.dataspace_consumer.aas_dsp_discovery_extension;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
import java.util.regex.Matcher;
21+
import java.util.regex.Pattern;
22+
23+
import com.fasterxml.jackson.databind.JsonNode;
24+
25+
public class AasDiscoveryParser {
26+
27+
private AasDiscoveryParser() {}
28+
29+
private static final Pattern SUBPROTOCOL_PATTERN =
30+
Pattern.compile("d=([^;]+);[^;=\s]+ndpoint=([^;\"]+)");
31+
32+
private static void extractFromEndpoints(JsonNode endpoints, List<ResultItem> result) {
33+
34+
if (!endpoints.isArray()) {
35+
return;
36+
}
37+
38+
for (JsonNode endpoint : endpoints) {
39+
40+
String interfaceType = endpoint.path("interface").asText(null);
41+
JsonNode proto = endpoint.path("protocolInformation");
42+
43+
String href = proto.path("href").asText(null);
44+
String subBody = proto.path("subprotocolBody").asText(null);
45+
46+
if (href == null || subBody == null) {
47+
continue;
48+
}
49+
50+
Matcher matcher = SUBPROTOCOL_PATTERN.matcher(subBody);
51+
if (!matcher.find()) {
52+
continue;
53+
}
54+
55+
String assetId = matcher.group(1);
56+
String dspEndpoint = matcher.group(2);
57+
58+
ResultItem item = new ResultItem(assetId, dspEndpoint, href, interfaceType);
59+
60+
result.add(item);
61+
}
62+
}
63+
64+
public static List<ResultItem> getResults(JsonNode discoveredInfos) {
65+
List<ResultItem> result = new ArrayList<>();
66+
JsonNode assets = discoveredInfos.path("result");
67+
if (!assets.isArray()) {
68+
return result;
69+
}
70+
71+
for (JsonNode asset : assets) {
72+
73+
// asset-level endpoints
74+
extractFromEndpoints(asset.path("endpoints"), result);
75+
76+
// submodel endpoints
77+
JsonNode submodels = asset.path("submodelDescriptors");
78+
if (submodels.isArray()) {
79+
for (JsonNode submodel : submodels) {
80+
extractFromEndpoints(submodel.path("endpoints"), result);
81+
}
82+
}
83+
}
84+
85+
return result;
86+
}
87+
}

extensions/aas-dsp-discovery-extension/src/main/java/de/fraunhofer/iosb/ilt/dataspace_consumer/aas_dsp_discovery_extension/DiscoveryImpl.java

Lines changed: 9 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,15 @@
1616
package de.fraunhofer.iosb.ilt.dataspace_consumer.aas_dsp_discovery_extension;
1717

1818
import java.io.IOException;
19-
import java.util.ArrayList;
2019
import java.util.HashMap;
21-
import java.util.HashSet;
2220
import java.util.List;
2321
import java.util.Map;
24-
import java.util.Set;
2522
import java.util.concurrent.TimeUnit;
26-
import java.util.logging.Logger;
27-
import java.util.regex.Matcher;
28-
import java.util.regex.Pattern;
2923

3024
import com.fasterxml.jackson.databind.JsonNode;
3125
import com.fasterxml.jackson.databind.ObjectMapper;
3226
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.accessandusagecontrol.AccessRequest;
3327
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.accessandusagecontrol.AccessResponse;
34-
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.accessandusagecontrol.subprotocols.dsp.DSPFilter;
35-
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.accessandusagecontrol.subprotocols.dsp.DSPRequest;
3628
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.config.Configurable;
3729
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.discovery.Discovery;
3830
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.exception.DSCExecuteException;
@@ -57,10 +49,9 @@
5749
public class DiscoveryImpl implements Discovery<JsonNode>, Configurable {
5850

5951
private final ObjectMapper mapper;
52+
private final DiscoveryRequestFactory factory;
6053
private OkHttpClient client;
6154

62-
private static final Logger LOGGER = Logger.getLogger(DiscoveryImpl.class.getName());
63-
6455
private String baseURL;
6556

6657
/**
@@ -71,6 +62,7 @@ public class DiscoveryImpl implements Discovery<JsonNode>, Configurable {
7162
*/
7263
public DiscoveryImpl() {
7364
mapper = new ObjectMapper();
65+
factory = new DiscoveryRequestFactory();
7466
client =
7567
new OkHttpClient.Builder()
7668
.connectTimeout(30, TimeUnit.SECONDS)
@@ -80,72 +72,6 @@ public DiscoveryImpl() {
8072
.build();
8173
}
8274

83-
private static final Pattern SUBPROTOCOL_PATTERN =
84-
Pattern.compile("d=([^;]+);[^;=\s]+ndpoint=([^;\"]+)");
85-
86-
/**
87-
* has a side effect on the endpointToHrefMap
88-
*
89-
* @param endpoints
90-
* @param result
91-
* @return
92-
*/
93-
private void extractFromEndpoints(JsonNode endpoints, List<ResultItem> result) {
94-
95-
if (!endpoints.isArray()) {
96-
return;
97-
}
98-
99-
for (JsonNode endpoint : endpoints) {
100-
101-
String interfaceType = endpoint.path("interface").asText(null);
102-
JsonNode proto = endpoint.path("protocolInformation");
103-
104-
String href = proto.path("href").asText(null);
105-
String subBody = proto.path("subprotocolBody").asText(null);
106-
107-
if (href == null || subBody == null) {
108-
continue;
109-
}
110-
111-
Matcher matcher = SUBPROTOCOL_PATTERN.matcher(subBody);
112-
if (!matcher.find()) {
113-
continue;
114-
}
115-
116-
String assetId = matcher.group(1);
117-
String dspEndpoint = matcher.group(2);
118-
119-
ResultItem item = new ResultItem(assetId, dspEndpoint, href, interfaceType);
120-
121-
result.add(item);
122-
}
123-
}
124-
125-
private List<ResultItem> getResultItems(JsonNode discoveredInfos) {
126-
List<ResultItem> result = new ArrayList<>();
127-
JsonNode assets = discoveredInfos.path("result");
128-
if (!assets.isArray()) {
129-
return result;
130-
}
131-
132-
for (JsonNode asset : assets) {
133-
134-
// asset-level endpoints
135-
extractFromEndpoints(asset.path("endpoints"), result);
136-
137-
// submodel endpoints
138-
JsonNode submodels = asset.path("submodelDescriptors");
139-
if (submodels.isArray()) {
140-
for (JsonNode submodel : submodels) {
141-
extractFromEndpoints(submodel.path("endpoints"), result);
142-
}
143-
}
144-
}
145-
146-
return result;
147-
}
148-
14975
/**
15076
* Build and return the AccessRequest that should be used to perform the discovery call against
15177
* the configured catalog service.
@@ -159,12 +85,7 @@ private List<ResultItem> getResultItems(JsonNode discoveredInfos) {
15985
@Override
16086
public AccessRequest getDiscoveryAccessRequest() throws DSCExecuteException {
16187

162-
DSPFilter filter =
163-
new DSPFilter(
164-
"'http://purl.org/dc/terms/type'.'@id'",
165-
null,
166-
"https://w3id.org/catenax/taxonomy#DigitalTwinRegistry");
167-
return new DSPRequest(filter, baseURL + "/v3/catalog/request");
88+
return factory.getDiscoveryRequest(baseURL);
16889
}
16990

17091
/**
@@ -196,11 +117,12 @@ public JsonNode discover(AccessResponse accessResponse) throws DSCExecuteExcepti
196117
}
197118

198119
ResponseBody body = response.body();
199-
if (body == null) {
120+
String responseBodyString = body.string();
121+
if (responseBodyString.isBlank()) {
200122
throw new IOException("Empty response body");
201123
}
202124

203-
return mapper.readTree(body.string());
125+
return mapper.readTree(responseBodyString);
204126

205127
} catch (IOException e) {
206128

@@ -228,19 +150,8 @@ public JsonNode discover(AccessResponse accessResponse) throws DSCExecuteExcepti
228150
public List<AccessRequest> getGateAccessRequests(JsonNode discoveredInfos)
229151
throws DSCExecuteException {
230152

231-
// we only need one access request per assetId
232-
Set<String> includedAssetIDs = new HashSet<>();
233-
234-
return getResultItems(discoveredInfos).stream()
235-
.filter(x -> includedAssetIDs.add(x.assetId()))
236-
.map(
237-
x -> {
238-
DSPFilter filter =
239-
new DSPFilter(
240-
"https://w3id.org/edc/v0.0.1/ns/id", null, x.assetId());
241-
return (AccessRequest) new DSPRequest(filter, x.endpoint());
242-
})
243-
.toList();
153+
List<ResultItem> items = AasDiscoveryParser.getResults(discoveredInfos);
154+
return factory.getGateAccessRequests(items);
244155
}
245156

246157
/**
@@ -268,7 +179,7 @@ public List<GateRequest> convertToGateRequests(
268179
tokenMap.put((String) response.identifier(), response.token());
269180
}
270181

271-
return getResultItems(discoveredInfos).stream()
182+
return AasDiscoveryParser.getResults(discoveredInfos).stream()
272183
.map(
273184
x -> {
274185
return new GateRequest(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2026 Fraunhofer IOSB, eine rechtlich nicht selbstaendige
3+
* Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten
4+
* Forschung e.V.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.fraunhofer.iosb.ilt.dataspace_consumer.aas_dsp_discovery_extension;
17+
18+
import java.util.HashSet;
19+
import java.util.List;
20+
import java.util.Set;
21+
22+
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.accessandusagecontrol.AccessRequest;
23+
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.accessandusagecontrol.subprotocols.dsp.DSPFilter;
24+
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.accessandusagecontrol.subprotocols.dsp.DSPRequest;
25+
import de.fraunhofer.iosb.ilt.dataspace_consumer.api.exception.DSCExecuteException;
26+
27+
public class DiscoveryRequestFactory {
28+
29+
AccessRequest getDiscoveryRequest(String baseUrl) {
30+
DSPFilter filter =
31+
new DSPFilter(
32+
"'http://purl.org/dc/terms/type'.'@id'",
33+
null,
34+
"https://w3id.org/catenax/taxonomy#DigitalTwinRegistry");
35+
return new DSPRequest(filter, baseUrl + "/v3/catalog/request");
36+
}
37+
38+
public List<AccessRequest> getGateAccessRequests(List<ResultItem> items)
39+
throws DSCExecuteException {
40+
41+
// we only need one access request per assetId
42+
Set<String> includedAssetIDs = new HashSet<>();
43+
44+
return items.stream()
45+
.filter(x -> includedAssetIDs.add(x.assetId()))
46+
.map(
47+
x -> {
48+
DSPFilter filter =
49+
new DSPFilter(
50+
"https://w3id.org/edc/v0.0.1/ns/id", null, x.assetId());
51+
return (AccessRequest) new DSPRequest(filter, x.endpoint());
52+
})
53+
.toList();
54+
}
55+
}

extensions/faaast-gate-extension/src/main/java/de/fraunhofer/iosb/ilt/dataspace_consumer/faaast_gate_extension/GateImpl.java

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,34 @@ public GateImpl() {}
5858

5959
private static final Pattern SUBMODEL_PATTERN = Pattern.compile("^submodel.*");
6060

61+
enum InterfaceType {
62+
AAS_REPOSITORY,
63+
AAS,
64+
SUBMODEL_REPOSITORY,
65+
SUBMODEL,
66+
UNKNOWN;
67+
68+
static InterfaceType from(String value) {
69+
if (value == null) return AAS_REPOSITORY;
70+
if (AAS_REPO_PATTERN.matcher(value).matches()) return AAS_REPOSITORY;
71+
if (AAS_PATTERN.matcher(value).matches()) return AAS;
72+
if (SUBMODEL_REPO_PATTERN.matcher(value).matches()) return SUBMODEL_REPOSITORY;
73+
if (SUBMODEL_PATTERN.matcher(value).matches()) return SUBMODEL;
74+
return UNKNOWN;
75+
}
76+
}
77+
78+
private String normalizeUrl(String url) {
79+
// faaast-client needs urls without the /shells or /submodels suffix:
80+
81+
if (url.endsWith("/shells")) {
82+
url = url.substring(0, url.length() - 7);
83+
} else if (url.endsWith("/submodels")) {
84+
url = url.substring(0, url.length() - 10);
85+
}
86+
return url;
87+
}
88+
6189
@Override
6290
public GateResponse getData(GateRequest gateRequest, List<GateResponseFormat> desiredFormats) {
6391

@@ -68,21 +96,14 @@ public GateResponse getData(GateRequest gateRequest, List<GateResponseFormat> de
6896
try (TokenAuthenticatedHttpClient client =
6997
new TokenAuthenticatedHttpClient(gateRequest.token())) {
7098

71-
String url = gateRequest.url();
72-
73-
// faaast-client needs urls without the /shells or /submodels suffix:
74-
if (url.endsWith("/shells")) {
75-
url = url.substring(0, url.length() - 7);
76-
} else if (url.endsWith("/submodels")) {
77-
url = url.substring(0, url.length() - 10);
78-
}
99+
String url = normalizeUrl(gateRequest.url());
79100

80101
URI aasServerAddressUri = new URI(url);
81102
String interfaceType = null;
82103

83104
Object metaInfo = gateRequest.metaInformation();
84-
if (metaInfo instanceof String) {
85-
interfaceType = ((String) metaInfo).toLowerCase();
105+
if (metaInfo instanceof String metaString) {
106+
interfaceType = metaString.toLowerCase();
86107
}
87108

88109
if (interfaceType == null || AAS_REPO_PATTERN.matcher(interfaceType).matches()) {
@@ -121,7 +142,6 @@ public GateResponse getData(GateRequest gateRequest, List<GateResponseFormat> de
121142
return new GateResponse(200, GateResponseFormat.JSON, headers, payload, "");
122143
} catch (SerializationException exception) {
123144
LOGGER.severe("Failed to process JSON: " + exception.getMessage());
124-
125145
} catch (URISyntaxException exception) {
126146
LOGGER.severe("Invalid AAS server URI: " + exception.getMessage());
127147

0 commit comments

Comments
 (0)