From 14d05d243907e3dba9ae4a937ac12250d52ac4a2 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Mon, 27 Nov 2023 13:08:02 +0100 Subject: [PATCH 01/20] Make extraction --- client/build.gradle.kts | 46 ++++++ .../iosb}/client/ClientEndpoint.java | 62 ++++---- .../iosb/client/ClientExtension.java | 128 +++++++++++++++++ .../CustomAuthenticationRequestFilter.java | 81 +++++++++++ .../dataTransfer/DataTransferEndpoint.java | 18 +-- .../dataTransfer/DataTransferObservable.java | 13 +- .../dataTransfer/TransferInitiator.java | 15 +- .../exception/AmbiguousOrNullException.java | 2 +- .../ClientContractNegotiationListener.java | 2 +- .../iosb}/client/negotiation/Negotiator.java | 12 +- .../client/policy}/PolicyDefinitionStore.java | 39 ++--- .../iosb/client/policy}/PolicyService.java | 79 ++++++----- .../de/fraunhofer/iosb/client/util/Pair.java | 70 +++++++++ ...rg.eclipse.edc.spi.system.ServiceExtension | 1 + .../iosb}/client/ClientEndpointTest.java | 31 ++-- .../dataTransfer/TransferInitiatorTest.java | 6 +- .../client/negotiation/NegotiatorTest.java | 134 ++++++++++++++++++ .../client/policy}/PolicyServiceTest.java | 31 ++-- .../iosb/client/testUtils/FileManager.java | 46 ++++++ .../src/test/resources/catalog.json | 0 edc-extension4aas/build.gradle.kts | 2 - .../de/fraunhofer/iosb/app/AasExtension.java | 117 ++------------- .../CustomAuthenticationRequestFilter.java | 37 +---- ...CustomAuthenticationRequestFilterTest.java | 1 - .../client/negotiation/NegotiatorTest.java | 131 ----------------- settings.gradle.kts | 1 + 26 files changed, 687 insertions(+), 418 deletions(-) create mode 100644 client/build.gradle.kts rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app => client/src/main/java/de/fraunhofer/iosb}/client/ClientEndpoint.java (84%) create mode 100644 client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java create mode 100644 client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app => client/src/main/java/de/fraunhofer/iosb}/client/dataTransfer/DataTransferEndpoint.java (79%) rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app => client/src/main/java/de/fraunhofer/iosb}/client/dataTransfer/DataTransferObservable.java (90%) rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app => client/src/main/java/de/fraunhofer/iosb}/client/dataTransfer/TransferInitiator.java (93%) rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app => client/src/main/java/de/fraunhofer/iosb}/client/exception/AmbiguousOrNullException.java (95%) rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app => client/src/main/java/de/fraunhofer/iosb}/client/negotiation/ClientContractNegotiationListener.java (98%) rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app => client/src/main/java/de/fraunhofer/iosb}/client/negotiation/Negotiator.java (93%) rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract => client/src/main/java/de/fraunhofer/iosb/client/policy}/PolicyDefinitionStore.java (76%) rename {edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract => client/src/main/java/de/fraunhofer/iosb/client/policy}/PolicyService.java (89%) create mode 100644 client/src/main/java/de/fraunhofer/iosb/client/util/Pair.java create mode 100644 client/src/main/resources/org.eclipse.edc.spi.system.ServiceExtension rename {edc-extension4aas/src/test/java/de/fraunhofer/iosb/app => client/src/test/java/de/fraunhofer/iosb}/client/ClientEndpointTest.java (90%) rename {edc-extension4aas/src/test/java/de/fraunhofer/iosb/app => client/src/test/java/de/fraunhofer/iosb}/client/dataTransfer/TransferInitiatorTest.java (95%) create mode 100644 client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java rename {edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/contract => client/src/test/java/de/fraunhofer/iosb/client/policy}/PolicyServiceTest.java (92%) create mode 100644 client/src/test/java/de/fraunhofer/iosb/client/testUtils/FileManager.java rename {edc-extension4aas => client}/src/test/resources/catalog.json (100%) delete mode 100644 edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/negotiation/NegotiatorTest.java diff --git a/client/build.gradle.kts b/client/build.gradle.kts new file mode 100644 index 00000000..8e6f6115 --- /dev/null +++ b/client/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + `java-library` + jacoco +} + +val javaVersion: String by project +val edcVersion: String by project +val rsApi: String by project +val mockitoVersion: String by project +val mockserverVersion: String by project +val metaModelVersion: String by project + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(javaVersion)) + } +} + +dependencies { + // See this project's README.MD for explanations + implementation("$group:contract-core:$edcVersion") + implementation("$group:dsp-catalog-http-dispatcher:$edcVersion") + implementation("$group:management-api:$edcVersion") + implementation("$group:runtime-metamodel:$edcVersion") + implementation("$group:data-plane-http-spi:$edcVersion") // HttpDataAddress + + implementation("jakarta.ws.rs:jakarta.ws.rs-api:${rsApi}") + + testImplementation("$group:junit:$edcVersion") + testImplementation("org.glassfish.jersey.core:jersey-common:3.1.3") + testImplementation("org.mockito:mockito-core:${mockitoVersion}") + testImplementation("org.mock-server:mockserver-junit-jupiter:${mockserverVersion}") + testImplementation("org.mock-server:mockserver-netty:${mockserverVersion}") +} + +repositories { + mavenCentral() +} + +tasks.test { + useJUnitPlatform() +} + +tasks.jacocoTestReport { + dependsOn(tasks.test) // tests are required to run before generating the report +} diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/ClientEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java similarity index 84% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/ClientEndpoint.java rename to client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java index 93ea75ff..6c280003 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/ClientEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java @@ -1,19 +1,4 @@ -/* - * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige - * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten - * Forschung e.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.fraunhofer.iosb.app.client; +package de.fraunhofer.iosb.client; import static java.lang.String.format; import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; @@ -26,14 +11,14 @@ import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.eclipse.edc.spi.types.domain.offer.ContractOffer; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.client.contract.PolicyService; -import de.fraunhofer.iosb.app.client.dataTransfer.TransferInitiator; -import de.fraunhofer.iosb.app.client.negotiation.Negotiator; -import de.fraunhofer.iosb.app.util.Pair; +import de.fraunhofer.iosb.client.dataTransfer.TransferInitiator; +import de.fraunhofer.iosb.client.negotiation.Negotiator; +import de.fraunhofer.iosb.client.policy.PolicyService; +import de.fraunhofer.iosb.client.util.Pair; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; import jakarta.ws.rs.GET; @@ -52,7 +37,6 @@ @Produces({ MediaType.APPLICATION_JSON }) @Path(ClientEndpoint.AUTOMATED_PATH) public class ClientEndpoint { - /* * Root path for the client */ @@ -64,11 +48,11 @@ public class ClientEndpoint { private static final String NEGOTIATE_PATH = "negotiate"; private static final String TRANSFER_PATH = "transfer"; - private static final Logger LOGGER = Logger.getInstance(); + private final Monitor monitor; private final Negotiator negotiator; - private final TransferInitiator transferInitiator; private final PolicyService policyService; + private final TransferInitiator transferInitiator; /** * Initialize a client endpoint. @@ -78,11 +62,12 @@ public class ClientEndpoint { * @param negotiator Send contract offer, negotiation status watch. * @param transferInitiator Initiate transfer requests. */ - public ClientEndpoint(PolicyService policyService, - Negotiator negotiator, + public ClientEndpoint(Monitor monitor, Negotiator negotiator, PolicyService policyService, TransferInitiator transferInitiator) { - this.policyService = policyService; + this.monitor = monitor; + this.negotiator = negotiator; + this.policyService = policyService; this.transferInitiator = transferInitiator; } @@ -103,7 +88,7 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, @QueryParam("providerId") String providerId, @QueryParam("assetId") String assetId, @QueryParam("dataDestinationUrl") URL dataDestinationUrl) { - LOGGER.debug(format("Received a %s POST request", NEGOTIATE_PATH)); + monitor.debug(format("[Client] Received a %s POST request", NEGOTIATE_PATH)); Objects.requireNonNull(providerUrl, "Provider URL must not be null"); Objects.requireNonNull(assetId, "Asset ID must not be null"); @@ -111,7 +96,7 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, try { idPolicyPair = policyService.getAcceptablePolicyForAssetId(providerUrl, assetId); } catch (InterruptedException negotiationException) { - LOGGER.error(format("Getting policies failed for provider %s and asset %s", providerUrl, + monitor.severe(format("[Client] Getting policies failed for provider %s and asset %s", providerUrl, assetId), negotiationException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) .build(); @@ -134,7 +119,7 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, try { agreement = negotiator.negotiate(contractRequest); } catch (InterruptedException | ExecutionException negotiationException) { - LOGGER.error(format("Negotiation failed for provider %s and contractOffer %s", providerUrl, + monitor.severe(format("[Client] Negotiation failed for provider %s and contractOffer %s", providerUrl, offer.getId()), negotiationException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) .build(); @@ -162,7 +147,7 @@ public Response getDataset(@QueryParam("providerUrl") URL providerUrl, var datasets = policyService.getDatasetForAssetId(providerUrl, assetId); return Response.ok(datasets).build(); } catch (InterruptedException interruptedException) { - LOGGER.error(format("Getting datasets failed for provider %s and asset %s", providerUrl, + monitor.severe(format("[Client] Getting datasets failed for provider %s and asset %s", providerUrl, assetId), interruptedException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(interruptedException.getMessage()) .build(); @@ -184,8 +169,8 @@ public Response negotiateContract(ContractRequest contractRequest) { var agreement = negotiator.negotiate(contractRequest); return Response.ok(agreement).build(); } catch (InterruptedException | ExecutionException negotiationException) { - LOGGER.error( - format("Negotiation failed for provider %s and contractRequest %s", contractRequest.getProviderId(), + monitor.severe( + format("[Client] Negotiation failed for provider %s and contractRequest %s", contractRequest.getProviderId(), contractRequest.getContractOffer().getId()), negotiationException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) @@ -218,7 +203,7 @@ public Response getData(@QueryParam("providerUrl") URL providerUrl, return Response.ok(data).build(); } catch (InterruptedException | ExecutionException negotiationException) { - LOGGER.error(format("Getting data failed for provider %s and agreementId %s", providerUrl, + monitor.severe(format("[Client] Getting data failed for provider %s and agreementId %s", providerUrl, agreementId), negotiationException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) .build(); @@ -250,7 +235,7 @@ public Response addAcceptedPolicies(PolicyDefinition[] policyDefinitions) { if (Objects.isNull(policyDefinitions)) { return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinitions array").build(); } - LOGGER.log(format("Adding %s accepted contract offers", policyDefinitions.length)); + monitor.info(format("[Client] Adding %s accepted contract offers", policyDefinitions.length)); policyService.addAccepted(policyDefinitions); return Response.ok().build(); @@ -264,7 +249,7 @@ public Response addAcceptedPolicies(PolicyDefinition[] policyDefinitions) { @GET @Path(ACCEPTED_POLICIES_PATH) public Response getAcceptedPolicyDefinitions() { - LOGGER.log("Returning accepted policyDefinitions"); + monitor.info("[Client] Returning accepted policyDefinitions"); return Response.ok(policyService.getAccepted()).build(); } @@ -277,7 +262,7 @@ public Response getAcceptedPolicyDefinitions() { @DELETE @Path(ACCEPTED_POLICIES_PATH) public Response deleteAcceptedPolicyDefinition(@QueryParam("policyDefinitionId") String policyDefinitionId) { - LOGGER.log(format("Removing policyDefinition with id %s", policyDefinitionId)); + monitor.info(format("[Client] Removing policyDefinition with id %s", policyDefinitionId)); if (Objects.isNull(policyDefinitionId)) { return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinitionId parameter").build(); } @@ -301,7 +286,7 @@ public Response updateAcceptedPolicyDefinition(PolicyDefinition policyDefinition if (Objects.isNull(policyDefinition)) { return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinition").build(); } - LOGGER.log(format("Updating policyDefinition with id %s", policyDefinition.getId())); + monitor.info(format("[Client] Updating policyDefinition with id %s", policyDefinition.getId())); var updated = policyService.updateAccepted(policyDefinition.getId(), policyDefinition); if (updated.isPresent()) { @@ -309,4 +294,5 @@ public Response updateAcceptedPolicyDefinition(PolicyDefinition policyDefinition } return Response.status(Response.Status.NOT_FOUND).entity("Unknown policyDefinitionId.").build(); } + } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java new file mode 100644 index 00000000..1541de01 --- /dev/null +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client; + +import static java.lang.String.format; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; +import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; +import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; +import org.eclipse.edc.connector.spi.catalog.CatalogService; +import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; +import org.eclipse.edc.web.spi.WebService; + +import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; +import de.fraunhofer.iosb.client.dataTransfer.DataTransferObservable; +import de.fraunhofer.iosb.client.dataTransfer.TransferInitiator; +import de.fraunhofer.iosb.client.negotiation.Negotiator; +import de.fraunhofer.iosb.client.policy.PolicyService; +import jakarta.ws.rs.core.UriBuilder; + +public class ClientExtension implements ServiceExtension { + + @Inject + private AuthenticationService authenticationService; + @Inject + private CatalogService catalogService; + @Inject + private TypeTransformerRegistry transformer; + @Inject + private ConsumerContractNegotiationManager consumerNegotiationManager; + @Inject + private ContractNegotiationObservable contractNegotiationObservable; + @Inject + private ContractNegotiationStore contractNegotiationStore; + @Inject + private TransferProcessManager transferProcessManager; + @Inject + private WebService webService; + + private static final String SETTINGS_PREFIX = "edc.client."; + + private Monitor monitor; + + @Override + public void initialize(ServiceExtensionContext context) { + monitor = context.getMonitor(); + var config = context.getConfig(); + + var observable = new DataTransferObservable(monitor); + var authenticationRequestFilter = new CustomAuthenticationRequestFilter(monitor, + authenticationService); + + // TODO better way to deal with config values + var policyService = new PolicyService(monitor, catalogService, transformer, + config.getBoolean(SETTINGS_PREFIX + "isAcceptAllProviderOffers"), + config.getInteger(SETTINGS_PREFIX + "getWaitForCatalogTimeout", 10), + config.getString(SETTINGS_PREFIX + "acceptedPolicyDefinitionsPath")); + var negotiator = new Negotiator(consumerNegotiationManager, contractNegotiationObservable, + contractNegotiationStore, config.getInteger(SETTINGS_PREFIX + "waitForAgreementTimeout", 5)); + var uri = createOwnUriFromConfigurationValues(config); + var transferInitiator = new TransferInitiator(uri, + transferProcessManager, + observable, + authenticationRequestFilter, + config.getInteger(SETTINGS_PREFIX + "getWaitForTransferTimeout", 10)); + + // TODO split up client Endpoint functionality? + var endpoint = new ClientEndpoint(monitor, negotiator, policyService, transferInitiator); + + webService.registerResource(endpoint); + + } + + /* + * TODO Maybe there is another way to retrieve these values? + */ + private URI createOwnUriFromConfigurationValues(Config config) { + URL protocolAddress; + var protocolAddressString = config.getString("edc.dsp.callback.address"); + + try { + protocolAddress = new URL(protocolAddressString); + } catch (MalformedURLException idsWebhookAddressException) { + throw new EdcException( + format("[Client] Configuration value edc.dsp.callback.address is a malformed URL: %s", + protocolAddressString), + idsWebhookAddressException); + } + + int ownPort = Integer.parseInt(config.getString("web.http.port")); + String ownPath = config.getString("web.http.path"); + + var ownUriBuilder = UriBuilder.newInstance() + .scheme(protocolAddress.getProtocol()) + .host(protocolAddress.getHost()) + .port(ownPort) + .path(ownPath); + + return ownUriBuilder.build(); + + } + +} diff --git a/client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java b/client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java new file mode 100644 index 00000000..43217725 --- /dev/null +++ b/client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client.authentication; + +import de.fraunhofer.iosb.client.ClientEndpoint; +import de.fraunhofer.iosb.client.dataTransfer.DataTransferEndpoint; +import jakarta.ws.rs.container.ContainerRequestContext; +import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter; +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.spi.monitor.Monitor; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +import static java.lang.String.format; + +/** + * Custom AuthenticationRequestFilter filtering requests that go directly to an + * AAS service (managed by this extension) or the extension's configuration. + */ +public class CustomAuthenticationRequestFilter extends AuthenticationRequestFilter { + + private final Monitor logger; + private final Map tempKeys; + + public CustomAuthenticationRequestFilter(Monitor logger, AuthenticationService authenticationService) { + super(authenticationService); + this.logger = logger; + tempKeys = new ConcurrentHashMap<>(); + } + + /** + * Add key,value pair for a request. This key will only be available for one + * request. + * + * @param key The key name + * @param value The actual key + */ + public void addTemporaryApiKey(String key, String value) { + tempKeys.put(key, value); + } + + /** + * On automated data transfer: If the request is valid, the key,value pair used + * for this request will no longer be valid. + */ + @Override + public void filter(ContainerRequestContext requestContext) { + Objects.requireNonNull(requestContext); + var requestPath = requestContext.getUriInfo().getPath(); + + for (String key : tempKeys.keySet()) { + if (requestContext.getHeaders().containsKey(key) + && requestContext.getHeaderString(key).equals(tempKeys.get(key)) + && requestPath.startsWith( + format("%s/%s", ClientEndpoint.AUTOMATED_PATH, DataTransferEndpoint.RECEIVE_DATA_PATH))) { + logger.debug( + format("CustomAuthenticationRequestFilter: Data Transfer request with custom api key %s", key)); + tempKeys.remove(key); + return; + } + } + + logger.debug("CustomAuthenticationRequestFilter: Intercepting this request"); + super.filter(requestContext); + } +} diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/DataTransferEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java similarity index 79% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/DataTransferEndpoint.java rename to client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java index aead4b1c..e71573aa 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/DataTransferEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java @@ -13,23 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.dataTransfer; +package de.fraunhofer.iosb.client.dataTransfer; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.client.ClientEndpoint; +import de.fraunhofer.iosb.client.ClientEndpoint; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.Objects; +import org.eclipse.edc.spi.monitor.Monitor; + import static java.lang.String.format; /** * Endpoint for automated data transfer */ -@Consumes({MediaType.APPLICATION_JSON, MediaType.WILDCARD}) -@Produces({MediaType.APPLICATION_JSON}) +@Consumes({ MediaType.APPLICATION_JSON, MediaType.WILDCARD }) +@Produces({ MediaType.APPLICATION_JSON }) @Path(ClientEndpoint.AUTOMATED_PATH) public class DataTransferEndpoint { @@ -38,10 +39,11 @@ public class DataTransferEndpoint { */ public static final String RECEIVE_DATA_PATH = "receiveData"; - private static final Logger LOGGER = Logger.getInstance(); + private final Monitor monitor; private final DataTransferObservable observable; - public DataTransferEndpoint(DataTransferObservable observable) { + public DataTransferEndpoint(Monitor monitor, DataTransferObservable observable) { + this.monitor = monitor; this.observable = observable; } @@ -56,7 +58,7 @@ public DataTransferEndpoint(DataTransferObservable observable) { @POST @Path("receiveData/{agreement}") public Response receiveData(@PathParam("agreement") String agreementId, String requestBody) { - LOGGER.log(format("Receiving data for agreement %s...", agreementId)); + monitor.info(format("Receiving data for agreement %s...", agreementId)); Objects.requireNonNull(agreementId); Objects.requireNonNull(requestBody); observable.update(agreementId, requestBody); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/DataTransferObservable.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java similarity index 90% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/DataTransferObservable.java rename to client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java index c2b6bd67..8c26e297 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/DataTransferObservable.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.dataTransfer; - -import de.fraunhofer.iosb.app.Logger; +package de.fraunhofer.iosb.client.dataTransfer; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; +import org.eclipse.edc.spi.monitor.Monitor; + import static java.lang.String.format; /** @@ -28,11 +28,12 @@ */ public class DataTransferObservable { - private static final Logger LOGGER = Logger.getInstance(); + private final Monitor monitor; private final Map> observers; - public DataTransferObservable() { + public DataTransferObservable(Monitor monitor) { + this.monitor = monitor; observers = new ConcurrentHashMap<>(); } @@ -64,7 +65,7 @@ public void unregister(String agreementId) { */ public void update(String agreementId, String data) { if (!observers.containsKey(agreementId)) { - LOGGER.warn(format( + monitor.warning(format( "A POST request to the client's data transfer endpoint with an unknown agreementID was caught. " + "AgreementID: %s", agreementId)); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/TransferInitiator.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java similarity index 93% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/TransferInitiator.java rename to client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java index 7536b126..8f0b3e4f 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/dataTransfer/TransferInitiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.dataTransfer; +package de.fraunhofer.iosb.client.dataTransfer; -import de.fraunhofer.iosb.app.authentication.CustomAuthenticationRequestFilter; -import de.fraunhofer.iosb.app.client.ClientEndpoint; -import de.fraunhofer.iosb.app.model.configuration.Configuration; +import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; +import de.fraunhofer.iosb.client.ClientEndpoint; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.connector.transfer.spi.types.TransferRequest; import org.eclipse.edc.spi.EdcException; @@ -46,7 +45,7 @@ public class TransferInitiator { private final TransferProcessManager transferProcessManager; private final URI ownUri; private final CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter; - + private final int waitForTransferTimeout; /** * Class constructor * @@ -62,13 +61,15 @@ public class TransferInitiator { */ public TransferInitiator(URI ownUri, TransferProcessManager transferProcessManager, DataTransferObservable observable, - CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter) { + CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter, + int waitForTransferTimeout) { this.ownUri = ownUri .resolve(format("./%s/%s/%s", ownUri.getPath(), ClientEndpoint.AUTOMATED_PATH, DataTransferEndpoint.RECEIVE_DATA_PATH)); this.transferProcessManager = transferProcessManager; this.observable = observable; this.dataEndpointAuthenticationRequestFilter = dataEndpointAuthenticationRequestFilter; + this.waitForTransferTimeout=waitForTransferTimeout; } /** @@ -144,7 +145,7 @@ public String waitForData(CompletableFuture dataFuture, String agreement throws InterruptedException, ExecutionException { try { // Fetch TransferTimeout everytime to adapt to runtime config changes - var data = dataFuture.get(Configuration.getInstance().getWaitForTransferTimeout(), TimeUnit.SECONDS); + var data = dataFuture.get(waitForTransferTimeout, TimeUnit.SECONDS); observable.unregister(agreementId); return data; } catch (TimeoutException transferTimeoutExceededException) { diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/exception/AmbiguousOrNullException.java b/client/src/main/java/de/fraunhofer/iosb/client/exception/AmbiguousOrNullException.java similarity index 95% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/exception/AmbiguousOrNullException.java rename to client/src/main/java/de/fraunhofer/iosb/client/exception/AmbiguousOrNullException.java index 57fe5b4f..7946cd33 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/exception/AmbiguousOrNullException.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/exception/AmbiguousOrNullException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.exception; +package de.fraunhofer.iosb.client.exception; import org.eclipse.edc.spi.EdcException; diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/negotiation/ClientContractNegotiationListener.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/ClientContractNegotiationListener.java similarity index 98% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/negotiation/ClientContractNegotiationListener.java rename to client/src/main/java/de/fraunhofer/iosb/client/negotiation/ClientContractNegotiationListener.java index 11c5ec73..181e8164 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/negotiation/ClientContractNegotiationListener.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/ClientContractNegotiationListener.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.negotiation; +package de.fraunhofer.iosb.client.negotiation; import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationListener; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/negotiation/Negotiator.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java similarity index 93% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/negotiation/Negotiator.java rename to client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java index 65b5bccb..ac18b4d2 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/negotiation/Negotiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.negotiation; +package de.fraunhofer.iosb.client.negotiation; -import de.fraunhofer.iosb.app.model.configuration.Configuration; import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; @@ -41,6 +40,8 @@ public class Negotiator { private final ClientContractNegotiationListener listener; private final ContractNegotiationStore contractNegotiationStore; + private final int waitForAgreementTimeout; + /** * Class constructor * @@ -50,10 +51,11 @@ public class Negotiator { * @param contractNegotiationStore Check for existing agreements before negotiating */ public Negotiator(ConsumerContractNegotiationManager consumerNegotiationManager, - ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore) { + ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore, + int waitForAgreementTimeout) { this.consumerNegotiationManager = consumerNegotiationManager; this.contractNegotiationStore = contractNegotiationStore; - + this.waitForAgreementTimeout=waitForAgreementTimeout; listener = new ClientContractNegotiationListener(); observable.registerListener(listener); } @@ -95,7 +97,7 @@ private ContractAgreement waitForAgreement(String negotiationId) throws Interrup listener.addListener(negotiationId, agreementFuture); try { - var negotiation = agreementFuture.get(Configuration.getInstance().getWaitForAgreementTimeout(), + var negotiation = agreementFuture.get(waitForAgreementTimeout, TimeUnit.SECONDS); listener.removeListener(negotiationId); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract/PolicyDefinitionStore.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java similarity index 76% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract/PolicyDefinitionStore.java rename to client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java index aa0c0d98..fa8636f7 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract/PolicyDefinitionStore.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.contract; +package de.fraunhofer.iosb.client.policy; import com.fasterxml.jackson.databind.ObjectMapper; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.model.configuration.Configuration; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; +import org.eclipse.edc.spi.monitor.Monitor; import java.io.IOException; import java.nio.file.Path; @@ -30,12 +29,13 @@ */ public class PolicyDefinitionStore { private final Map policyDefinitions; - private static final Logger LOGGER = Logger.getInstance(); + private final Monitor monitor; private final ObjectMapper objectMapper = new ObjectMapper(); - public PolicyDefinitionStore() { + public PolicyDefinitionStore(Monitor monitor, String acceptedPolicyDefinitionsPath) { + this.monitor = monitor; this.policyDefinitions = new ConcurrentHashMap<>(); - loadPolicyDefinitions(Configuration.getInstance()); + loadPolicyDefinitions(acceptedPolicyDefinitionsPath); } /** @@ -80,7 +80,7 @@ public Optional removePolicyDefinition(String policyDefinition * @return Optional containing updated policy definition or null */ public Optional updatePolicyDefinitions(String policyDefinitionId, - PolicyDefinition policyDefinition) { + PolicyDefinition policyDefinition) { Objects.requireNonNull(policyDefinitionId, "contractOfferId is null"); Objects.requireNonNull(policyDefinition, "contractOffer is null"); if (policyDefinitions.containsKey(policyDefinitionId)) { @@ -89,18 +89,19 @@ public Optional updatePolicyDefinitions(String policyDefinitio return Optional.empty(); } - private void loadPolicyDefinitions(Configuration config) { - if (Objects.isNull(config.getAcceptedPolicyDefinitionsPath())) { - return; - } - var acceptedPolicyDefinitionsPath = Path.of(config.getAcceptedPolicyDefinitionsPath()); - try { - var acceptedPolicyDefinitions = objectMapper.readValue(acceptedPolicyDefinitionsPath.toFile(), - PolicyDefinition[].class); - putPolicyDefinitions(acceptedPolicyDefinitions); - } catch (IOException e) { - LOGGER.warn("[Client] Could not load accepted ContractOffers (edc.aas.client.acceptedContractOfferPaths)", - e); + private void loadPolicyDefinitions(String acceptedPolicyDefinitionsPath) { + Path path; + if (Objects.nonNull(acceptedPolicyDefinitionsPath)) { + path = Path.of(acceptedPolicyDefinitionsPath); + try { + var acceptedPolicyDefinitions = objectMapper.readValue(path.toFile(), + PolicyDefinition[].class); + putPolicyDefinitions(acceptedPolicyDefinitions); + } catch (IOException e) { + monitor.warning( + "[Client] Could not load accepted ContractOffers (edc.aas.client.acceptedContractOfferPaths)", + e); + } } } } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract/PolicyService.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java similarity index 89% rename from edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract/PolicyService.java rename to client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java index fbcf579d..8a932f45 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/client/contract/PolicyService.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java @@ -13,16 +13,36 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.contract; +package de.fraunhofer.iosb.client.policy; + +import static java.lang.String.format; +import static org.eclipse.edc.jsonld.spi.Namespaces.DCAT_PREFIX; +import static org.eclipse.edc.jsonld.spi.Namespaces.DCAT_SCHEMA; +import static org.eclipse.edc.jsonld.spi.Namespaces.DCT_PREFIX; +import static org.eclipse.edc.jsonld.spi.Namespaces.DCT_SCHEMA; +import static org.eclipse.edc.jsonld.spi.Namespaces.DSPACE_PREFIX; +import static org.eclipse.edc.jsonld.spi.Namespaces.DSPACE_SCHEMA; +import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.DCAT_ACCESS_SERVICE_ATTRIBUTE; +import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.DCT_FORMAT_ATTRIBUTE; +import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_PREFIX; +import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; +import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; +import static org.eclipse.edc.spi.query.Criterion.criterion; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import de.fraunhofer.iosb.app.client.exception.AmbiguousOrNullException; -import de.fraunhofer.iosb.app.model.configuration.Configuration; -import de.fraunhofer.iosb.app.util.Pair; -import jakarta.json.Json; -import jakarta.json.JsonObject; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.catalog.spi.DataService; import org.eclipse.edc.catalog.spi.Dataset; @@ -32,41 +52,32 @@ import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.policy.model.Rule; import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.URL; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; -import static java.lang.String.format; -import static org.eclipse.edc.jsonld.spi.Namespaces.*; -import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.DCAT_ACCESS_SERVICE_ATTRIBUTE; -import static org.eclipse.edc.jsonld.spi.PropertyAndTypeNames.DCT_FORMAT_ATTRIBUTE; -import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_PREFIX; -import static org.eclipse.edc.policy.model.OdrlNamespace.ODRL_SCHEMA; -import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; -import static org.eclipse.edc.spi.query.Criterion.criterion; +import de.fraunhofer.iosb.client.exception.AmbiguousOrNullException; +import de.fraunhofer.iosb.client.util.Pair; +import jakarta.json.Json; +import jakarta.json.JsonObject; /** * Finds out policy for a given asset id and provider EDC url */ public class PolicyService { - private static final Configuration configuration = Configuration.getInstance(); - private final CatalogService catalogService; private final TypeTransformerRegistry transformer; private final PolicyDefinitionStore policyDefinitionStore; + private final boolean acceptAllProviderOffers; + private final int waitForCatalogTimeout; /** * Class constructor @@ -74,10 +85,14 @@ public class PolicyService { * @param catalogService Fetching the catalog of a provider. * @param transformer Transform json-ld byte-array catalog to catalog class */ - public PolicyService(CatalogService catalogService, TypeTransformerRegistry transformer) { + public PolicyService(Monitor monitor, CatalogService catalogService, TypeTransformerRegistry transformer, + boolean isAcceptAllProviderOffers, int getWaitForCatalogTimeout, String acceptedPolicyDefinitionsPath) { this.catalogService = catalogService; this.transformer = transformer; - this.policyDefinitionStore = new PolicyDefinitionStore(); + this.policyDefinitionStore = new PolicyDefinitionStore(monitor, acceptedPolicyDefinitionsPath); + + this.acceptAllProviderOffers = isAcceptAllProviderOffers; + this.waitForCatalogTimeout = getWaitForCatalogTimeout; } /** @@ -100,7 +115,7 @@ public Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws Inte StatusResult catalogResponse; try { - catalogResponse = catalogFuture.get(configuration.getWaitForCatalogTimeout(), TimeUnit.SECONDS); + catalogResponse = catalogFuture.get(waitForCatalogTimeout, TimeUnit.SECONDS); } catch (ExecutionException futureExecutionException) { throw new EdcException(format("Failed fetching a catalog by provider %s.", providerUrl), futureExecutionException); @@ -159,7 +174,7 @@ public Pair getAcceptablePolicyForAssetId(URL providerUrl, Strin var dataset = getDatasetForAssetId(providerUrl, assetId); Map.Entry acceptablePolicy; - if (configuration.isAcceptAllProviderOffers()) { + if (acceptAllProviderOffers) { acceptablePolicy = dataset.getOffers().entrySet().stream() .findAny() .orElseThrow(); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/util/Pair.java b/client/src/main/java/de/fraunhofer/iosb/client/util/Pair.java new file mode 100644 index 00000000..c52072cf --- /dev/null +++ b/client/src/main/java/de/fraunhofer/iosb/client/util/Pair.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client.util; + +/** + * Lightweight implementation of a tuple. + */ +public class Pair { + private T first; + private U second; + + public Pair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public U getSecond() { + return second; + } + + public void setFirst(T t) { + this.first = t; + } + + public void setSecond(U u) { + this.second = u; + } + + @Override + public String toString() { + return first.toString().concat(";").concat(second.toString()); + } + + public static class PairBuilder { + private T first; + private U second; + + public PairBuilder first(T t) { + this.first = t; + return this; + } + + public PairBuilder second(U u) { + this.second = u; + return this; + } + + public Pair build() { + return new Pair<>(this.first, this.second); + } + + } +} diff --git a/client/src/main/resources/org.eclipse.edc.spi.system.ServiceExtension b/client/src/main/resources/org.eclipse.edc.spi.system.ServiceExtension new file mode 100644 index 00000000..1ff3e7b2 --- /dev/null +++ b/client/src/main/resources/org.eclipse.edc.spi.system.ServiceExtension @@ -0,0 +1 @@ +de.fraunhofer.iosb.client.ClientExtension \ No newline at end of file diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/ClientEndpointTest.java b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java similarity index 90% rename from edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/ClientEndpointTest.java rename to client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java index ddda3b23..4aee4776 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/ClientEndpointTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client; +package de.fraunhofer.iosb.client; import com.fasterxml.jackson.databind.ObjectMapper; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.authentication.CustomAuthenticationRequestFilter; -import de.fraunhofer.iosb.app.client.contract.PolicyService; -import de.fraunhofer.iosb.app.client.dataTransfer.DataTransferObservable; -import de.fraunhofer.iosb.app.client.dataTransfer.TransferInitiator; -import de.fraunhofer.iosb.app.client.negotiation.Negotiator; +import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; +import de.fraunhofer.iosb.client.dataTransfer.DataTransferObservable; +import de.fraunhofer.iosb.client.dataTransfer.TransferInitiator; +import de.fraunhofer.iosb.client.negotiation.Negotiator; +import de.fraunhofer.iosb.client.policy.PolicyService; import jakarta.ws.rs.core.Response; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.catalog.spi.Dataset; @@ -64,6 +63,8 @@ public class ClientEndpointTest { + private static Monitor monitor; + private static URL url; private static ClientAndServer mockServer; @@ -74,7 +75,7 @@ public class ClientEndpointTest { @BeforeAll public static void initialize() throws IOException { - Logger.getInstance().setMonitor(mock(Monitor.class)); + monitor = mock(Monitor.class); int port = 8080; url = new URL(format("http://localhost:%s", port)); mockServer = startClientAndServer(port); @@ -92,13 +93,13 @@ public static void initialize() throws IOException { } @BeforeEach - public void setupSynchronizer() throws IOException { - clientEndpoint = new ClientEndpoint( - new PolicyService(mockCatalogService(), mockTransformer()), + public void setup() throws IOException { + clientEndpoint = new ClientEndpoint(monitor, new Negotiator(mockConsumerNegotiationManager(), mock(ContractNegotiationObservable.class), - mock(ContractNegotiationStore.class)), + mock(ContractNegotiationStore.class), 10), + new PolicyService(monitor, mockCatalogService(), mockTransformer(), false, 10, null), new TransferInitiator(URI.create("http://localhost:8181/api"), mockTransferProcessManager(), - mock(DataTransferObservable.class), mock(CustomAuthenticationRequestFilter.class))); + mock(DataTransferObservable.class), mock(CustomAuthenticationRequestFilter.class), 10)); } private TypeTransformerRegistry mockTransformer() { @@ -192,7 +193,7 @@ public void getAcceptedContractOffersTest() { public void addAcceptedContractOffersTest() { var mockPolicyDefinitionsAsList = new ArrayList(); mockPolicyDefinitionsAsList.add(mockPolicyDefinition); // ClientEndpoint creates ArrayList - var offers = new PolicyDefinition[]{mockPolicyDefinition}; + var offers = new PolicyDefinition[] { mockPolicyDefinition }; clientEndpoint.addAcceptedPolicies(offers); @@ -201,7 +202,7 @@ public void addAcceptedContractOffersTest() { @Test public void updateAcceptedContractOfferTest() { - var offers = new PolicyDefinition[]{mockPolicyDefinition}; + var offers = new PolicyDefinition[] { mockPolicyDefinition }; clientEndpoint.addAcceptedPolicies(offers); diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/dataTransfer/TransferInitiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java similarity index 95% rename from edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/dataTransfer/TransferInitiatorTest.java rename to client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java index 3e519df2..be7075c2 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/dataTransfer/TransferInitiatorTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.dataTransfer; +package de.fraunhofer.iosb.client.dataTransfer; -import de.fraunhofer.iosb.app.authentication.CustomAuthenticationRequestFilter; +import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; import org.eclipse.edc.spi.EdcException; @@ -43,7 +43,7 @@ public class TransferInitiatorTest { void initializeContractOfferService() throws URISyntaxException { URI ownUri = new URI("http://localhost:4321/api/ids"); transferInitiator = new TransferInitiator(ownUri, mockTransferProcessManager, - mock(DataTransferObservable.class), mock(CustomAuthenticationRequestFilter.class)); + mock(DataTransferObservable.class), mock(CustomAuthenticationRequestFilter.class), 10); mockStatusResult = (StatusResult) mock(StatusResult.class); when(mockTransferProcessManager.initiateConsumerRequest(any())).thenReturn(mockStatusResult); } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java new file mode 100644 index 00000000..667f4674 --- /dev/null +++ b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client.negotiation; + +import org.eclipse.edc.connector.contract.observe.ContractNegotiationObservableImpl; +import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; +import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; +import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; +import org.eclipse.edc.policy.model.Action; +import org.eclipse.edc.policy.model.Permission; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.response.StatusResult; +import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; +import org.eclipse.edc.spi.types.domain.offer.ContractOffer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NegotiatorTest { + + private final ConsumerContractNegotiationManager ccnmMock = mock(ConsumerContractNegotiationManager.class); + private final ContractNegotiationStore cnsMock = mock(ContractNegotiationStore.class); + private final ContractNegotiationObservable contractNegotiationObservable = new ContractNegotiationObservableImpl(); + private final String assetId = "test-asset-id"; + private final Policy mockPolicy = buildPolicy(); + private final ContractNegotiation negotiation = getContractNegotiation(); + + private Negotiator clientNegotiator; + + @BeforeEach + void initializeClientNegotiator() { + defineMockBehaviour(); + clientNegotiator = new Negotiator(ccnmMock, contractNegotiationObservable, cnsMock, 10); + } + + void defineMockBehaviour() { + when(cnsMock.queryAgreements(any())).thenReturn(Stream.of()); + when(ccnmMock.initiate(any())) + .thenReturn(StatusResult.success(negotiation)); + } + + @Test + void testNegotiate() throws MalformedURLException, ExecutionException, InterruptedException { + // Mocked EDC negotiation manager returns future which completes to a successful + // negotiation (agreement) + // Input is providerUrl (unimportant), contractOffer. The resulting + // contractAgreement should have the same + // policy as our contractOffer (not the same object reference) and the same + // asset ID + var fakeUrl = new URL("https://example.com/fakeurl"); + var contractOffer = ContractOffer.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .assetId(assetId) + .policy(mockPolicy) + .build(); + + var contractRequest = ContractRequest.Builder.newInstance() + .contractOffer(contractOffer) + .counterPartyAddress(fakeUrl.toString()) + .protocol("dataspace-protocol-http") + .build(); + + var future = Executors.newSingleThreadExecutor() + .submit(() -> clientNegotiator.negotiate(contractRequest)); + // Let the negotiator think we need time to process + // If not, the "confirmed" signal will be sent too soon, and the negotiator will + // never complete + Thread.sleep(1000); + contractNegotiationObservable.invokeForEach(listener -> listener.finalized(negotiation)); + + assertNotNull(future); + var agreement = future.get(); + assertNotNull(agreement); + assertEquals(mockPolicy, agreement.getPolicy()); + assertEquals(assetId, agreement.getAssetId()); + } + + /* + * Policy containing MOCK as permitted action + */ + private Policy buildPolicy() { + return Policy.Builder.newInstance() + .permission(Permission.Builder.newInstance() + .action(Action.Builder.newInstance() + .type("MOCK") + .build()) + .build()) + .build(); + } + + private ContractNegotiation getContractNegotiation() { + return ContractNegotiation.Builder.newInstance() + .counterPartyId("") + .counterPartyAddress("") + .protocol("") + .id("mocked-negotiation-id") + .contractAgreement(ContractAgreement.Builder.newInstance() + .id(UUID.randomUUID().toString()) + .providerId("provider") + .consumerId("consumer") + .assetId(assetId) + .policy(mockPolicy) + .build()) + .build(); + } + +} diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/contract/PolicyServiceTest.java b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java similarity index 92% rename from edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/contract/PolicyServiceTest.java rename to client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java index 6f8a64dc..840c27b2 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/contract/PolicyServiceTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java @@ -13,9 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package de.fraunhofer.iosb.app.client.contract; +package de.fraunhofer.iosb.client.policy; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; -import de.fraunhofer.iosb.app.testUtils.FileManager; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.catalog.spi.DataService; import org.eclipse.edc.catalog.spi.Dataset; @@ -23,27 +34,19 @@ import org.eclipse.edc.connector.spi.catalog.CatalogService; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.result.Result; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import de.fraunhofer.iosb.client.testUtils.FileManager; public class PolicyServiceTest { private final int providerPort = 54321; + private final Monitor mockMonitor = mock(Monitor.class); private final CatalogService mockCatalogService = mock(CatalogService.class); private final TypeTransformerRegistry mockTransformer = mock(TypeTransformerRegistry.class); @@ -55,7 +58,7 @@ public PolicyServiceTest() throws MalformedURLException { @BeforeEach void initializeContractOfferService() { - policyService = new PolicyService(mockCatalogService, mockTransformer); + policyService = new PolicyService(mockMonitor, mockCatalogService, mockTransformer, false, 10, null); } @Test diff --git a/client/src/test/java/de/fraunhofer/iosb/client/testUtils/FileManager.java b/client/src/test/java/de/fraunhofer/iosb/client/testUtils/FileManager.java new file mode 100644 index 00000000..3e7c70ec --- /dev/null +++ b/client/src/test/java/de/fraunhofer/iosb/client/testUtils/FileManager.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client.testUtils; + +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.fail; + +public class FileManager { + + private FileManager() { + } + + private static final File resourcesDirectory = new File("src/test/resources"); + + public static String loadResource(String fileName) { + try (FileInputStream x = new FileInputStream(new File(resourcesDirectory, fileName))) { + return IOUtils.toString(x, StandardCharsets.UTF_8); + } catch (FileNotFoundException e) { + fail("File not found exception on file " + fileName); + return null; + } catch (IOException e) { + fail("IO exception on file " + fileName); + return null; + } + } +} diff --git a/edc-extension4aas/src/test/resources/catalog.json b/client/src/test/resources/catalog.json similarity index 100% rename from edc-extension4aas/src/test/resources/catalog.json rename to client/src/test/resources/catalog.json diff --git a/edc-extension4aas/build.gradle.kts b/edc-extension4aas/build.gradle.kts index 29f35e55..1656d1ca 100644 --- a/edc-extension4aas/build.gradle.kts +++ b/edc-extension4aas/build.gradle.kts @@ -21,12 +21,10 @@ java { dependencies { // See this project's README.MD for explanations implementation("$group:contract-core:$edcVersion") - implementation("$group:dsp-catalog-http-dispatcher:$edcVersion") implementation("$group:management-api:$edcVersion") implementation("$group:runtime-metamodel:$edcVersion") implementation("$group:data-plane-http-spi:$edcVersion") // HttpDataAddress - implementation("com.squareup.okhttp3:okhttp:${okHttpVersion}") implementation("de.fraunhofer.iosb.ilt.faaast.service:starter:${faaastVersion}") implementation("io.admin-shell.aas:dataformat-json:1.2.1") diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java index 389a474f..c10e3d32 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java @@ -15,16 +15,25 @@ */ package de.fraunhofer.iosb.app; +import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; +import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; +import org.eclipse.edc.runtime.metamodel.annotation.Inject; +import org.eclipse.edc.spi.asset.AssetIndex; +import org.eclipse.edc.spi.system.ServiceExtension; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.web.spi.WebService; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; + import de.fraunhofer.iosb.app.authentication.CustomAuthenticationRequestFilter; -import de.fraunhofer.iosb.app.client.ClientEndpoint; -import de.fraunhofer.iosb.app.client.contract.PolicyService; -import de.fraunhofer.iosb.app.client.dataTransfer.DataTransferEndpoint; -import de.fraunhofer.iosb.app.client.dataTransfer.DataTransferObservable; -import de.fraunhofer.iosb.app.client.dataTransfer.TransferInitiator; -import de.fraunhofer.iosb.app.client.negotiation.Negotiator; import de.fraunhofer.iosb.app.controller.AasController; import de.fraunhofer.iosb.app.controller.ConfigurationController; import de.fraunhofer.iosb.app.controller.ResourceController; @@ -32,34 +41,6 @@ import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository; import de.fraunhofer.iosb.app.sync.Synchronizer; import okhttp3.OkHttpClient; -import org.apache.http.client.utils.URIBuilder; -import org.eclipse.edc.api.auth.spi.AuthenticationService; -import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; -import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; -import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; -import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; -import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; -import org.eclipse.edc.connector.spi.catalog.CatalogService; -import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; -import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.asset.AssetIndex; -import org.eclipse.edc.spi.system.ServiceExtension; -import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.eclipse.edc.web.spi.WebService; - -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Objects; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import static java.lang.String.format; /** * EDC Extension supporting usage of Asset Administration Shells. @@ -71,24 +52,12 @@ public class AasExtension implements ServiceExtension { @Inject private AuthenticationService authenticationService; @Inject - private CatalogService catalogService; - @Inject - private TypeTransformerRegistry transformer; - @Inject - private ConsumerContractNegotiationManager consumerNegotiationManager; - @Inject private ContractDefinitionStore contractStore; @Inject - private ContractNegotiationObservable contractNegotiationObservable; - @Inject - private ContractNegotiationStore contractNegotiationStore; - @Inject private OkHttpClient okHttpClient; @Inject private PolicyDefinitionStore policyStore; @Inject - private TransferProcessManager transferProcessManager; - @Inject private WebService webService; private static final String SETTINGS_PREFIX = "edc.aas."; @@ -134,62 +103,6 @@ public void initialize(ServiceExtensionContext context) { configInstance.isExposeSelfDescription() ? Endpoint.SELF_DESCRIPTION_PATH : null); webService.registerResource(authenticationRequestFilter); - initializeClient(context, authenticationRequestFilter); - } - - private void initializeClient(ServiceExtensionContext context, - CustomAuthenticationRequestFilter authenticationRequestFilter) { - URI ownUri; - try { - ownUri = createOwnUriFromConfigurationValues(context.getConfig()); - } catch (EdcException buildUriException) { - logger.error("Own URI for client could not be built. Reason:", buildUriException); - logger.warn("Client Endpoint will not be exposed and its functionality will not be available"); - return; - } - - var observable = new DataTransferObservable(); - - var clientEndpoint = new ClientEndpoint(new PolicyService(catalogService, transformer), - new Negotiator(consumerNegotiationManager, contractNegotiationObservable, - contractNegotiationStore), - new TransferInitiator(ownUri, transferProcessManager, observable, - authenticationRequestFilter)); - webService.registerResource(clientEndpoint); - - var dataTransferEndpoint = new DataTransferEndpoint(observable); - webService.registerResource(dataTransferEndpoint); - } - - /* - Maybe there is another way to retrieve these values? - */ - private URI createOwnUriFromConfigurationValues(Config config) { - URL protocolAddress; - var protocolAddressString = config.getString("edc.dsp.callback.address"); - - try { - protocolAddress = new URL(protocolAddressString); - } catch (MalformedURLException idsWebhookAddressException) { - throw new EdcException(format("Configuration value edc.dsp.callback.address is a malformed URL: %s", - protocolAddressString), - idsWebhookAddressException); - } - - int ownPort = Integer.parseInt(config.getString("web.http.port")); - String ownPath = config.getString("web.http.path"); - - var ownUriBuilder = new URIBuilder() - .setScheme(protocolAddress.getProtocol()) - .setHost(protocolAddress.getHost()) - .setPort(ownPort) - .setPath(ownPath); - - try { - return ownUriBuilder.build(); - } catch (URISyntaxException ownUriBuildException) { - throw new EdcException("Own URI could not be built:", ownUriBuildException); - } } /** diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java index ecd27554..427659b2 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java @@ -15,18 +15,13 @@ */ package de.fraunhofer.iosb.app.authentication; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.client.ClientEndpoint; -import de.fraunhofer.iosb.app.client.dataTransfer.DataTransferEndpoint; -import jakarta.ws.rs.container.ContainerRequestContext; +import java.util.Objects; + import org.eclipse.edc.api.auth.spi.AuthenticationRequestFilter; import org.eclipse.edc.api.auth.spi.AuthenticationService; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; - -import static java.lang.String.format; +import de.fraunhofer.iosb.app.Logger; +import jakarta.ws.rs.container.ContainerRequestContext; /** * Custom AuthenticationRequestFilter filtering requests that go directly to an @@ -35,12 +30,10 @@ public class CustomAuthenticationRequestFilter extends AuthenticationRequestFilter { private static final Logger LOGGER = Logger.getInstance(); - private final Map tempKeys; private final String[] endpoints; public CustomAuthenticationRequestFilter(AuthenticationService authenticationService, String... acceptedEndpoints) { super(authenticationService); - tempKeys = new ConcurrentHashMap<>(); if (Objects.nonNull(acceptedEndpoints)) { endpoints = acceptedEndpoints; } else { @@ -48,16 +41,6 @@ public CustomAuthenticationRequestFilter(AuthenticationService authenticationSer } } - /** - * Add key,value pair for a request. This key will only be available for one request. - * - * @param key The key name - * @param value The actual key - */ - public void addTemporaryApiKey(String key, String value) { - tempKeys.put(key, value); - } - /** * On automated data transfer: If the request is valid, the key,value pair used * for this request will no longer be valid. @@ -75,18 +58,6 @@ public void filter(ContainerRequestContext requestContext) { } } - for (String key : tempKeys.keySet()) { - if (requestContext.getHeaders().containsKey(key) - && requestContext.getHeaderString(key).equals(tempKeys.get(key)) - && requestPath.startsWith( - format("%s/%s", ClientEndpoint.AUTOMATED_PATH, DataTransferEndpoint.RECEIVE_DATA_PATH))) { - LOGGER.debug( - format("CustomAuthenticationRequestFilter: Data Transfer request with custom api key %s", key)); - tempKeys.remove(key); - return; - } - } - LOGGER.debug("CustomAuthenticationRequestFilter: Intercepting this request"); super.filter(requestContext); } diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java index 96ed8e20..046dc57d 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java @@ -52,7 +52,6 @@ public void initializeTestObject() { @Test void filterDataTransferTest() { - authRequestFilter.addTemporaryApiKey("test-key", "test-password"); verify(authService, times(0)).isAuthenticated(any()); diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/negotiation/NegotiatorTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/negotiation/NegotiatorTest.java deleted file mode 100644 index b55ad275..00000000 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/client/negotiation/NegotiatorTest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige - * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten - * Forschung e.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package de.fraunhofer.iosb.app.client.negotiation; - -import org.eclipse.edc.connector.contract.observe.ContractNegotiationObservableImpl; -import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; -import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; -import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; -import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; -import org.eclipse.edc.policy.model.Action; -import org.eclipse.edc.policy.model.Permission; -import org.eclipse.edc.policy.model.Policy; -import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; -import org.eclipse.edc.spi.types.domain.offer.ContractOffer; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.net.MalformedURLException; -import java.net.URL; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class NegotiatorTest { - - private final ConsumerContractNegotiationManager ccnmMock = mock(ConsumerContractNegotiationManager.class); - private final ContractNegotiationStore cnsMock = mock(ContractNegotiationStore.class); - private final ContractNegotiationObservable contractNegotiationObservable = new ContractNegotiationObservableImpl(); - private final String assetId = "test-asset-id"; - private final Policy mockPolicy = buildPolicy(); - private final ContractNegotiation negotiation = getContractNegotiation(); - - private Negotiator clientNegotiator; - - - @BeforeEach - void initializeClientNegotiator() { - defineMockBehaviour(); - clientNegotiator = new Negotiator(ccnmMock, contractNegotiationObservable, cnsMock); - } - - void defineMockBehaviour() { - when(cnsMock.queryAgreements(any())).thenReturn(Stream.of()); - when(ccnmMock.initiate(any())) - .thenReturn(StatusResult.success(negotiation)); - } - - @Test - void testNegotiate() throws MalformedURLException, ExecutionException, InterruptedException { - // Mocked EDC negotiation manager returns future which completes to a successful negotiation (agreement) - // Input is providerUrl (unimportant), contractOffer. The resulting contractAgreement should have the same - // policy as our contractOffer (not the same object reference) and the same asset ID - var fakeUrl = new URL("https://example.com/fakeurl"); - var contractOffer = ContractOffer.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .assetId(assetId) - .policy(mockPolicy) - .build(); - - var contractRequest = ContractRequest.Builder.newInstance() - .contractOffer(contractOffer) - .counterPartyAddress(fakeUrl.toString()) - .protocol("dataspace-protocol-http") - .build(); - - var future = Executors.newSingleThreadExecutor().submit(() -> clientNegotiator.negotiate(contractRequest)); - // Let the negotiator think we need time to process - // If not, the "confirmed" signal will be sent too soon, and the negotiator will - // never complete - Thread.sleep(1000); - contractNegotiationObservable.invokeForEach(listener -> listener.finalized(negotiation)); - - assertNotNull(future); - var agreement = future.get(); - assertNotNull(agreement); - assertEquals(mockPolicy, agreement.getPolicy()); - assertEquals(assetId, agreement.getAssetId()); - } - - /* - Policy containing MOCK as permitted action - */ - private Policy buildPolicy() { - return Policy.Builder.newInstance() - .permission(Permission.Builder.newInstance() - .action(Action.Builder.newInstance() - .type("MOCK") - .build()) - .build()) - .build(); - } - - private ContractNegotiation getContractNegotiation() { - return ContractNegotiation.Builder.newInstance() - .counterPartyId("") - .counterPartyAddress("") - .protocol("") - .id("mocked-negotiation-id") - .contractAgreement(ContractAgreement.Builder.newInstance() - .id(UUID.randomUUID().toString()) - .providerId("provider") - .consumerId("consumer") - .assetId(assetId) - .policy(mockPolicy) - .build()) - .build(); - } - -} diff --git a/settings.gradle.kts b/settings.gradle.kts index e826aadb..dfb7d8df 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,6 @@ // include the extension in the build process include(":edc-extension4aas") +include(":client") // include the launcher in the build process include(":example") \ No newline at end of file From 9592042dcf169f7bbeaca59fdd671802c4666d58 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:21:59 +0100 Subject: [PATCH 02/20] Distribute config object to modules For more current config values --- .../iosb/client/ClientEndpoint.java | 15 ++++++ .../iosb/client/ClientExtension.java | 19 +++----- .../dataTransfer/TransferInitiator.java | 33 +++++++------ .../iosb/client/negotiation/Negotiator.java | 34 ++++++++----- .../iosb/client/policy/PolicyService.java | 18 +++---- .../client/policy/PolicyServiceConfig.java | 48 +++++++++++++++++++ 6 files changed, 119 insertions(+), 48 deletions(-) create mode 100644 client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java index 6c280003..3647336b 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java @@ -1,3 +1,18 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package de.fraunhofer.iosb.client; import static java.lang.String.format; diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java index 1541de01..4fdeaa5d 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java @@ -62,7 +62,7 @@ public class ClientExtension implements ServiceExtension { @Inject private WebService webService; - private static final String SETTINGS_PREFIX = "edc.client."; + public static final String SETTINGS_PREFIX = "edc.client."; private Monitor monitor; @@ -75,19 +75,14 @@ public void initialize(ServiceExtensionContext context) { var authenticationRequestFilter = new CustomAuthenticationRequestFilter(monitor, authenticationService); - // TODO better way to deal with config values - var policyService = new PolicyService(monitor, catalogService, transformer, - config.getBoolean(SETTINGS_PREFIX + "isAcceptAllProviderOffers"), - config.getInteger(SETTINGS_PREFIX + "getWaitForCatalogTimeout", 10), - config.getString(SETTINGS_PREFIX + "acceptedPolicyDefinitionsPath")); + var policyService = new PolicyService(monitor, catalogService, transformer, config); + var negotiator = new Negotiator(consumerNegotiationManager, contractNegotiationObservable, - contractNegotiationStore, config.getInteger(SETTINGS_PREFIX + "waitForAgreementTimeout", 5)); + contractNegotiationStore, config); + var uri = createOwnUriFromConfigurationValues(config); - var transferInitiator = new TransferInitiator(uri, - transferProcessManager, - observable, - authenticationRequestFilter, - config.getInteger(SETTINGS_PREFIX + "getWaitForTransferTimeout", 10)); + var transferInitiator = new TransferInitiator(config, authenticationRequestFilter, observable, uri, + transferProcessManager); // TODO split up client Endpoint functionality? var endpoint = new ClientEndpoint(monitor, negotiator, policyService, transferInitiator); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java index 8f0b3e4f..f4bb1834 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java @@ -20,6 +20,7 @@ import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.connector.transfer.spi.types.TransferRequest; import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.DataAddress; import java.net.URI; @@ -30,6 +31,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; import static java.lang.String.format; import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; @@ -40,12 +42,14 @@ public class TransferInitiator { private static final String DATA_TRANSFER_API_KEY = "data-transfer-api-key"; + private static final int WAIT_FOR_TRANSFER_TIMEOUT_DEFAULT = 10; private final DataTransferObservable observable; private final TransferProcessManager transferProcessManager; private final URI ownUri; private final CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter; - private final int waitForTransferTimeout; + private final Config config; + /** * Class constructor * @@ -59,17 +63,14 @@ public class TransferInitiator { * custom api keys for each data * transfer */ - public TransferInitiator(URI ownUri, - TransferProcessManager transferProcessManager, DataTransferObservable observable, - CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter, - int waitForTransferTimeout) { - this.ownUri = ownUri - .resolve(format("./%s/%s/%s", ownUri.getPath(), ClientEndpoint.AUTOMATED_PATH, - DataTransferEndpoint.RECEIVE_DATA_PATH)); - this.transferProcessManager = transferProcessManager; - this.observable = observable; + public TransferInitiator(Config config, CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter, + DataTransferObservable observable, URI ownUri, TransferProcessManager transferProcessManager) { + this.config = config; this.dataEndpointAuthenticationRequestFilter = dataEndpointAuthenticationRequestFilter; - this.waitForTransferTimeout=waitForTransferTimeout; + this.ownUri = ownUri.resolve(format("./%s/%s/%s", ownUri.getPath(), ClientEndpoint.AUTOMATED_PATH, + DataTransferEndpoint.RECEIVE_DATA_PATH)); + this.observable = observable; + this.transferProcessManager = transferProcessManager; } /** @@ -80,7 +81,7 @@ public TransferInitiator(URI ownUri, * @param agreementId Non-null ContractAgreement of the negotiation process. * @param assetId The asset to be fetched. * @return A completable future whose result will be the data or an error - * message. + * message. */ public CompletableFuture initiateTransferProcess(URL providerUrl, String agreementId, String assetId) { var apiKey = UUID.randomUUID().toString(); @@ -106,10 +107,10 @@ public CompletableFuture initiateTransferProcess(URL providerUrl, String * @param dataSinkAddress HTTPDataAddress the result of the transfer should be * sent to. * @return A completable future whose result will be the data or an error - * message. + * message. */ public CompletableFuture initiateTransferProcess(URL providerUrl, String agreementId, String assetId, - DataAddress dataSinkAddress) { + DataAddress dataSinkAddress) { // Prepare for incoming data var dataFuture = new CompletableFuture(); observable.register(dataFuture, agreementId); @@ -145,7 +146,9 @@ public String waitForData(CompletableFuture dataFuture, String agreement throws InterruptedException, ExecutionException { try { // Fetch TransferTimeout everytime to adapt to runtime config changes - var data = dataFuture.get(waitForTransferTimeout, TimeUnit.SECONDS); + var data = dataFuture.get( + config.getInteger(SETTINGS_PREFIX + "getWaitForTransferTimeout", WAIT_FOR_TRANSFER_TIMEOUT_DEFAULT), + TimeUnit.SECONDS); observable.unregister(agreementId); return data; } catch (TimeoutException transferTimeoutExceededException) { diff --git a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java index ac18b4d2..e10cb104 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java @@ -22,6 +22,7 @@ import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import java.util.concurrent.CompletableFuture; @@ -29,6 +30,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; import static java.lang.String.format; /** @@ -36,11 +38,13 @@ */ public class Negotiator { + // How long the client waits for a negotiation to finish (seconds) + private static final int WAIT_FOR_AGREEMENT_TIMEOUT_DEFAULT = 10; + private final ConsumerContractNegotiationManager consumerNegotiationManager; private final ClientContractNegotiationListener listener; private final ContractNegotiationStore contractNegotiationStore; - - private final int waitForAgreementTimeout; + private final Config config; /** * Class constructor @@ -48,20 +52,23 @@ public class Negotiator { * @param consumerNegotiationManager Initiating a negotiation as a consumer. * @param observable Status updates for waiting data transfer * requesters to avoid busy waiting. - * @param contractNegotiationStore Check for existing agreements before negotiating + * @param contractNegotiationStore Check for existing agreements before + * negotiating */ public Negotiator(ConsumerContractNegotiationManager consumerNegotiationManager, - ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore, - int waitForAgreementTimeout) { + ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore, + Config config) { this.consumerNegotiationManager = consumerNegotiationManager; this.contractNegotiationStore = contractNegotiationStore; - this.waitForAgreementTimeout=waitForAgreementTimeout; + this.config = config; + listener = new ClientContractNegotiationListener(); observable.registerListener(listener); } /** - * Negotiate a contract agreement using the given contract offer if no agreement exists for this constellation. + * Negotiate a contract agreement using the given contract offer if no agreement + * exists for this constellation. * * @param contractRequest The contract request to be sent. * @return contractAgreement of the completed negotiation. @@ -94,17 +101,20 @@ public ContractAgreement negotiate(ContractRequest contractRequest) private ContractAgreement waitForAgreement(String negotiationId) throws InterruptedException, ExecutionException { var agreementFuture = new CompletableFuture(); + var timeout = config.getInteger(SETTINGS_PREFIX + "waitForAgreementTimeout", + WAIT_FOR_AGREEMENT_TIMEOUT_DEFAULT); + listener.addListener(negotiationId, agreementFuture); try { - var negotiation = agreementFuture.get(waitForAgreementTimeout, - TimeUnit.SECONDS); + var negotiation = agreementFuture.get(timeout, TimeUnit.SECONDS); listener.removeListener(negotiationId); return negotiation.getContractAgreement(); - } catch (TimeoutException agreementTimeoutExceededException) { - throw new EdcException(format("Waiting for an agreement failed for negotiationId: %s", negotiationId), - agreementTimeoutExceededException); + } catch (TimeoutException agreementTimeout) { + throw new EdcException( + format("[Client] Agreement negotiation timed out for negotiation id: %s", negotiationId), + agreementTimeout); } } } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java index 8a932f45..7e3938d1 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java @@ -55,6 +55,7 @@ import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.response.StatusResult; +import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; @@ -72,12 +73,11 @@ */ public class PolicyService { + private final PolicyServiceConfig config; + private final CatalogService catalogService; private final TypeTransformerRegistry transformer; - private final PolicyDefinitionStore policyDefinitionStore; - private final boolean acceptAllProviderOffers; - private final int waitForCatalogTimeout; /** * Class constructor @@ -86,13 +86,13 @@ public class PolicyService { * @param transformer Transform json-ld byte-array catalog to catalog class */ public PolicyService(Monitor monitor, CatalogService catalogService, TypeTransformerRegistry transformer, - boolean isAcceptAllProviderOffers, int getWaitForCatalogTimeout, String acceptedPolicyDefinitionsPath) { + Config systemConfig) { + this.config = new PolicyServiceConfig(systemConfig); + this.policyDefinitionStore = new PolicyDefinitionStore(monitor, this.config.getAcceptedPolicyDefinitionsPath()); + this.catalogService = catalogService; this.transformer = transformer; - this.policyDefinitionStore = new PolicyDefinitionStore(monitor, acceptedPolicyDefinitionsPath); - this.acceptAllProviderOffers = isAcceptAllProviderOffers; - this.waitForCatalogTimeout = getWaitForCatalogTimeout; } /** @@ -115,7 +115,7 @@ public Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws Inte StatusResult catalogResponse; try { - catalogResponse = catalogFuture.get(waitForCatalogTimeout, TimeUnit.SECONDS); + catalogResponse = catalogFuture.get(config.getWaitForCatalogTimeout(), TimeUnit.SECONDS); } catch (ExecutionException futureExecutionException) { throw new EdcException(format("Failed fetching a catalog by provider %s.", providerUrl), futureExecutionException); @@ -174,7 +174,7 @@ public Pair getAcceptablePolicyForAssetId(URL providerUrl, Strin var dataset = getDatasetForAssetId(providerUrl, assetId); Map.Entry acceptablePolicy; - if (acceptAllProviderOffers) { + if (config.isAcceptAllProviderOffers()) { acceptablePolicy = dataset.getOffers().entrySet().stream() .findAny() .orElseThrow(); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java new file mode 100644 index 00000000..577898a9 --- /dev/null +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client.policy; + +import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; + +import org.eclipse.edc.spi.system.configuration.Config; + +/** + * Simple {@link org.eclipse.edc.spi.system.configuration.Config} wrapper + */ +public class PolicyServiceConfig { + + private static final boolean ACCEPT_ALL_PROVIDER_OFFERS_DEFAULT = false; + private static final int WAIT_FOR_CATALOG_TIMEOUT_DEFAULT = 10; + + private final Config config; + + public PolicyServiceConfig(Config config) { + this.config = config; + } + + boolean isAcceptAllProviderOffers() { + return config.getBoolean(SETTINGS_PREFIX + "acceptAllProviderOffers", ACCEPT_ALL_PROVIDER_OFFERS_DEFAULT); + } + + int getWaitForCatalogTimeout() { + return config.getInteger(SETTINGS_PREFIX + "waitForCatalogTimeout", WAIT_FOR_CATALOG_TIMEOUT_DEFAULT); + } + + String getAcceptedPolicyDefinitionsPath() { + return config.getString(SETTINGS_PREFIX + "acceptedPolicyDefinitionsPath"); + } + +} From 8059c5cea385139a34c8b0c874a38c2d12acbb27 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 11:22:20 +0100 Subject: [PATCH 03/20] Update test cases according to recent changes --- .../iosb/client/ClientEndpointTest.java | 24 ++++++-- .../dataTransfer/TransferInitiatorTest.java | 7 ++- .../client/negotiation/NegotiatorTest.java | 56 +++++++++++-------- .../iosb/client/policy/PolicyServiceTest.java | 3 +- 4 files changed, 58 insertions(+), 32 deletions(-) diff --git a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java index 4aee4776..888ced3b 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java @@ -38,6 +38,7 @@ import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.response.ResponseStatus; import org.eclipse.edc.spi.response.StatusResult; +import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.spi.types.domain.offer.ContractOffer; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; @@ -95,11 +96,22 @@ public static void initialize() throws IOException { @BeforeEach public void setup() throws IOException { clientEndpoint = new ClientEndpoint(monitor, - new Negotiator(mockConsumerNegotiationManager(), mock(ContractNegotiationObservable.class), - mock(ContractNegotiationStore.class), 10), - new PolicyService(monitor, mockCatalogService(), mockTransformer(), false, 10, null), - new TransferInitiator(URI.create("http://localhost:8181/api"), mockTransferProcessManager(), - mock(DataTransferObservable.class), mock(CustomAuthenticationRequestFilter.class), 10)); + new Negotiator( + mockConsumerNegotiationManager(), + mock(ContractNegotiationObservable.class), + mock(ContractNegotiationStore.class), + mock(Config.class)), + new PolicyService( + monitor, + mockCatalogService(), + mockTransformer(), + mock(Config.class)), + new TransferInitiator( + mock(Config.class), + mock(CustomAuthenticationRequestFilter.class), + mock(DataTransferObservable.class), + URI.create("http://localhost:8181/api"), + mockTransferProcessManager())); } private TypeTransformerRegistry mockTransformer() { @@ -161,7 +173,7 @@ public void negotiateContractTest() { fail(); } catch (EdcException expected) { if (!(expected.getCause().getClass().equals(TimeoutException.class) - && expected.getMessage().contains("agreement"))) { + && expected.getMessage().contains("Agreement"))) { fail(); // This must fail because of agreement timeout. } } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java index be7075c2..aa2741dc 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java @@ -20,6 +20,7 @@ import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.response.StatusResult; +import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,9 +43,11 @@ public class TransferInitiatorTest { @SuppressWarnings("unchecked") void initializeContractOfferService() throws URISyntaxException { URI ownUri = new URI("http://localhost:4321/api/ids"); - transferInitiator = new TransferInitiator(ownUri, mockTransferProcessManager, - mock(DataTransferObservable.class), mock(CustomAuthenticationRequestFilter.class), 10); + transferInitiator = new TransferInitiator(mock(Config.class), mock(CustomAuthenticationRequestFilter.class), + mock(DataTransferObservable.class), ownUri, mockTransferProcessManager); + mockStatusResult = (StatusResult) mock(StatusResult.class); + when(mockTransferProcessManager.initiateConsumerRequest(any())).thenReturn(mockStatusResult); } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java index 667f4674..60563233 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java @@ -15,6 +15,20 @@ */ package de.fraunhofer.iosb.client.negotiation; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.UUID; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.stream.Stream; + import org.eclipse.edc.connector.contract.observe.ContractNegotiationObservableImpl; import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; @@ -25,29 +39,20 @@ import org.eclipse.edc.policy.model.Permission; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.response.StatusResult; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.eclipse.edc.spi.types.domain.offer.ContractOffer; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.UUID; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - public class NegotiatorTest { private final ConsumerContractNegotiationManager ccnmMock = mock(ConsumerContractNegotiationManager.class); private final ContractNegotiationStore cnsMock = mock(ContractNegotiationStore.class); private final ContractNegotiationObservable contractNegotiationObservable = new ContractNegotiationObservableImpl(); + private final Config configMock = ConfigFactory.empty(); + private final String assetId = "test-asset-id"; private final Policy mockPolicy = buildPolicy(); private final ContractNegotiation negotiation = getContractNegotiation(); @@ -57,7 +62,7 @@ public class NegotiatorTest { @BeforeEach void initializeClientNegotiator() { defineMockBehaviour(); - clientNegotiator = new Negotiator(ccnmMock, contractNegotiationObservable, cnsMock, 10); + clientNegotiator = new Negotiator(ccnmMock, contractNegotiationObservable, cnsMock, configMock); } void defineMockBehaviour() { @@ -68,8 +73,8 @@ void defineMockBehaviour() { @Test void testNegotiate() throws MalformedURLException, ExecutionException, InterruptedException { - // Mocked EDC negotiation manager returns future which completes to a successful - // negotiation (agreement) + // Mocked EDC negotiation manager returns a future which completes to a + // successful negotiation (agreement) // Input is providerUrl (unimportant), contractOffer. The resulting // contractAgreement should have the same // policy as our contractOffer (not the same object reference) and the same @@ -89,10 +94,15 @@ void testNegotiate() throws MalformedURLException, ExecutionException, Interrupt var future = Executors.newSingleThreadExecutor() .submit(() -> clientNegotiator.negotiate(contractRequest)); - // Let the negotiator think we need time to process - // If not, the "confirmed" signal will be sent too soon, and the negotiator will - // never complete - Thread.sleep(1000); + + // Let the negotiator add a listener to this negotiation. + // If we don't, the "confirmed" signal will be sent too soon, and the negotiator + // will never see it + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + fail(); + } contractNegotiationObservable.invokeForEach(listener -> listener.finalized(negotiation)); assertNotNull(future); @@ -117,9 +127,9 @@ private Policy buildPolicy() { private ContractNegotiation getContractNegotiation() { return ContractNegotiation.Builder.newInstance() - .counterPartyId("") - .counterPartyAddress("") - .protocol("") + .counterPartyId("mock-counter-party-id") + .counterPartyAddress("mock-counter-party-address") + .protocol("mock-protocol") .id("mocked-negotiation-id") .contractAgreement(ContractAgreement.Builder.newInstance() .id(UUID.randomUUID().toString()) diff --git a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java index 840c27b2..ffb5d296 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java @@ -37,6 +37,7 @@ import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.result.Result; +import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -58,7 +59,7 @@ public PolicyServiceTest() throws MalformedURLException { @BeforeEach void initializeContractOfferService() { - policyService = new PolicyService(mockMonitor, mockCatalogService, mockTransformer, false, 10, null); + policyService = new PolicyService(mockMonitor, mockCatalogService, mockTransformer, mock(Config.class)); } @Test From e3a9e5f34f7513aeab9548bfdf538ae156cc308a Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:18:33 +0100 Subject: [PATCH 04/20] Add controllers for modules --- .../iosb/client/ClientEndpoint.java | 187 +++++++++--------- .../iosb/client/ClientExtension.java | 111 +++-------- .../dataTransfer/DataTransferController.java | 114 +++++++++++ .../dataTransfer/DataTransferObservable.java | 10 +- .../dataTransfer/TransferInitiator.java | 134 ++++--------- .../negotiation/NegotiationController.java | 90 +++++++++ .../iosb/client/negotiation/Negotiator.java | 76 ++----- .../iosb/client/policy/PolicyController.java | 93 +++++++++ .../client/policy/PolicyDefinitionStore.java | 25 +-- .../iosb/client/policy/PolicyService.java | 90 +-------- 10 files changed, 502 insertions(+), 428 deletions(-) create mode 100644 client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java create mode 100644 client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java create mode 100644 client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java index 3647336b..c999ca9e 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java @@ -23,16 +23,15 @@ import java.util.concurrent.ExecutionException; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; -import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; import org.eclipse.edc.spi.types.domain.offer.ContractOffer; -import de.fraunhofer.iosb.client.dataTransfer.TransferInitiator; -import de.fraunhofer.iosb.client.negotiation.Negotiator; -import de.fraunhofer.iosb.client.policy.PolicyService; +import de.fraunhofer.iosb.client.dataTransfer.DataTransferController; +import de.fraunhofer.iosb.client.negotiation.NegotiationController; +import de.fraunhofer.iosb.client.policy.PolicyController; import de.fraunhofer.iosb.client.util.Pair; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DELETE; @@ -56,7 +55,6 @@ public class ClientEndpoint { * Root path for the client */ public static final String AUTOMATED_PATH = "automated"; - private static final String ACCEPTED_POLICIES_PATH = "acceptedPolicies"; private static final String DATASET_PATH = "dataset"; private static final String NEGOTIATE_CONTRACT_PATH = "negotiateContract"; @@ -65,9 +63,9 @@ public class ClientEndpoint { private final Monitor monitor; - private final Negotiator negotiator; - private final PolicyService policyService; - private final TransferInitiator transferInitiator; + private final NegotiationController negotiationController; + private final PolicyController policyController; + private final DataTransferController transferController; /** * Initialize a client endpoint. @@ -77,23 +75,56 @@ public class ClientEndpoint { * @param negotiator Send contract offer, negotiation status watch. * @param transferInitiator Initiate transfer requests. */ - public ClientEndpoint(Monitor monitor, Negotiator negotiator, PolicyService policyService, - TransferInitiator transferInitiator) { + public ClientEndpoint(Monitor monitor, + NegotiationController negotiationController, + PolicyController policyController, + DataTransferController transferController) { this.monitor = monitor; - this.negotiator = negotiator; - this.policyService = policyService; - this.transferInitiator = transferInitiator; + this.policyController = policyController; + this.negotiationController = negotiationController; + this.transferController = transferController; } /** - * Negotiate a contract with a provider edc. - * WARNING: By initiating this request, any policy provided by the provider for - * the specified asset will be sent - * as a contract offer unmodified if edc.aas.client.acceptAllProviderOffers is - * set to true. + * Return policyDefinition for assetId that match any policyDefinitions' policy + * of the services' policyDefinitionStore instance containing user added + * policyDefinitions. If more than one policyDefinitions are provided by the + * provider connector, an AmbiguousOrNullException will be thrown. + * + * @param providerUrl Provider of the asset. + * @param assetId Asset ID of the asset whose contract should be fetched. + * @return One policyDefinition offered by the provider for the given assetId. + * @throws InterruptedException Thread for agreementId was waiting, sleeping, or + * otherwise occupied, and was + * interrupted. + */ + @GET + @Path(DATASET_PATH) + public Response getDataset(@QueryParam("providerUrl") URL providerUrl, + @QueryParam("assetId") String assetId) { + monitor.debug(format("[Client] Received a %s GET request", DATASET_PATH)); + + if (Objects.isNull(providerUrl)) { + return Response.status(Response.Status.BAD_REQUEST).entity("Provider URL must not be null").build(); + } + + try { + var dataset = policyController.getDataset(providerUrl, assetId); + return Response.ok(dataset).build(); + } catch (InterruptedException interruptedException) { + monitor.severe(format("[Client] Getting dataset failed for provider %s and asset %s", providerUrl, + assetId), interruptedException); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(interruptedException.getMessage()) + .build(); + } + } + + /** + * Negotiate a contract agreement using the given contract offer if no agreement + * exists for this constellation. * - * @param providerUrl Provider EDCs URL (IDS endpoint) + * @param providerUrl Provider EDCs URL (DSP endpoint) * @param assetId ID of the asset to be retrieved * @return Asset data */ @@ -109,7 +140,7 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, Pair idPolicyPair; // id means contractOfferId try { - idPolicyPair = policyService.getAcceptablePolicyForAssetId(providerUrl, assetId); + idPolicyPair = policyController.getAcceptablePolicyForAssetId(providerUrl, assetId); } catch (InterruptedException negotiationException) { monitor.severe(format("[Client] Getting policies failed for provider %s and asset %s", providerUrl, assetId), negotiationException); @@ -132,7 +163,7 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, ContractAgreement agreement; try { - agreement = negotiator.negotiate(contractRequest); + agreement = negotiationController.negotiateContract(contractRequest); } catch (InterruptedException | ExecutionException negotiationException) { monitor.severe(format("[Client] Negotiation failed for provider %s and contractOffer %s", providerUrl, offer.getId()), negotiationException); @@ -144,48 +175,24 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, } /** - * Returns dataset offered by the given provider for the given asset. - * - * @param providerUrl Provider whose dataset should be fetched (non null). - * @param assetId Asset ID for which dataset should be fetched. - * @return A dataset or an error message. - */ - @GET - @Path(DATASET_PATH) - public Response getDataset(@QueryParam("providerUrl") URL providerUrl, - @QueryParam("assetId") String assetId) { - if (Objects.isNull(providerUrl)) { - return Response.status(Response.Status.BAD_REQUEST).entity("Provider URL must not be null").build(); - } - - try { - var datasets = policyService.getDatasetForAssetId(providerUrl, assetId); - return Response.ok(datasets).build(); - } catch (InterruptedException interruptedException) { - monitor.severe(format("[Client] Getting datasets failed for provider %s and asset %s", providerUrl, - assetId), interruptedException); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(interruptedException.getMessage()) - .build(); - } - } - - /** - * Initiate a contract negotiation, acting as a consumer, with a provider - * connector. + * Negotiate a contract agreement using the given contract offer if no agreement + * exists for this constellation. * * @param contractRequest The contract request to be sent. - * @return An agreementID on success or an error message on error. + * @return contractAgreement of the completed negotiation. */ @POST @Path(NEGOTIATE_CONTRACT_PATH) public Response negotiateContract(ContractRequest contractRequest) { + monitor.debug(format("[Client] Received a %s POST request", NEGOTIATE_CONTRACT_PATH)); Objects.requireNonNull(contractRequest, "ContractRequest must not be null"); try { - var agreement = negotiator.negotiate(contractRequest); + var agreement = negotiationController.negotiateContract(contractRequest); return Response.ok(agreement).build(); } catch (InterruptedException | ExecutionException negotiationException) { monitor.severe( - format("[Client] Negotiation failed for provider %s and contractRequest %s", contractRequest.getProviderId(), + format("[Client] Negotiation failed for provider %s and contractRequest %s", + contractRequest.getProviderId(), contractRequest.getContractOffer().getId()), negotiationException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) @@ -207,81 +214,75 @@ public Response negotiateContract(ContractRequest contractRequest) { public Response getData(@QueryParam("providerUrl") URL providerUrl, @QueryParam("agreementId") String agreementId, @QueryParam("assetId") String assetId, @QueryParam("dataDestinationUrl") URL dataDestinationUrl) { + monitor.debug(format("[Client] Received a %s GET request", TRANSFER_PATH)); Objects.requireNonNull(providerUrl, "providerUrl must not be null"); Objects.requireNonNull(agreementId, "agreementId must not be null"); Objects.requireNonNull(assetId, "assetId must not be null"); - if (Objects.isNull(dataDestinationUrl)) { - try { - var dataFuture = transferInitiator.initiateTransferProcess(providerUrl, agreementId, assetId); - var data = transferInitiator.waitForData(dataFuture, agreementId); + try { + var data = transferController.initiateTransferProcess(providerUrl, agreementId, assetId, dataDestinationUrl); + if (Objects.isNull(dataDestinationUrl)) { return Response.ok(data).build(); - - } catch (InterruptedException | ExecutionException negotiationException) { - monitor.severe(format("[Client] Getting data failed for provider %s and agreementId %s", providerUrl, - agreementId), negotiationException); - return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) - .build(); + } else { + return Response.ok(format("Data transfer request to URL %s sent.", dataDestinationUrl)).build(); } - } else { - var sinkAddress = HttpDataAddress.Builder.newInstance() - .baseUrl(dataDestinationUrl.toString()) + } catch (InterruptedException | ExecutionException negotiationException) { + monitor.severe(format("[Client] Data transfer failed for provider %s and agreementId %s", providerUrl, + agreementId), negotiationException); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(negotiationException.getMessage()) .build(); - // Don't need future as the EDC does not receive the data - transferInitiator.initiateTransferProcess(providerUrl, agreementId, assetId, sinkAddress); - return Response.ok(format("Data transfer request to URL %s sent.", dataDestinationUrl)).build(); } } /** - * Add policyDefinitions to the 'accepted list'. These policies or any other - * stored - * policy must be matched on automated contract negotiation. - * This means, any policyDefinition by a provider must have the same rules - * as any of the stored policyDefinitions. + * Adds an accepted contractOffer to match when checking a provider + * contractOffer. Only the policies' rules are relevant. * - * @param policyDefinitions The policyDefinitions to add (Only their rules are - * relevant) - * @return OK as response. + * @param policyDefinitions policies' rules that are acceptable for an automated + * contract negotiation */ @POST @Path(ACCEPTED_POLICIES_PATH) - public Response addAcceptedPolicies(PolicyDefinition[] policyDefinitions) { + public Response addAcceptedPolicyDefinitions(PolicyDefinition[] policyDefinitions) { + monitor.debug(format("[Client] Received a %s POST request", ACCEPTED_POLICIES_PATH)); + if (Objects.isNull(policyDefinitions)) { return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinitions array").build(); } monitor.info(format("[Client] Adding %s accepted contract offers", policyDefinitions.length)); - policyService.addAccepted(policyDefinitions); + policyController.addAcceptedPolicyDefinitions(policyDefinitions); return Response.ok().build(); } /** - * Returns all policyDefinitions in the 'accepted list'. + * Return accepted policyDefinitions * - * @return A list of accepted policyDefinitions + * @return Accepted policyDefinitions list */ @GET @Path(ACCEPTED_POLICIES_PATH) public Response getAcceptedPolicyDefinitions() { - monitor.info("[Client] Returning accepted policyDefinitions"); - return Response.ok(policyService.getAccepted()).build(); + monitor.debug(format("[Client] Received a %s GET request", ACCEPTED_POLICIES_PATH)); + return Response.ok(policyController.getAcceptedPolicyDefinitions()).build(); } /** - * Removes a policyDefinition from the 'accepted list'. + * Removes an accepted policyDefinitions. * - * @param policyDefinitionId The id of the policyDefinition to remove - * @return OK as response. + * @param policyDefinitions policyDefinition id of policyDefinition to be + * removed + * @return Optional containing removed policy definition or null */ @DELETE @Path(ACCEPTED_POLICIES_PATH) public Response deleteAcceptedPolicyDefinition(@QueryParam("policyDefinitionId") String policyDefinitionId) { - monitor.info(format("[Client] Removing policyDefinition with id %s", policyDefinitionId)); + monitor.debug( + format("[Client] Received a %s DELETE request for %s", ACCEPTED_POLICIES_PATH, policyDefinitionId)); if (Objects.isNull(policyDefinitionId)) { return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinitionId parameter").build(); } - var removed = policyService.removeAccepted(policyDefinitionId); + var removed = policyController.deleteAcceptedPolicyDefinition(policyDefinitionId); if (removed.isPresent()) { return Response.ok(policyDefinitionId).build(); @@ -290,24 +291,24 @@ public Response deleteAcceptedPolicyDefinition(@QueryParam("policyDefinitionId") } /** - * Updates a policyDefinition of the 'accepted list'. + * Updates an accepted policyDefinition. * - * @param policyDefinition The policyDefinition to update - * @return OK as response. + * @param policyDefinitionId PolicyDefinition id of policyDefinition to be + * updated + * @param policyDefinition Updated PolicyDefinition */ @PUT @Path(ACCEPTED_POLICIES_PATH) public Response updateAcceptedPolicyDefinition(PolicyDefinition policyDefinition) { + monitor.debug(format("[Client] Received a %s PUT request", ACCEPTED_POLICIES_PATH)); if (Objects.isNull(policyDefinition)) { return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinition").build(); } - monitor.info(format("[Client] Updating policyDefinition with id %s", policyDefinition.getId())); - var updated = policyService.updateAccepted(policyDefinition.getId(), policyDefinition); + var updated = policyController.updateAcceptedPolicyDefinition(policyDefinition); if (updated.isPresent()) { return Response.ok(policyDefinition.getId()).build(); } return Response.status(Response.Status.NOT_FOUND).entity("Unknown policyDefinitionId.").build(); } - } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java index 4fdeaa5d..fbef9f92 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java @@ -15,12 +15,6 @@ */ package de.fraunhofer.iosb.client; -import static java.lang.String.format; - -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; - import org.eclipse.edc.api.auth.spi.AuthenticationService; import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; @@ -28,96 +22,55 @@ import org.eclipse.edc.connector.spi.catalog.CatalogService; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; -import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.eclipse.edc.web.spi.WebService; -import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; -import de.fraunhofer.iosb.client.dataTransfer.DataTransferObservable; -import de.fraunhofer.iosb.client.dataTransfer.TransferInitiator; -import de.fraunhofer.iosb.client.negotiation.Negotiator; -import de.fraunhofer.iosb.client.policy.PolicyService; -import jakarta.ws.rs.core.UriBuilder; +import de.fraunhofer.iosb.client.dataTransfer.DataTransferController; +import de.fraunhofer.iosb.client.negotiation.NegotiationController; +import de.fraunhofer.iosb.client.policy.PolicyController; public class ClientExtension implements ServiceExtension { - @Inject - private AuthenticationService authenticationService; - @Inject - private CatalogService catalogService; - @Inject - private TypeTransformerRegistry transformer; - @Inject - private ConsumerContractNegotiationManager consumerNegotiationManager; - @Inject - private ContractNegotiationObservable contractNegotiationObservable; - @Inject - private ContractNegotiationStore contractNegotiationStore; - @Inject - private TransferProcessManager transferProcessManager; - @Inject - private WebService webService; - - public static final String SETTINGS_PREFIX = "edc.client."; - - private Monitor monitor; - - @Override - public void initialize(ServiceExtensionContext context) { - monitor = context.getMonitor(); - var config = context.getConfig(); + @Inject + private AuthenticationService authenticationService; + @Inject + private CatalogService catalogService; + @Inject + private TypeTransformerRegistry transformer; + @Inject + private ConsumerContractNegotiationManager consumerNegotiationManager; + @Inject + private ContractNegotiationObservable contractNegotiationObservable; + @Inject + private ContractNegotiationStore contractNegotiationStore; + @Inject + private TransferProcessManager transferProcessManager; + @Inject + private WebService webService; - var observable = new DataTransferObservable(monitor); - var authenticationRequestFilter = new CustomAuthenticationRequestFilter(monitor, - authenticationService); + public static final String SETTINGS_PREFIX = "edc.client."; - var policyService = new PolicyService(monitor, catalogService, transformer, config); + private Monitor monitor; - var negotiator = new Negotiator(consumerNegotiationManager, contractNegotiationObservable, - contractNegotiationStore, config); + @Override + public void initialize(ServiceExtensionContext context) { + monitor = context.getMonitor(); + var config = context.getConfig(); - var uri = createOwnUriFromConfigurationValues(config); - var transferInitiator = new TransferInitiator(config, authenticationRequestFilter, observable, uri, - transferProcessManager); + var policyController = new PolicyController(monitor, catalogService, transformer, config); - // TODO split up client Endpoint functionality? - var endpoint = new ClientEndpoint(monitor, negotiator, policyService, transferInitiator); + var negotiationController = new NegotiationController(consumerNegotiationManager, + contractNegotiationObservable, contractNegotiationStore, config); - webService.registerResource(endpoint); + var dataTransferController = new DataTransferController(monitor, config, webService, + authenticationService, transferProcessManager); - } + webService.registerResource(new ClientEndpoint(monitor, negotiationController, policyController, + dataTransferController)); - /* - * TODO Maybe there is another way to retrieve these values? - */ - private URI createOwnUriFromConfigurationValues(Config config) { - URL protocolAddress; - var protocolAddressString = config.getString("edc.dsp.callback.address"); - - try { - protocolAddress = new URL(protocolAddressString); - } catch (MalformedURLException idsWebhookAddressException) { - throw new EdcException( - format("[Client] Configuration value edc.dsp.callback.address is a malformed URL: %s", - protocolAddressString), - idsWebhookAddressException); } - int ownPort = Integer.parseInt(config.getString("web.http.port")); - String ownPath = config.getString("web.http.path"); - - var ownUriBuilder = UriBuilder.newInstance() - .scheme(protocolAddress.getProtocol()) - .host(protocolAddress.getHost()) - .port(ownPort) - .path(ownPath); - - return ownUriBuilder.build(); - - } - } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java new file mode 100644 index 00000000..2b0b7bdb --- /dev/null +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java @@ -0,0 +1,114 @@ +package de.fraunhofer.iosb.client.dataTransfer; + +import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; +import static java.lang.String.format; + +import java.net.URL; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; +import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.web.spi.WebService; + +import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; + +public class DataTransferController { + + static final String DATA_TRANSFER_API_KEY = "data-transfer-api-key"; + + private static final int WAIT_FOR_TRANSFER_TIMEOUT_DEFAULT = 10; + + private final Config config; + + private final DataTransferEndpoint dataTransferEndpoint; + private final DataTransferObservable dataTransferObservable; + private final TransferInitiator transferInitiator; + + private final CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter; + + /** + * Class constructor + * + * @param monitor Logging. + * @param config Read config value transfer timeout and own URI + * @param webService Register data transfer endpoint. + * @param dataEndpointAuthRequestFilter Creating and passing through custom api + * keys for each data transfer. + * @param transferProcessManager Initiating a transfer process as a + * consumer. + */ + public DataTransferController(Monitor monitor, Config config, WebService webService, + AuthenticationService authenticationService, TransferProcessManager transferProcessManager) { + this.config = config; + this.transferInitiator = new TransferInitiator(config, transferProcessManager); + this.dataEndpointAuthenticationRequestFilter = new CustomAuthenticationRequestFilter(monitor, + authenticationService); + + this.dataTransferObservable = new DataTransferObservable(monitor); + this.dataTransferEndpoint = new DataTransferEndpoint(monitor, dataTransferObservable); + webService.registerResource(dataTransferEndpoint); + } + + /** + * Initiates the transfer process defined by the arguments. The data of the + * transfer will be sent to {@link DataTransferEndpoint#RECEIVE_DATA_PATH}. + * + * @param providerUrl The provider from whom the data is to be fetched. + * @param agreementId Non-null ContractAgreement of the negotiation process. + * @param assetId The asset to be fetched. + * @param dataSinkAddress HTTPDataAddress the result of the transfer should be + * sent to. (If null, send to extension and print in log) + * + * @return A completable future whose result will be the data or an error + * message. + * @throws InterruptedException If the data transfer was interrupted + * @throws ExecutionException If the data transfer process failed + */ + public String initiateTransferProcess(URL providerUrl, String agreementId, String assetId, + URL dataDestinationUrl) throws InterruptedException, ExecutionException { + // Prepare for incoming data + var dataFuture = new CompletableFuture(); + dataTransferObservable.register(dataFuture, agreementId); + + if (Objects.isNull(dataDestinationUrl)) { + var apiKey = UUID.randomUUID().toString(); + dataEndpointAuthenticationRequestFilter.addTemporaryApiKey(DATA_TRANSFER_API_KEY, apiKey); + + this.transferInitiator.initiateTransferProcess(providerUrl, agreementId, assetId, apiKey); + return waitForData(dataFuture, agreementId); + } else { + var dataSinkAddress = HttpDataAddress.Builder.newInstance() + .baseUrl(dataDestinationUrl.toString()) + .build(); + + this.transferInitiator.initiateTransferProcess(providerUrl, agreementId, assetId, dataSinkAddress); + return null; + } + + } + + private String waitForData(CompletableFuture dataFuture, String agreementId) + throws InterruptedException, ExecutionException { + var waitForTransferTimeout = config.getInteger(SETTINGS_PREFIX + "getWaitForTransferTimeout", + WAIT_FOR_TRANSFER_TIMEOUT_DEFAULT); + try { + // Fetch TransferTimeout everytime to adapt to runtime config changes + var data = dataFuture.get(waitForTransferTimeout, TimeUnit.SECONDS); + dataTransferObservable.unregister(agreementId); + return data; + } catch (TimeoutException transferTimeoutExceededException) { + dataTransferObservable.unregister(agreementId); + throw new EdcException(format("Waiting for an transfer failed for agreementId: %s", agreementId), + transferTimeoutExceededException); + } + } +} diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java index 8c26e297..fcf6b1fe 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferObservable.java @@ -26,13 +26,13 @@ /** * Gets notified about incoming data by a provider connector. */ -public class DataTransferObservable { +class DataTransferObservable { private final Monitor monitor; private final Map> observers; - public DataTransferObservable(Monitor monitor) { + DataTransferObservable(Monitor monitor) { this.monitor = monitor; observers = new ConcurrentHashMap<>(); } @@ -43,7 +43,7 @@ public DataTransferObservable(Monitor monitor) { * @param observer The future to complete if data transfer is finished. * @param agreementId The agreement ID this future is dependent on. */ - public void register(CompletableFuture observer, String agreementId) { + void register(CompletableFuture observer, String agreementId) { observers.put(agreementId, observer); } @@ -52,7 +52,7 @@ public void register(CompletableFuture observer, String agreementId) { * * @param agreementId Identifier of the observer. */ - public void unregister(String agreementId) { + void unregister(String agreementId) { observers.remove(agreementId); } @@ -63,7 +63,7 @@ public void unregister(String agreementId) { * @param agreementId The agreementId coming with a provider's data transfer * @param data Any data by a provider connector */ - public void update(String agreementId, String data) { + void update(String agreementId, String data) { if (!observers.containsKey(agreementId)) { monitor.warning(format( "A POST request to the client's data transfer endpoint with an unknown agreementID was caught. " + diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java index f4bb1834..e9708499 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java @@ -15,78 +15,40 @@ */ package de.fraunhofer.iosb.client.dataTransfer; -import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; -import de.fraunhofer.iosb.client.ClientEndpoint; +import static de.fraunhofer.iosb.client.dataTransfer.DataTransferController.DATA_TRANSFER_API_KEY; +import static java.lang.String.format; +import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; +import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; + +import java.net.URI; +import java.net.URL; +import java.util.UUID; + import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.connector.transfer.spi.types.TransferRequest; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.DataAddress; -import java.net.URI; -import java.net.URL; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; -import static java.lang.String.format; -import static org.eclipse.edc.protocol.dsp.spi.types.HttpMessageProtocol.DATASPACE_PROTOCOL_HTTP; -import static org.eclipse.edc.spi.CoreConstants.EDC_NAMESPACE; +import de.fraunhofer.iosb.client.ClientEndpoint; +import jakarta.ws.rs.core.UriBuilder; /** * Initiate transfer requests */ -public class TransferInitiator { +class TransferInitiator { - private static final String DATA_TRANSFER_API_KEY = "data-transfer-api-key"; - private static final int WAIT_FOR_TRANSFER_TIMEOUT_DEFAULT = 10; - - private final DataTransferObservable observable; private final TransferProcessManager transferProcessManager; private final URI ownUri; - private final CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter; - private final Config config; - - /** - * Class constructor - * - * @param ownUri URL of this running EDC. - * @param transferProcessManager Initiating a transfer process - * as a consumer. - * @param observable Status updates for waiting - * data transfer requesters to - * avoid busy waiting. - * @param dataEndpointAuthenticationRequestFilter Creating and passing through - * custom api keys for each data - * transfer - */ - public TransferInitiator(Config config, CustomAuthenticationRequestFilter dataEndpointAuthenticationRequestFilter, - DataTransferObservable observable, URI ownUri, TransferProcessManager transferProcessManager) { - this.config = config; - this.dataEndpointAuthenticationRequestFilter = dataEndpointAuthenticationRequestFilter; - this.ownUri = ownUri.resolve(format("./%s/%s/%s", ownUri.getPath(), ClientEndpoint.AUTOMATED_PATH, - DataTransferEndpoint.RECEIVE_DATA_PATH)); - this.observable = observable; + + TransferInitiator(Config config, + TransferProcessManager transferProcessManager) { + + this.ownUri = createOwnUriFromConfigurationValues(config); this.transferProcessManager = transferProcessManager; } - /** - * Initiates the transfer process defined by the arguments. The data of the - * transfer will be sent to {@link DataTransferEndpoint#RECEIVE_DATA_PATH}. - * - * @param providerUrl The provider from whom the data is to be fetched. - * @param agreementId Non-null ContractAgreement of the negotiation process. - * @param assetId The asset to be fetched. - * @return A completable future whose result will be the data or an error - * message. - */ - public CompletableFuture initiateTransferProcess(URL providerUrl, String agreementId, String assetId) { - var apiKey = UUID.randomUUID().toString(); - dataEndpointAuthenticationRequestFilter.addTemporaryApiKey(DATA_TRANSFER_API_KEY, apiKey); - + void initiateTransferProcess(URL providerUrl, String agreementId, String assetId, String apiKey) { var dataDestination = DataAddress.Builder.newInstance() .type("HttpData") .property(EDC_NAMESPACE + "baseUrl", ownUri.toString()) @@ -94,26 +56,10 @@ public CompletableFuture initiateTransferProcess(URL providerUrl, String .property("header:" + DATA_TRANSFER_API_KEY, apiKey) // API key for validation on consumer side .build(); - return initiateTransferProcess(providerUrl, agreementId, assetId, dataDestination); + initiateTransferProcess(providerUrl, agreementId, assetId, dataDestination); } - /** - * Initiates the transfer process defined by the arguments. The data of the - * transfer will be sent to {@link DataTransferEndpoint#RECEIVE_DATA_PATH}. - * - * @param providerUrl The provider from whom the data is to be fetched. - * @param agreementId Non-null ContractAgreement of the negotiation process. - * @param assetId The asset to be fetched. - * @param dataSinkAddress HTTPDataAddress the result of the transfer should be - * sent to. - * @return A completable future whose result will be the data or an error - * message. - */ - public CompletableFuture initiateTransferProcess(URL providerUrl, String agreementId, String assetId, - DataAddress dataSinkAddress) { - // Prepare for incoming data - var dataFuture = new CompletableFuture(); - observable.register(dataFuture, agreementId); + void initiateTransferProcess(URL providerUrl, String agreementId, String assetId, DataAddress dataSinkAddress) { var transferRequest = TransferRequest.Builder.newInstance() .id(UUID.randomUUID().toString()) // this is not relevant, thus can be random @@ -129,32 +75,22 @@ public CompletableFuture initiateTransferProcess(URL providerUrl, String if (transferProcessStatus.failed()) { throw new EdcException(transferProcessStatus.getFailureDetail()); } - return dataFuture; - } - /** - * Call this with a future received by initiateTransferProcess() - * - * @param dataFuture Data future created by initiateTransferProcess method - * @param agreementId AgreementId corresponding to this transfer - * @return The data - * @throws InterruptedException If the future was interrupted - * @throws ExecutionException If the data transfer process failed - */ - public String waitForData(CompletableFuture dataFuture, String agreementId) - throws InterruptedException, ExecutionException { - try { - // Fetch TransferTimeout everytime to adapt to runtime config changes - var data = dataFuture.get( - config.getInteger(SETTINGS_PREFIX + "getWaitForTransferTimeout", WAIT_FOR_TRANSFER_TIMEOUT_DEFAULT), - TimeUnit.SECONDS); - observable.unregister(agreementId); - return data; - } catch (TimeoutException transferTimeoutExceededException) { - observable.unregister(agreementId); - throw new EdcException(format("Waiting for an transfer failed for agreementId: %s", agreementId), - transferTimeoutExceededException); - } + private URI createOwnUriFromConfigurationValues(Config config) { + var protocolAddressString = config.getString("edc.dsp.callback.address"); + var ownPort = config.getInteger("web.http.port"); + var ownPath = config.getString("web.http.path"); + + return UriBuilder + .fromUri(protocolAddressString) + .port(ownPort) + .path(format( + "%s/%s/%s", + ownPath, + ClientEndpoint.AUTOMATED_PATH, + DataTransferEndpoint.RECEIVE_DATA_PATH)) + .build(); } + } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java new file mode 100644 index 00000000..76045d33 --- /dev/null +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client.negotiation; + +import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; +import static java.lang.String.format; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; +import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; +import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; +import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; +import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; + +/** + * Provides API for contract negotiation by + * {@link de.fraunhofer.iosb.client.negotiation.Negotiator the Negotiator + * class}. + * For documentation see {@link de.fraunhofer.iosb.client.ClientEndpoint} + */ +public class NegotiationController { + + // How long the client waits for a negotiation to finish (seconds) + private static final int WAIT_FOR_AGREEMENT_TIMEOUT_DEFAULT = 10; + + private final Config config; + + private final Negotiator negotiator; + private final ClientContractNegotiationListener listener; + + public NegotiationController(ConsumerContractNegotiationManager consumerNegotiationManager, + ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore, + Config config) { + this.config = config; + this.negotiator = new Negotiator(consumerNegotiationManager, contractNegotiationStore, config); + this.listener = new ClientContractNegotiationListener(); + observable.registerListener(listener); + } + + public ContractAgreement negotiateContract(ContractRequest contractRequest) + throws InterruptedException, ExecutionException { + + var negotiationStatusResult = negotiator.negotiate(contractRequest); + + if (negotiationStatusResult.succeeded()) { + return waitForAgreement(negotiationStatusResult.getContent().getId()); + } else { + throw new EdcException(negotiationStatusResult.getFailureDetail()); + } + } + + private ContractAgreement waitForAgreement(String negotiationId) throws InterruptedException, ExecutionException { + var agreementFuture = new CompletableFuture(); + var timeout = config.getInteger(SETTINGS_PREFIX + "waitForAgreementTimeout", + WAIT_FOR_AGREEMENT_TIMEOUT_DEFAULT); + + listener.addListener(negotiationId, agreementFuture); + + try { + var negotiation = agreementFuture.get(timeout, TimeUnit.SECONDS); + listener.removeListener(negotiationId); + + return negotiation.getContractAgreement(); + } catch (TimeoutException agreementTimeout) { + throw new EdcException( + format("[Client] Agreement negotiation timed out for negotiation id: %s", negotiationId), + agreementTimeout); + } + } +} diff --git a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java index e10cb104..ae2018a2 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/Negotiator.java @@ -15,36 +15,23 @@ */ package de.fraunhofer.iosb.client.negotiation; +import java.util.concurrent.ExecutionException; + import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; -import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation; import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest; -import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.query.QuerySpec; +import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.spi.types.domain.agreement.ContractAgreement; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; -import static java.lang.String.format; /** * Send contractrequest, negotiation status watch */ public class Negotiator { - // How long the client waits for a negotiation to finish (seconds) - private static final int WAIT_FOR_AGREEMENT_TIMEOUT_DEFAULT = 10; - private final ConsumerContractNegotiationManager consumerNegotiationManager; - private final ClientContractNegotiationListener listener; private final ContractNegotiationStore contractNegotiationStore; - private final Config config; /** * Class constructor @@ -56,30 +43,20 @@ public class Negotiator { * negotiating */ public Negotiator(ConsumerContractNegotiationManager consumerNegotiationManager, - ContractNegotiationObservable observable, ContractNegotiationStore contractNegotiationStore, - Config config) { + ContractNegotiationStore contractNegotiationStore, Config config) { this.consumerNegotiationManager = consumerNegotiationManager; this.contractNegotiationStore = contractNegotiationStore; - this.config = config; - - listener = new ClientContractNegotiationListener(); - observable.registerListener(listener); } - /** - * Negotiate a contract agreement using the given contract offer if no agreement - * exists for this constellation. - * - * @param contractRequest The contract request to be sent. - * @return contractAgreement of the completed negotiation. - * @throws ExecutionException Attempted to retrieve the agreementId but the - * task aborted by throwing an exception. This - * exception can be inspected using the getCause() - * method. - * @throws InterruptedException Thread for agreementId was waiting, sleeping, or - * otherwise occupied, and was interrupted. + /* + * InterruptedException: Thread for agreementId was waiting, sleeping, or + * otherwise occupied, and was interrupted. + * + * ExecutionException: Attempted to retrieve the agreementId but the task + * aborted by throwing an exception. This exception can be inspected using the + * getCause() method. */ - public ContractAgreement negotiate(ContractRequest contractRequest) + StatusResult negotiate(ContractRequest contractRequest) throws InterruptedException, ExecutionException { var previousAgreements = contractNegotiationStore.queryAgreements(QuerySpec.max()); var relevantAgreements = previousAgreements @@ -88,33 +65,12 @@ public ContractAgreement negotiate(ContractRequest contractRequest) .toList(); if (!relevantAgreements.isEmpty()) { - return relevantAgreements.get(0); // assuming contractNegotiationStore removes invalid agreements + // assuming contractNegotiationStore removes invalid agreements + return StatusResult.success( + ContractNegotiation.Builder.newInstance().contractAgreement(relevantAgreements.get(0)).build()); } - var result = consumerNegotiationManager.initiate(contractRequest); - if (result.succeeded()) { - return waitForAgreement(result.getContent().getId()); - } else { - throw new EdcException(result.getFailureDetail()); - } + return consumerNegotiationManager.initiate(contractRequest); } - private ContractAgreement waitForAgreement(String negotiationId) throws InterruptedException, ExecutionException { - var agreementFuture = new CompletableFuture(); - var timeout = config.getInteger(SETTINGS_PREFIX + "waitForAgreementTimeout", - WAIT_FOR_AGREEMENT_TIMEOUT_DEFAULT); - - listener.addListener(negotiationId, agreementFuture); - - try { - var negotiation = agreementFuture.get(timeout, TimeUnit.SECONDS); - listener.removeListener(negotiationId); - - return negotiation.getContractAgreement(); - } catch (TimeoutException agreementTimeout) { - throw new EdcException( - format("[Client] Agreement negotiation timed out for negotiation id: %s", negotiationId), - agreementTimeout); - } - } } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java new file mode 100644 index 00000000..66608ba8 --- /dev/null +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyController.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package de.fraunhofer.iosb.client.policy; + +import java.net.URL; +import java.util.List; +import java.util.Optional; + +import org.eclipse.edc.catalog.spi.Dataset; +import org.eclipse.edc.connector.policy.spi.PolicyDefinition; +import org.eclipse.edc.connector.spi.catalog.CatalogService; +import org.eclipse.edc.policy.model.Policy; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.transform.spi.TypeTransformerRegistry; + +import de.fraunhofer.iosb.client.util.Pair; + +/** + * Provides API for accepted policy management and provider dataset retrieval. + * For documentation see {@link de.fraunhofer.iosb.client.ClientEndpoint} + */ +public class PolicyController { + + + private final PolicyServiceConfig config; + + private final PolicyDefinitionStore policyDefinitionStore; + private final PolicyService policyService; + + public PolicyController(Monitor monitor, CatalogService catalogService, + TypeTransformerRegistry typeTransformerRegistry, Config systemConfig) { + this.config = new PolicyServiceConfig(systemConfig); + + this.policyDefinitionStore = new PolicyDefinitionStore(monitor, this.config.getAcceptedPolicyDefinitionsPath()); + this.policyService = new PolicyService(catalogService, typeTransformerRegistry, this.config, + this.policyDefinitionStore); + } + + public Dataset getDataset(URL providerUrl, String assetId) throws InterruptedException { + return policyService.getDatasetForAssetId(providerUrl, assetId); + } + + /** + * Return policyDefinition for assetId that match any policyDefinitions' policy + * of + * the services' policyDefinitionStore instance containing user added + * policyDefinitions. + * If more than one policyDefinitions are provided by the provider + * connector, an AmbiguousOrNullException will be thrown. + * + * @param providerUrl Provider of the asset. + * @param assetId Asset ID of the asset whose contract should be fetched. + * @return One policyDefinition offered by the provider for the given assetId. + * @throws InterruptedException Thread for agreementId was waiting, sleeping, or + * otherwise occupied, and was + * interrupted. + */ + public Pair getAcceptablePolicyForAssetId(URL providerUrl, String assetId) + throws InterruptedException { + return policyService.getAcceptablePolicyForAssetId(providerUrl, assetId); + } + + public void addAcceptedPolicyDefinitions(PolicyDefinition[] policyDefinitions) { + policyDefinitionStore.putPolicyDefinitions(policyDefinitions); + } + + public List getAcceptedPolicyDefinitions() { + return policyDefinitionStore.getPolicyDefinitions(); + } + + public Optional deleteAcceptedPolicyDefinition(String policyDefinitionId) { + return policyDefinitionStore.removePolicyDefinition(policyDefinitionId); + } + + public Optional updateAcceptedPolicyDefinition(PolicyDefinition policyDefinition) { + return policyDefinitionStore.updatePolicyDefinitions(policyDefinition); + } + +} diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java index fa8636f7..ac02b672 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java @@ -19,6 +19,8 @@ import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.spi.monitor.Monitor; +import static java.lang.String.format; + import java.io.IOException; import java.nio.file.Path; import java.util.*; @@ -27,12 +29,12 @@ /** * Contains user added PolicyDefinitions. */ -public class PolicyDefinitionStore { +class PolicyDefinitionStore { private final Map policyDefinitions; private final Monitor monitor; private final ObjectMapper objectMapper = new ObjectMapper(); - public PolicyDefinitionStore(Monitor monitor, String acceptedPolicyDefinitionsPath) { + PolicyDefinitionStore(Monitor monitor, String acceptedPolicyDefinitionsPath) { this.monitor = monitor; this.policyDefinitions = new ConcurrentHashMap<>(); loadPolicyDefinitions(acceptedPolicyDefinitionsPath); @@ -43,7 +45,7 @@ public PolicyDefinitionStore(Monitor monitor, String acceptedPolicyDefinitionsPa * * @return Stored PolicyDefinitions (non-null but possibly empty) */ - public List getPolicyDefinitions() { + List getPolicyDefinitions() { return new ArrayList<>(policyDefinitions.values()); } @@ -52,7 +54,7 @@ public List getPolicyDefinitions() { * * @param newPolicyDefinitions PolicyDefinitions to be stored (non-null) */ - public void putPolicyDefinitions(PolicyDefinition... newPolicyDefinitions) { + void putPolicyDefinitions(PolicyDefinition... newPolicyDefinitions) { Objects.requireNonNull(newPolicyDefinitions, "newPolicyDefinitions is null"); for (PolicyDefinition newPolicyDefinition : newPolicyDefinitions) { if (!policyDefinitions.containsKey(newPolicyDefinition.getId())) { @@ -67,7 +69,7 @@ public void putPolicyDefinitions(PolicyDefinition... newPolicyDefinitions) { * @param policyDefinitionId policyDefinition ID (non null) * @return Optional containing removed policy definition or null */ - public Optional removePolicyDefinition(String policyDefinitionId) { + Optional removePolicyDefinition(String policyDefinitionId) { Objects.requireNonNull(policyDefinitionId, "policyDefinitionId is null"); return Optional.ofNullable(policyDefinitions.remove(policyDefinitionId)); } @@ -79,8 +81,8 @@ public Optional removePolicyDefinition(String policyDefinition * @param policyDefinition The updated policyDefinition * @return Optional containing updated policy definition or null */ - public Optional updatePolicyDefinitions(String policyDefinitionId, - PolicyDefinition policyDefinition) { + Optional updatePolicyDefinitions(PolicyDefinition policyDefinition) { + var policyDefinitionId = policyDefinition.getId(); Objects.requireNonNull(policyDefinitionId, "contractOfferId is null"); Objects.requireNonNull(policyDefinition, "contractOffer is null"); if (policyDefinitions.containsKey(policyDefinitionId)) { @@ -89,7 +91,7 @@ public Optional updatePolicyDefinitions(String policyDefinitio return Optional.empty(); } - private void loadPolicyDefinitions(String acceptedPolicyDefinitionsPath) { + void loadPolicyDefinitions(String acceptedPolicyDefinitionsPath) { Path path; if (Objects.nonNull(acceptedPolicyDefinitionsPath)) { path = Path.of(acceptedPolicyDefinitionsPath); @@ -97,10 +99,11 @@ private void loadPolicyDefinitions(String acceptedPolicyDefinitionsPath) { var acceptedPolicyDefinitions = objectMapper.readValue(path.toFile(), PolicyDefinition[].class); putPolicyDefinitions(acceptedPolicyDefinitions); - } catch (IOException e) { + } catch (IOException loadAcceptedPolicyException) { monitor.warning( - "[Client] Could not load accepted ContractOffers (edc.aas.client.acceptedContractOfferPaths)", - e); + format("[Client] Could not load accepted ContractOffers from %s", + acceptedPolicyDefinitionsPath), + loadAcceptedPolicyException); } } } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java index 7e3938d1..9cf0049a 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyService.java @@ -36,7 +36,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -47,15 +46,12 @@ import org.eclipse.edc.catalog.spi.DataService; import org.eclipse.edc.catalog.spi.Dataset; import org.eclipse.edc.catalog.spi.Distribution; -import org.eclipse.edc.connector.policy.spi.PolicyDefinition; import org.eclipse.edc.connector.spi.catalog.CatalogService; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.policy.model.Rule; import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.query.QuerySpec; import org.eclipse.edc.spi.response.StatusResult; -import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; @@ -71,12 +67,12 @@ /** * Finds out policy for a given asset id and provider EDC url */ -public class PolicyService { - - private final PolicyServiceConfig config; +class PolicyService { private final CatalogService catalogService; private final TypeTransformerRegistry transformer; + + private final PolicyServiceConfig config; private final PolicyDefinitionStore policyDefinitionStore; /** @@ -85,27 +81,16 @@ public class PolicyService { * @param catalogService Fetching the catalog of a provider. * @param transformer Transform json-ld byte-array catalog to catalog class */ - public PolicyService(Monitor monitor, CatalogService catalogService, TypeTransformerRegistry transformer, - Config systemConfig) { - this.config = new PolicyServiceConfig(systemConfig); - this.policyDefinitionStore = new PolicyDefinitionStore(monitor, this.config.getAcceptedPolicyDefinitionsPath()); - + public PolicyService(CatalogService catalogService, TypeTransformerRegistry transformer, + PolicyServiceConfig config, PolicyDefinitionStore policyDefinitionStore) { this.catalogService = catalogService; this.transformer = transformer; + this.config = config; + this.policyDefinitionStore = policyDefinitionStore; } - /** - * Returns dataset for this asset by this provider. - * - * @param providerUrl Provider of the asset. - * @param assetId Asset ID of the asset whose contract should be fetched. - * @return A list of dataset offered by the provider for the given - * assetId. - * @throws InterruptedException Thread for agreementId was waiting, sleeping, or - * otherwise occupied, and was interrupted. - */ - public Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws InterruptedException { + Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws InterruptedException { var catalogFuture = catalogService.requestCatalog( providerUrl.toString(), DATASPACE_PROTOCOL_HTTP, @@ -154,22 +139,7 @@ public Dataset getDatasetForAssetId(URL providerUrl, String assetId) throws Inte return dataset; } - /** - * Return policyDefinition for assetId that match any policyDefinitions' policy - * of - * the services' policyDefinitionStore instance containing user added - * policyDefinitions. - * If more than one policyDefinitions are provided by the provider - * connector, an AmbiguousOrNullException will be thrown. - * - * @param providerUrl Provider of the asset. - * @param assetId Asset ID of the asset whose contract should be fetched. - * @return One policyDefinition offered by the provider for the given assetId. - * @throws InterruptedException Thread for agreementId was waiting, sleeping, or - * otherwise occupied, and was - * interrupted. - */ - public Pair getAcceptablePolicyForAssetId(URL providerUrl, String assetId) + Pair getAcceptablePolicyForAssetId(URL providerUrl, String assetId) throws InterruptedException { var dataset = getDatasetForAssetId(providerUrl, assetId); @@ -194,48 +164,6 @@ public Pair getAcceptablePolicyForAssetId(URL providerUrl, Strin .first(acceptablePolicy.getKey()).second(acceptablePolicy.getValue()).build(); } - /** - * Adds an accepted contractOffer to match when checking a provider - * contractOffer. Only the policies' rules are relevant. - * - * @param policyDefinitions policies' rules that are acceptable for an automated - * contract negotiation - */ - public void addAccepted(PolicyDefinition[] policyDefinitions) { - policyDefinitionStore.putPolicyDefinitions(policyDefinitions); - } - - /** - * Return accepted policyDefinitions - * - * @return Accepted policyDefinitions list - */ - public List getAccepted() { - return policyDefinitionStore.getPolicyDefinitions(); - } - - /** - * Removes an accepted policyDefinitions. - * - * @param policyDefinitions policyDefinition id of policyDefinition to be - * removed - * @return Optional containing removed policy definition or null - */ - public Optional removeAccepted(String policyDefinitions) { - return policyDefinitionStore.removePolicyDefinition(policyDefinitions); - } - - /** - * Updates an accepted policyDefinition. - * - * @param policyDefinitionId PolicyDefinition id of policyDefinition to be - * updated - * @param policyDefinition Updated PolicyDefinition - */ - public Optional updateAccepted(String policyDefinitionId, PolicyDefinition policyDefinition) { - return policyDefinitionStore.updatePolicyDefinitions(policyDefinitionId, policyDefinition); - } - private boolean matchesOwnPolicyDefinitions(Policy policy) { return policyDefinitionStore.getPolicyDefinitions().stream().anyMatch( acceptedPolicyDefinition -> policyDefinitionRulesEquality(acceptedPolicyDefinition.getPolicy(), From d3807a834f3dbbbb70bb7cced976aa9c09e720e4 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:40:18 +0100 Subject: [PATCH 05/20] Add warning if own uri could not be built --- .../dataTransfer/DataTransferController.java | 5 ++- .../dataTransfer/TransferInitiator.java | 40 +++++++++++++------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java index 2b0b7bdb..02f008c6 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java @@ -39,7 +39,8 @@ public class DataTransferController { * Class constructor * * @param monitor Logging. - * @param config Read config value transfer timeout and own URI + * @param config Read config value transfer timeout and + * own URI * @param webService Register data transfer endpoint. * @param dataEndpointAuthRequestFilter Creating and passing through custom api * keys for each data transfer. @@ -49,7 +50,7 @@ public class DataTransferController { public DataTransferController(Monitor monitor, Config config, WebService webService, AuthenticationService authenticationService, TransferProcessManager transferProcessManager) { this.config = config; - this.transferInitiator = new TransferInitiator(config, transferProcessManager); + this.transferInitiator = new TransferInitiator(config, monitor, transferProcessManager); this.dataEndpointAuthenticationRequestFilter = new CustomAuthenticationRequestFilter(monitor, authenticationService); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java index e9708499..70ec7a92 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java @@ -22,16 +22,19 @@ import java.net.URI; import java.net.URL; +import java.util.Objects; import java.util.UUID; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.connector.transfer.spi.types.TransferRequest; import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.types.domain.DataAddress; import de.fraunhofer.iosb.client.ClientEndpoint; import jakarta.ws.rs.core.UriBuilder; +import jakarta.ws.rs.core.UriBuilderException; /** * Initiate transfer requests @@ -39,16 +42,22 @@ class TransferInitiator { private final TransferProcessManager transferProcessManager; + private final Monitor monitor; private final URI ownUri; - TransferInitiator(Config config, + TransferInitiator(Config config, Monitor monitor, TransferProcessManager transferProcessManager) { - + this.monitor = monitor; this.ownUri = createOwnUriFromConfigurationValues(config); this.transferProcessManager = transferProcessManager; } void initiateTransferProcess(URL providerUrl, String agreementId, String assetId, String apiKey) { + if (Objects.isNull(ownUri)) { + monitor.warning( + "Cannot transfer to own EDC since own URI could not be built while initializing client extension. Not continuing..."); + return; + } var dataDestination = DataAddress.Builder.newInstance() .type("HttpData") .property(EDC_NAMESPACE + "baseUrl", ownUri.toString()) @@ -81,16 +90,23 @@ private URI createOwnUriFromConfigurationValues(Config config) { var protocolAddressString = config.getString("edc.dsp.callback.address"); var ownPort = config.getInteger("web.http.port"); var ownPath = config.getString("web.http.path"); - - return UriBuilder - .fromUri(protocolAddressString) - .port(ownPort) - .path(format( - "%s/%s/%s", - ownPath, - ClientEndpoint.AUTOMATED_PATH, - DataTransferEndpoint.RECEIVE_DATA_PATH)) - .build(); + try { + return UriBuilder + .fromUri(protocolAddressString) + .port(ownPort) + .path(format( + "%s/%s/%s", + ownPath, + ClientEndpoint.AUTOMATED_PATH, + DataTransferEndpoint.RECEIVE_DATA_PATH)) + .build(); + + } catch (UriBuilderException ownUriBuilderException) { + monitor.severe( + "Could not build own URI, thus cannot transfer data to this EDC. Only data transfers to external endpoints are supported. Exception thrown:", + ownUriBuilderException); + } + return null; } } From a8333bf4265b52aab5a379b14c129f3ebd70db10 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:40:35 +0100 Subject: [PATCH 06/20] Adapt tests to new structure again --- .../iosb/client/ClientEndpointTest.java | 77 +++++++++++-------- .../dataTransfer/TransferInitiatorTest.java | 42 ++++++---- .../client/negotiation/NegotiatorTest.java | 10 +-- .../iosb/client/policy/PolicyServiceTest.java | 14 +++- 4 files changed, 85 insertions(+), 58 deletions(-) diff --git a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java index 888ced3b..c0da8c39 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java @@ -15,13 +15,24 @@ */ package de.fraunhofer.iosb.client; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; -import de.fraunhofer.iosb.client.dataTransfer.DataTransferObservable; -import de.fraunhofer.iosb.client.dataTransfer.TransferInitiator; -import de.fraunhofer.iosb.client.negotiation.Negotiator; -import de.fraunhofer.iosb.client.policy.PolicyService; -import jakarta.ws.rs.core.Response; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeoutException; + +import org.eclipse.edc.api.auth.spi.AuthenticationService; import org.eclipse.edc.catalog.spi.Catalog; import org.eclipse.edc.catalog.spi.Dataset; import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; @@ -39,28 +50,23 @@ import org.eclipse.edc.spi.response.ResponseStatus; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.eclipse.edc.spi.types.domain.asset.Asset; import org.eclipse.edc.spi.types.domain.offer.ContractOffer; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; -import org.junit.jupiter.api.*; +import org.eclipse.edc.web.spi.WebService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockserver.integration.ClientAndServer; -import java.io.IOException; -import java.net.URI; -import java.net.URL; -import java.util.ArrayList; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeoutException; +import com.fasterxml.jackson.databind.ObjectMapper; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import de.fraunhofer.iosb.client.dataTransfer.DataTransferController; +import de.fraunhofer.iosb.client.negotiation.NegotiationController; +import de.fraunhofer.iosb.client.policy.PolicyController; +import jakarta.ws.rs.core.Response; public class ClientEndpointTest { @@ -96,24 +102,29 @@ public static void initialize() throws IOException { @BeforeEach public void setup() throws IOException { clientEndpoint = new ClientEndpoint(monitor, - new Negotiator( + new NegotiationController( mockConsumerNegotiationManager(), mock(ContractNegotiationObservable.class), mock(ContractNegotiationStore.class), - mock(Config.class)), - new PolicyService( + mockConfig()), + new PolicyController( monitor, mockCatalogService(), mockTransformer(), mock(Config.class)), - new TransferInitiator( - mock(Config.class), - mock(CustomAuthenticationRequestFilter.class), - mock(DataTransferObservable.class), - URI.create("http://localhost:8181/api"), + new DataTransferController( + mock(Monitor.class), + mockConfig(), + mock(WebService.class), + mock(AuthenticationService.class), mockTransferProcessManager())); } + private Config mockConfig() { + return ConfigFactory.fromMap(Map.of("edc.dsp.callback.address", "http://localhost:4321/dsp", + "web.http.port", "8080", "web.http.path", "/api")); + } + private TypeTransformerRegistry mockTransformer() { var mockTransformer = mock(TypeTransformerRegistry.class); when(mockTransformer.transform(any(), any())).thenReturn(null); @@ -207,7 +218,7 @@ public void addAcceptedContractOffersTest() { mockPolicyDefinitionsAsList.add(mockPolicyDefinition); // ClientEndpoint creates ArrayList var offers = new PolicyDefinition[] { mockPolicyDefinition }; - clientEndpoint.addAcceptedPolicies(offers); + clientEndpoint.addAcceptedPolicyDefinitions(offers); assertEquals(mockPolicyDefinitionsAsList, clientEndpoint.getAcceptedPolicyDefinitions().getEntity()); } @@ -216,7 +227,7 @@ public void addAcceptedContractOffersTest() { public void updateAcceptedContractOfferTest() { var offers = new PolicyDefinition[] { mockPolicyDefinition }; - clientEndpoint.addAcceptedPolicies(offers); + clientEndpoint.addAcceptedPolicyDefinitions(offers); var mockPolicy = Policy.Builder.newInstance().build(); var mockUpdatedContractOffer = PolicyDefinition.Builder.newInstance() diff --git a/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java index aa2741dc..488f1df6 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiatorTest.java @@ -15,47 +15,57 @@ */ package de.fraunhofer.iosb.client.dataTransfer; -import de.fraunhofer.iosb.client.authentication.CustomAuthenticationRequestFilter; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Map; +import java.util.UUID; + +import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.connector.transfer.spi.types.TransferProcess; import org.eclipse.edc.spi.EdcException; +import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.system.configuration.Config; -import org.eclipse.edc.connector.dataplane.http.spi.HttpDataAddress; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; - -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.*; - public class TransferInitiatorTest { private final TransferProcessManager mockTransferProcessManager = mock(TransferProcessManager.class); + private Config configMock; + private TransferInitiator transferInitiator; private StatusResult mockStatusResult; @BeforeEach @SuppressWarnings("unchecked") void initializeContractOfferService() throws URISyntaxException { - URI ownUri = new URI("http://localhost:4321/api/ids"); - transferInitiator = new TransferInitiator(mock(Config.class), mock(CustomAuthenticationRequestFilter.class), - mock(DataTransferObservable.class), ownUri, mockTransferProcessManager); + configMock = ConfigFactory.fromMap(Map.of("edc.dsp.callback.address", "http://localhost:4321/dsp", + "web.http.port", "8080", "web.http.path", "/api")); + + transferInitiator = new TransferInitiator(configMock, mock(Monitor.class), mockTransferProcessManager); mockStatusResult = (StatusResult) mock(StatusResult.class); - + when(mockTransferProcessManager.initiateConsumerRequest(any())).thenReturn(mockStatusResult); } @Test void testInitiateTransferProcess() throws MalformedURLException { when(mockStatusResult.failed()).thenReturn(false); + transferInitiator.initiateTransferProcess(new URL("http://provider-url:1234"), "test-agreement-id", - "test-asset"); + "test-asset", UUID.randomUUID().toString()); verify(mockTransferProcessManager, times(1)).initiateConsumerRequest(any()); } @@ -73,7 +83,7 @@ void testInitiateTransferProcessThrowsEdcExceptionOnFailedTransferInitiation() t when(mockStatusResult.failed()).thenReturn(true); try { transferInitiator.initiateTransferProcess(new URL("http://provider-url:1234"), "test-agreement-id", - "test-asset"); + "test-asset", UUID.randomUUID().toString()); fail(); } catch (EdcException expected) { } diff --git a/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java index 60563233..b99e093f 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/negotiation/NegotiatorTest.java @@ -62,7 +62,7 @@ public class NegotiatorTest { @BeforeEach void initializeClientNegotiator() { defineMockBehaviour(); - clientNegotiator = new Negotiator(ccnmMock, contractNegotiationObservable, cnsMock, configMock); + clientNegotiator = new Negotiator(ccnmMock, cnsMock, configMock); } void defineMockBehaviour() { @@ -106,10 +106,10 @@ void testNegotiate() throws MalformedURLException, ExecutionException, Interrupt contractNegotiationObservable.invokeForEach(listener -> listener.finalized(negotiation)); assertNotNull(future); - var agreement = future.get(); - assertNotNull(agreement); - assertEquals(mockPolicy, agreement.getPolicy()); - assertEquals(assetId, agreement.getAssetId()); + var contractNegotiation = future.get(); + assertNotNull(contractNegotiation); + assertEquals(mockPolicy, contractNegotiation.getContent().getContractAgreement().getPolicy()); + assertEquals(assetId, contractNegotiation.getContent().getContractAgreement().getAssetId()); } /* diff --git a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java index ffb5d296..d1286017 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java @@ -24,6 +24,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -34,10 +35,9 @@ import org.eclipse.edc.connector.spi.catalog.CatalogService; import org.eclipse.edc.policy.model.Policy; import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.response.StatusResult; import org.eclipse.edc.spi.result.Result; -import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -47,7 +47,6 @@ public class PolicyServiceTest { private final int providerPort = 54321; - private final Monitor mockMonitor = mock(Monitor.class); private final CatalogService mockCatalogService = mock(CatalogService.class); private final TypeTransformerRegistry mockTransformer = mock(TypeTransformerRegistry.class); @@ -59,7 +58,8 @@ public PolicyServiceTest() throws MalformedURLException { @BeforeEach void initializeContractOfferService() { - policyService = new PolicyService(mockMonitor, mockCatalogService, mockTransformer, mock(Config.class)); + policyService = new PolicyService(mockCatalogService, mockTransformer, mockConfig(), + mock(PolicyDefinitionStore.class)); } @Test @@ -100,4 +100,10 @@ void getContractUnreachableProviderTest() throws MalformedURLException, Interrup } } + private PolicyServiceConfig mockConfig() { + return new PolicyServiceConfig( + ConfigFactory.fromMap(Map.of("edc.dsp.callback.address", "http://localhost:4321/dsp", + "web.http.port", "8080", "web.http.path", "/api"))); + + } } From 54774aa65e034580180b69ab3c1f9de85a1e7ad3 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:11:10 +0100 Subject: [PATCH 07/20] Make client extension runnable --- client/build.gradle.kts | 1 - .../services}/org.eclipse.edc.spi.system.ServiceExtension | 0 edc-extension4aas/build.gradle.kts | 1 - example/build.gradle.kts | 1 + 4 files changed, 1 insertion(+), 2 deletions(-) rename client/src/main/resources/{ => META-INF/services}/org.eclipse.edc.spi.system.ServiceExtension (100%) diff --git a/client/build.gradle.kts b/client/build.gradle.kts index 8e6f6115..7ae43a80 100644 --- a/client/build.gradle.kts +++ b/client/build.gradle.kts @@ -8,7 +8,6 @@ val edcVersion: String by project val rsApi: String by project val mockitoVersion: String by project val mockserverVersion: String by project -val metaModelVersion: String by project java { toolchain { diff --git a/client/src/main/resources/org.eclipse.edc.spi.system.ServiceExtension b/client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension similarity index 100% rename from client/src/main/resources/org.eclipse.edc.spi.system.ServiceExtension rename to client/src/main/resources/META-INF/services/org.eclipse.edc.spi.system.ServiceExtension diff --git a/edc-extension4aas/build.gradle.kts b/edc-extension4aas/build.gradle.kts index 1656d1ca..1718982e 100644 --- a/edc-extension4aas/build.gradle.kts +++ b/edc-extension4aas/build.gradle.kts @@ -10,7 +10,6 @@ val okHttpVersion: String by project val rsApi: String by project val mockitoVersion: String by project val mockserverVersion: String by project -val metaModelVersion: String by project java { toolchain { diff --git a/example/build.gradle.kts b/example/build.gradle.kts index 4a9f4e03..8996c6df 100644 --- a/example/build.gradle.kts +++ b/example/build.gradle.kts @@ -19,6 +19,7 @@ val edcVersion: String by project dependencies { implementation(project(":edc-extension4aas")) + implementation(project(":client")) implementation("$group:control-plane-core:$edcVersion") implementation("$group:dsp:$edcVersion") From 0df9362c8a10b1a7ea369b16dc11bd1a5b9bd7df Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:11:24 +0100 Subject: [PATCH 08/20] Remove API auth log output --- .../CustomAuthenticationRequestFilter.java | 11 +++++------ .../CustomAuthenticationRequestFilter.java | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java b/client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java index 43217725..63b8a74e 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/authentication/CustomAuthenticationRequestFilter.java @@ -34,12 +34,12 @@ */ public class CustomAuthenticationRequestFilter extends AuthenticationRequestFilter { - private final Monitor logger; + private final Monitor monitor; private final Map tempKeys; - public CustomAuthenticationRequestFilter(Monitor logger, AuthenticationService authenticationService) { + public CustomAuthenticationRequestFilter(Monitor monitor, AuthenticationService authenticationService) { super(authenticationService); - this.logger = logger; + this.monitor = monitor; tempKeys = new ConcurrentHashMap<>(); } @@ -68,14 +68,13 @@ public void filter(ContainerRequestContext requestContext) { && requestContext.getHeaderString(key).equals(tempKeys.get(key)) && requestPath.startsWith( format("%s/%s", ClientEndpoint.AUTOMATED_PATH, DataTransferEndpoint.RECEIVE_DATA_PATH))) { - logger.debug( - format("CustomAuthenticationRequestFilter: Data Transfer request with custom api key %s", key)); + monitor.debug( + format("[Client] Data Transfer request with custom api key %s", key)); tempKeys.remove(key); return; } } - logger.debug("CustomAuthenticationRequestFilter: Intercepting this request"); super.filter(requestContext); } } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java index 427659b2..818b6f61 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilter.java @@ -58,7 +58,6 @@ public void filter(ContainerRequestContext requestContext) { } } - LOGGER.debug("CustomAuthenticationRequestFilter: Intercepting this request"); super.filter(requestContext); } } From 22a2973bceb19fe44bbe93edb8c843d371d0310f Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:11:49 +0100 Subject: [PATCH 09/20] Add defaults for config values --- .../iosb/client/dataTransfer/DataTransferEndpoint.java | 2 +- .../iosb/client/dataTransfer/TransferInitiator.java | 10 +++++----- .../iosb/client/policy/PolicyServiceConfig.java | 4 +++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java index e71573aa..8b0cc590 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferEndpoint.java @@ -58,7 +58,7 @@ public DataTransferEndpoint(Monitor monitor, DataTransferObservable observable) @POST @Path("receiveData/{agreement}") public Response receiveData(@PathParam("agreement") String agreementId, String requestBody) { - monitor.info(format("Receiving data for agreement %s...", agreementId)); + monitor.info(format("[Client] Receiving data for agreement %s...", agreementId)); Objects.requireNonNull(agreementId); Objects.requireNonNull(requestBody); observable.update(agreementId, requestBody); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java index 70ec7a92..b3439bf4 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java @@ -87,9 +87,9 @@ void initiateTransferProcess(URL providerUrl, String agreementId, String assetId } private URI createOwnUriFromConfigurationValues(Config config) { - var protocolAddressString = config.getString("edc.dsp.callback.address"); - var ownPort = config.getInteger("web.http.port"); - var ownPath = config.getString("web.http.path"); + var protocolAddressString = config.getString("edc.dsp.callback.address", null); + var ownPort = config.getInteger("web.http.port", -1); + var ownPath = config.getString("web.http.path", null); try { return UriBuilder .fromUri(protocolAddressString) @@ -101,9 +101,9 @@ private URI createOwnUriFromConfigurationValues(Config config) { DataTransferEndpoint.RECEIVE_DATA_PATH)) .build(); - } catch (UriBuilderException ownUriBuilderException) { + } catch (IllegalArgumentException | UriBuilderException ownUriBuilderException) { monitor.severe( - "Could not build own URI, thus cannot transfer data to this EDC. Only data transfers to external endpoints are supported. Exception thrown:", + "[Client] Could not build own URI, thus cannot transfer data to this EDC. Only data transfers to external endpoints are supported. Exception thrown:", ownUriBuilderException); } return null; diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java index 577898a9..4b4dcf34 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java @@ -26,6 +26,7 @@ public class PolicyServiceConfig { private static final boolean ACCEPT_ALL_PROVIDER_OFFERS_DEFAULT = false; private static final int WAIT_FOR_CATALOG_TIMEOUT_DEFAULT = 10; + private static final String ACCEPTED_POLICY_DEFINITIONS_PATH_DEFAULT = null; private final Config config; @@ -42,7 +43,8 @@ int getWaitForCatalogTimeout() { } String getAcceptedPolicyDefinitionsPath() { - return config.getString(SETTINGS_PREFIX + "acceptedPolicyDefinitionsPath"); + return config.getString(SETTINGS_PREFIX + "acceptedPolicyDefinitionsPath", + ACCEPTED_POLICY_DEFINITIONS_PATH_DEFAULT); } } From 440afa6cb92033cde9807f5c886171163324fa0e Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:13:56 +0100 Subject: [PATCH 10/20] Update log output --- .../main/java/de/fraunhofer/iosb/client/ClientEndpoint.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java index c999ca9e..06d48803 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java @@ -247,9 +247,8 @@ public Response addAcceptedPolicyDefinitions(PolicyDefinition[] policyDefinition monitor.debug(format("[Client] Received a %s POST request", ACCEPTED_POLICIES_PATH)); if (Objects.isNull(policyDefinitions)) { - return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinitions array").build(); + return Response.status(Response.Status.BAD_REQUEST).entity("Missing request body").build(); } - monitor.info(format("[Client] Adding %s accepted contract offers", policyDefinitions.length)); policyController.addAcceptedPolicyDefinitions(policyDefinitions); return Response.ok().build(); @@ -302,7 +301,7 @@ public Response deleteAcceptedPolicyDefinition(@QueryParam("policyDefinitionId") public Response updateAcceptedPolicyDefinition(PolicyDefinition policyDefinition) { monitor.debug(format("[Client] Received a %s PUT request", ACCEPTED_POLICIES_PATH)); if (Objects.isNull(policyDefinition)) { - return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinition").build(); + return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinition as request body").build(); } var updated = policyController.updateAcceptedPolicyDefinition(policyDefinition); From b1d08d28a1c0c0ae4f193f40809d137b1872f7b9 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:50:03 +0100 Subject: [PATCH 11/20] Update config value names --- README.md | 8 ++++---- example/README.md | 4 ++-- example/configurations/consumer-https.properties | 8 ++++---- example/configurations/consumer.properties | 10 +++++----- example/dataspaceconnector-configuration.properties | 6 +++--- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 797f4830..8e45668b 100644 --- a/README.md +++ b/README.md @@ -101,10 +101,10 @@ Provide digital twin (AAS) data to business partners in Data Spaces like Catena- | Key | Value Type | Description | |:------------------------------------------|:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| edc.aas.client.waitForAgreementTimeout | whole number in seconds | How long should the extension wait for an agreement when automatically negotiating a contract? Default value is 10(s). | -| edc.aas.client.waitForTransferTimeout | whole number in seconds | How long should the extension wait for a data transfer when automatically negotiating a contract? Default value is 10(s). | -| edc.aas.client.acceptAllProviderOffers | boolean | If true, the client accepts any contractOffer offered by a provider connector on automated contract negotiation (e.g., trusted provider). Default value: false | -| edc.aas.client.acceptedContractOffersPath | path | Path pointing to a JSON-file containing acceptable ContractOffers for automated contract negotiation in a list (only policies must match in a provider's ContractOffer) | +| edc.client.waitForAgreementTimeout | whole number in seconds | How long should the extension wait for an agreement when automatically negotiating a contract? Default value is 10(s). | +| edc.client.waitForTransferTimeout | whole number in seconds | How long should the extension wait for a data transfer when automatically negotiating a contract? Default value is 10(s). | +| edc.client.acceptAllProviderOffers | boolean | If true, the client accepts any contractOffer offered by a provider connector on automated contract negotiation (e.g., trusted provider). Default value: false | +| edc.client.acceptedContractOffersPath | path | Path pointing to a JSON-file containing acceptable ContractOffers for automated contract negotiation in a list (only policies must match in a provider's ContractOffer) | ## Terminology diff --git a/example/README.md b/example/README.md index 4f73a9a0..0d7d82d4 100644 --- a/example/README.md +++ b/example/README.md @@ -119,11 +119,11 @@ request located in `/examples/resources`. Do the following steps: __Important__: -- If the (consumer's) config value `edc.aas.client.acceptAllProviderOffers` is set to `true`: This command will fetch +- If the (consumer's) config value `edc.client.acceptAllProviderOffers` is set to `true`: This command will fetch a provider policy for the selected asset and accept it as is. If no policies exist for this asset, no negotiation will take place. -- If the (consumer's) config value `edc.aas.client.acceptAllProviderOffers` is set to `false` (default): This command +- If the (consumer's) config value `edc.client.acceptAllProviderOffers` is set to `false` (default): This command will request the provider's offered policy for the asset and check it against its own accepted policyDefinitions by comparing the permissions, prohibitions and obligations of both the provider's policyDefinition and the ones in the policyDefinitionStore. The assetID or other IDs must not be equal for the policyDefinitions to match. Initially, this diff --git a/example/configurations/consumer-https.properties b/example/configurations/consumer-https.properties index 0f1aecab..b033e8cd 100644 --- a/example/configurations/consumer-https.properties +++ b/example/configurations/consumer-https.properties @@ -6,10 +6,10 @@ edc.web.https.keystore.type=PKCS12 # IDS AAS Extension specific edc.aas.exposeSelfDescription=False # Timeouts in seconds -edc.aas.client.waitForAgreementTimeout=15 -edc.aas.client.waitForTransferTimeout=30 -edc.aas.client.acceptAllProviderOffers=true -edc.aas.client.acceptedPolicyDefinitionsPath=./example/resources/acceptedContractOffers.json +edc.client.waitForAgreementTimeout=15 +edc.client.waitForTransferTimeout=30 +edc.client.acceptAllProviderOffers=true +edc.client.acceptedPolicyDefinitionsPath=./example/resources/acceptedContractOffers.json # EDC specific values web.http.port=9191 web.http.path=/api diff --git a/example/configurations/consumer.properties b/example/configurations/consumer.properties index af726333..83c70eca 100644 --- a/example/configurations/consumer.properties +++ b/example/configurations/consumer.properties @@ -1,11 +1,11 @@ # EDC4AAS Extension specific edc.aas.exposeSelfDescription=false # Timeouts in seconds -edc.aas.client.waitForAgreementTimeout=15 -edc.aas.client.waitForTransferTimeout=30 -edc.aas.client.waitForCatalogTimeout=30 -edc.aas.client.acceptAllProviderOffers=true -# edc.aas.client.acceptedContractOffersPath = ./example/resources/acceptedContractOffers.json +edc.client.waitForAgreementTimeout=15 +edc.client.waitForTransferTimeout=30 +edc.client.waitForCatalogTimeout=30 +edc.client.acceptAllProviderOffers=true +# edc.client.acceptedContractOffersPath = ./example/resources/acceptedContractOffers.json # Port and path for e.g., this extension's SelfDescription web.http.port=9191 web.http.path=/api diff --git a/example/dataspaceconnector-configuration.properties b/example/dataspaceconnector-configuration.properties index 86ff082b..11c2e303 100644 --- a/example/dataspaceconnector-configuration.properties +++ b/example/dataspaceconnector-configuration.properties @@ -23,9 +23,9 @@ edc.web.rest.cors.enabled=true edc.web.rest.cors.origins=* edc.web.rest.cors.headers=x-api-key, content-type edc.web.rest.cors.methods=GET, POST, DELETE, PUT, OPTIONS -edc.aas.client.waitForTransferTimeout=100 -edc.aas.client.waitForAgreementTimeout=100 -edc.aas.client.acceptAllProviderOffers=true +edc.client.waitForTransferTimeout=100 +edc.client.waitForAgreementTimeout=100 +edc.client.acceptAllProviderOffers=true # EDC specific values web.http.port=9191 web.http.path=/api From 625092b148243826253852ef4c03517fa420b82d Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:50:52 +0100 Subject: [PATCH 12/20] Make logger lightweight ConsoleMonitor extension --- .../de/fraunhofer/iosb/app/AasExtension.java | 93 +++++++++---------- .../java/de/fraunhofer/iosb/app/Endpoint.java | 41 ++++---- .../java/de/fraunhofer/iosb/app/Logger.java | 69 ++------------ .../de/fraunhofer/iosb/app/aas/AasAgent.java | 6 +- .../iosb/app/aas/FaaastServiceManager.java | 2 +- .../iosb/app/controller/AasController.java | 8 +- .../controller/ConfigurationController.java | 47 +++++++--- .../iosb/app/edc/ContractHandler.java | 2 +- .../model/configuration/Configuration.java | 52 +---------- .../iosb/app/model/ids/SelfDescription.java | 4 +- .../fraunhofer/iosb/app/util/Transformer.java | 2 +- .../de/fraunhofer/iosb/app/EndpointTest.java | 40 ++++---- .../fraunhofer/iosb/app/aas/AasAgentTest.java | 32 +++---- .../app/aas/FaaastServiceManagerTest.java | 28 ++---- ...CustomAuthenticationRequestFilterTest.java | 34 +++---- .../iosb/app/edc/ContractHandlerTest.java | 20 ++-- .../iosb/app/sync/SynchronizerTest.java | 36 +++---- 17 files changed, 207 insertions(+), 309 deletions(-) diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java index c10e3d32..13044252 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java @@ -15,6 +15,8 @@ */ package de.fraunhofer.iosb.app; +import java.io.IOException; +import java.nio.file.Path; import java.util.Objects; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; @@ -29,10 +31,6 @@ import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.web.spi.WebService; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; - import de.fraunhofer.iosb.app.authentication.CustomAuthenticationRequestFilter; import de.fraunhofer.iosb.app.controller.AasController; import de.fraunhofer.iosb.app.controller.ConfigurationController; @@ -64,72 +62,67 @@ public class AasExtension implements ServiceExtension { private static final Logger logger = Logger.getInstance(); private final ScheduledExecutorService syncExecutor = new ScheduledThreadPoolExecutor(1); private AasController aasController; + private ConfigurationController configurationController; @Override public void initialize(ServiceExtensionContext context) { - logger.setMonitor(context.getMonitor()); + this.configurationController = new ConfigurationController(context.getConfig(SETTINGS_PREFIX)); - // Distribute controllers, repositories + // Distribute controllers, repository var selfDescriptionRepository = new SelfDescriptionRepository(); - aasController = new AasController(okHttpClient); - var endpoint = new Endpoint(selfDescriptionRepository, aasController); - var synchronizer = new Synchronizer(selfDescriptionRepository, aasController, - new ResourceController(assetIndex, contractStore, policyStore)); - selfDescriptionRepository.registerListener(synchronizer); + this.aasController = new AasController(okHttpClient); + var endpoint = new Endpoint(selfDescriptionRepository, this.aasController, this.configurationController); + + // Initialize/Start synchronizer, start AAS services defined in configuration + initializeSynchronizer(selfDescriptionRepository); + registerServicesByConfig(selfDescriptionRepository); + + var authenticationRequestFilter = new CustomAuthenticationRequestFilter(authenticationService, + Configuration.getInstance().isExposeSelfDescription() ? Endpoint.SELF_DESCRIPTION_PATH : null); + + webService.registerResource(authenticationRequestFilter); + webService.registerResource(endpoint); + } - loadConfig(context); + private void registerServicesByConfig(SelfDescriptionRepository selfDescriptionRepository) { var configInstance = Configuration.getInstance(); - // Remote AAS service URL supplied? if (Objects.nonNull(configInstance.getRemoteAasLocation())) { - endpoint.postAasService(configInstance.getRemoteAasLocation()); + selfDescriptionRepository.createSelfDescription(configInstance.getRemoteAasLocation()); } - // AAS model supplied? if (Objects.nonNull(configInstance.getLocalAasModelPath())) { - endpoint.postAasEnvironment(configInstance.getLocalAasModelPath(), configInstance.getAasServiceConfigPath(), - configInstance.getLocalAasServicePort()); + try { + Path aasConfigPath = null; + if (Objects.nonNull(configInstance.getAasServiceConfigPath())) { + aasConfigPath = Path.of(configInstance.getAasServiceConfigPath()); + } + var serviceUrl = aasController.startService( + Path.of(configInstance.getLocalAasModelPath()), + configInstance.getLocalAasServicePort(), + aasConfigPath); + + selfDescriptionRepository.createSelfDescription(serviceUrl); + } catch (IOException startAASException) { + logger.warning("Could not start AAS service provided by configuration", startAASException); + } } - // Task: get all AAS service URLs, synchronize EDC and AAS - syncExecutor.scheduleAtFixedRate( - synchronizer::synchronize, - 1, - configInstance.getSyncPeriod(), TimeUnit.SECONDS); - - webService.registerResource(endpoint); - - var authenticationRequestFilter = new CustomAuthenticationRequestFilter(authenticationService, - configInstance.isExposeSelfDescription() ? Endpoint.SELF_DESCRIPTION_PATH : null); - webService.registerResource(authenticationRequestFilter); - } - /** - * Get extension specific configuration from EDC config object - * - * @param context EDC config reference provider - */ - private void loadConfig(ServiceExtensionContext context) { - var objectMapper = new ObjectMapper(); - objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - var config = context.getConfig(); - - String configAsString; - try { - configAsString = objectMapper.writeValueAsString(config.getRelativeEntries(SETTINGS_PREFIX)); - } catch (JsonProcessingException e) { - // This should not be reached, unless there is an error inside EDCs Config.java - logger.error("Could not load AAS extension configuration, using default values", e); - configAsString = ""; - } - new ConfigurationController().handleRequest(RequestType.PUT, null, configAsString); + private void initializeSynchronizer(SelfDescriptionRepository selfDescriptionRepository) { + var synchronizer = new Synchronizer(selfDescriptionRepository, aasController, + new ResourceController(assetIndex, contractStore, policyStore)); + selfDescriptionRepository.registerListener(synchronizer); + + // Task: get all AAS service URLs, synchronize EDC and AAS + syncExecutor.scheduleAtFixedRate(synchronizer::synchronize, 1, + Configuration.getInstance().getSyncPeriod(), TimeUnit.SECONDS); } @Override public void shutdown() { - logger.log("Shutting down EDC4AAS extension..."); + logger.info("Shutting down EDC4AAS extension..."); syncExecutor.shutdown(); aasController.stopServices(); } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java index 8d0d0e1b..15672b0e 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Endpoint.java @@ -36,8 +36,8 @@ /** * Delegates (HTTP) Requests to controllers. */ -@Consumes({MediaType.APPLICATION_JSON, MediaType.WILDCARD}) -@Produces({MediaType.APPLICATION_JSON}) +@Consumes({ MediaType.APPLICATION_JSON, MediaType.WILDCARD }) +@Produces({ MediaType.APPLICATION_JSON }) @Path("/") public class Endpoint { @@ -60,11 +60,12 @@ public class Endpoint { * @param selfDescriptionRepository Manage self descriptions * @param aasController Communication with AAS services */ - public Endpoint(SelfDescriptionRepository selfDescriptionRepository, AasController aasController) { + public Endpoint(SelfDescriptionRepository selfDescriptionRepository, AasController aasController, + ConfigurationController configurationController) { this.selfDescriptionRepository = Objects.requireNonNull(selfDescriptionRepository); this.aasController = Objects.requireNonNull(aasController); - this.configurationController = new ConfigurationController(); + this.configurationController = configurationController; objectMapper = new ObjectMapper(); } @@ -103,7 +104,7 @@ public Response putConfig(String newConfigurationJson) { @POST @Path(CLIENT_PATH) public Response postAasService(@QueryParam("url") URL aasServiceUrl) { - LOGGER.log("Received a client POST request"); + LOGGER.info("Received a client POST request"); Objects.requireNonNull(aasServiceUrl); if (Objects.nonNull(selfDescriptionRepository.getSelfDescription(aasServiceUrl))) { return Response.ok("Service was already registered at EDC").build(); @@ -129,9 +130,9 @@ public Response postAasService(@QueryParam("url") URL aasServiceUrl) { @POST @Path(ENVIRONMENT_PATH) public Response postAasEnvironment(@QueryParam("environment") String pathToEnvironment, - @QueryParam(CONFIG_PATH) String pathToAssetAdministrationShellConfig, - @QueryParam("port") int port) { - LOGGER.log("Received an environment POST request"); + @QueryParam(CONFIG_PATH) String pathToAssetAdministrationShellConfig, + @QueryParam("port") int port) { + LOGGER.info("Received an environment POST request"); Objects.requireNonNull(pathToEnvironment); URL newAssetAdministrationShellUrl; try { @@ -143,20 +144,20 @@ public Response postAasEnvironment(@QueryParam("environment") String pathToEnvir newAssetAdministrationShellUrl = aasController.startService(environmentPath, port, aasConfigPath); } catch (IOException aasServiceException) { - LOGGER.error(format("Could not start/read from AAS service. Message from AAS Service: %s", + LOGGER.severe(format("Could not start/read from AAS service. Message from AAS Service: %s", aasServiceException.getMessage())); return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), "Could not start AAS service. Check logs for details").build(); } catch (InvalidPathException invalidPathException) { - LOGGER.error("Could not resolve paths", invalidPathException); + LOGGER.severe("Could not resolve paths", invalidPathException); return Response.status(Response.Status.BAD_REQUEST).build(); } selfDescriptionRepository.createSelfDescription(newAssetAdministrationShellUrl); return Response.ok(format("%s\n%s\n%s: %s", - "Booted up and registered AAS service managed by extension.", - "Wait for next synchronization period for assetIndex and selfDescription.", - "URL of new AAS service", newAssetAdministrationShellUrl)) + "Booted up and registered AAS service managed by extension.", + "Wait for next synchronization period for assetIndex and selfDescription.", + "URL of new AAS service", newAssetAdministrationShellUrl)) .build(); } @@ -170,7 +171,7 @@ public Response postAasEnvironment(@QueryParam("environment") String pathToEnvir @DELETE @Path(CLIENT_PATH) public Response removeAasService(@QueryParam("url") URL aasServiceUrl) { - LOGGER.log("Received a client DELETE request"); + LOGGER.info("Received a client DELETE request"); Objects.requireNonNull(aasServiceUrl); if (Objects.isNull(selfDescriptionRepository.getSelfDescription(aasServiceUrl))) { @@ -196,7 +197,7 @@ public Response removeAasService(@QueryParam("url") URL aasServiceUrl) { @POST @Path(AAS_REQUEST_PATH) public Response postAasRequest(@QueryParam("requestUrl") URL requestUrl, String requestBody) { - LOGGER.log("Received an AAS POST request"); + LOGGER.info("Received an AAS POST request"); Objects.requireNonNull(requestUrl); Objects.requireNonNull(requestBody); return handleAasRequest(RequestType.POST, requestUrl, requestBody); @@ -213,7 +214,7 @@ public Response postAasRequest(@QueryParam("requestUrl") URL requestUrl, String @DELETE @Path(AAS_REQUEST_PATH) public Response deleteAasRequest(@QueryParam("requestUrl") URL requestUrl) { - LOGGER.log("Received an AAS DELETE request"); + LOGGER.info("Received an AAS DELETE request"); Objects.requireNonNull(requestUrl); return handleAasRequest(RequestType.DELETE, requestUrl, ""); } @@ -228,7 +229,7 @@ public Response deleteAasRequest(@QueryParam("requestUrl") URL requestUrl) { @PUT @Path(AAS_REQUEST_PATH) public Response putAasRequest(@QueryParam("requestUrl") URL requestUrl, String requestBody) { - LOGGER.log("Received an AAS PUT request"); + LOGGER.info("Received an AAS PUT request"); Objects.requireNonNull(requestUrl); Objects.requireNonNull(requestBody); return handleAasRequest(RequestType.PUT, requestUrl, requestBody); @@ -260,7 +261,7 @@ public Response getSelfDescription(@QueryParam("aasService") URL aasServiceUrl) if (Objects.nonNull(selfDescription)) { return Response.ok(selfDescription.toString()).build(); } else { - LOGGER.error(format("Self description with URL %s not found.", aasServiceUrl)); + LOGGER.severe(format("Self description with URL %s not found.", aasServiceUrl)); return Response.status(Status.NOT_FOUND).build(); } } @@ -274,10 +275,10 @@ private Response handleAasRequest(RequestType requestType, URL requestUrl, Strin try (var response = aasController.handleRequest(requestType, requestUrl, body)) { if (!response.getStatusInfo().getFamily().equals(Family.SUCCESSFUL)) { - LOGGER.error("AAS request failed. Response from URL: " + response.getStatusInfo()); + LOGGER.severe("AAS request failed. Response from URL: " + response.getStatusInfo()); return Response.status(response.getStatus()).build(); } - LOGGER.log("AAS request succeeded."); + LOGGER.info("AAS request succeeded."); } return Response.ok().build(); } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java index cefd70e1..f1745232 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/Logger.java @@ -15,20 +15,18 @@ */ package de.fraunhofer.iosb.app; -import org.eclipse.edc.spi.monitor.Monitor; - import java.util.Objects; +import java.util.function.Supplier; + +import org.eclipse.edc.spi.monitor.ConsoleMonitor; /** * Singleton class. - * Wrapper for prefix logging using - * org.eclipse.edc.spi.monitor.Monitor. + * Wrapper for logging with prefix + * ({@link org.eclipse.edc.spi.monitor.ConsoleMonitor}). */ -public class Logger { - private static final String PREFIX_SEPARATOR = " :: "; +public class Logger extends ConsoleMonitor { private static Logger instance; - private final String prefix = "EDC4AAS"; - private Monitor monitor; private Logger() { } @@ -46,57 +44,8 @@ public static Logger getInstance() { return instance; } - /** - * Only to be called by the extension itself. - * - * @param monitor The monitor used to log stuff from - */ - public void setMonitor(Monitor monitor) { - this.monitor = monitor; - } - - /** - * Log a message with severity "info" - * - * @param message Message to be logged - */ - public void log(String message) { - monitor.info(prefix + PREFIX_SEPARATOR + message); - } - - /** - * Log a message with severity "info" - * - * @param message Message to be logged - */ - public void log(String... message) { - monitor.info(prefix + PREFIX_SEPARATOR + String.join(" ", message)); - } - - /** - * Log a message with severity "debug" - * - * @param message Message to be logged - */ - public void debug(String message) { - monitor.debug(prefix + PREFIX_SEPARATOR + message); - } - - /** - * Log a message with severity "debug" - * - * @param message Message to be logged - */ - public void warn(String message, Throwable... errors) { - monitor.warning(prefix + PREFIX_SEPARATOR + message, errors); - } - - /** - * Log a message with severity "severe" - * - * @param message Message to be logged - */ - public void error(String message, Throwable... errors) { - monitor.severe(prefix + PREFIX_SEPARATOR + message, errors); + @Override + public String sanitizeMessage(Supplier supplier) { + return "EDC4AAS: " + super.sanitizeMessage(supplier); } } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java index 6c947ee4..e7ef9693 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/AasAgent.java @@ -69,7 +69,7 @@ public Response putModel(URL aasServiceUrl, String element) { try { response = Transformer.okHttpResponseToJakartaResponse(httpRestClient.put(aasServiceUrl, element)); } catch (IOException io) { - logger.error("Could not fetch AAS env from AAS service", io); + logger.severe("Could not fetch AAS env from AAS service", io); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } @@ -88,7 +88,7 @@ public Response postModel(URL aasServiceUrl, String element) { try { response = Transformer.okHttpResponseToJakartaResponse(httpRestClient.post(aasServiceUrl, element)); } catch (IOException io) { - logger.error("Could not fetch AAS env from AAS service", io); + logger.severe("Could not fetch AAS env from AAS service", io); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } @@ -108,7 +108,7 @@ public Response deleteModel(URL aasServiceUrl, String element) { try { response = Transformer.okHttpResponseToJakartaResponse(httpRestClient.delete(aasServiceUrl, element)); } catch (IOException io) { - logger.error("Could not fetch AAS env from AAS service", io); + logger.severe("Could not fetch AAS env from AAS service", io); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java index 41328e70..13071166 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/aas/FaaastServiceManager.java @@ -56,7 +56,7 @@ public URL startService(Path aasModelPath, int port) throws IOException { Objects.requireNonNull(aasModelPath); if (!isValidPort(port)) { var errorMessage = format("Port is not valid: (%s).", port); - logger.error(errorMessage); + logger.severe(errorMessage); throw new EdcException(errorMessage); } logger.debug(format("Booting up FA³ST service using AAS model path (%s) and service port (%s).", aasModelPath, diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/AasController.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/AasController.java index 921de8a2..c8602379 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/AasController.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/AasController.java @@ -90,12 +90,12 @@ public URL startService(Path aasModelPath, int aasServicePort, Path aasConfigPat Objects.requireNonNull(aasModelPath); if (Objects.isNull(aasConfigPath)) { - logger.log(format( + logger.info(format( "Booting up AAS service given AAS model path (%s)\n and service port (%s)\n...", aasModelPath, aasServicePort)); return aasServiceManager.startService(aasModelPath, aasServicePort); } - logger.log(format( + logger.info(format( "Booting up AAS service given AAS model path (%s)\n and service config path (%s)...", aasModelPath, aasConfigPath)); return aasServiceManager.startService(aasModelPath, aasConfigPath, aasServicePort); @@ -107,7 +107,7 @@ public URL startService(Path aasModelPath, int aasServicePort, Path aasConfigPat * @param aasServiceUrl URL of service to be stopped */ public void stopAssetAdministrationShellService(URL aasServiceUrl) { - logger.log(format("Shutting down AAS service with URL %s...", aasServiceUrl.toString())); + logger.info(format("Shutting down AAS service with URL %s...", aasServiceUrl.toString())); aasServiceManager.stopService(aasServiceUrl); } @@ -115,7 +115,7 @@ public void stopAssetAdministrationShellService(URL aasServiceUrl) { * Stops all internally started AAS services */ public void stopServices() { - logger.log("Shutting down all AAS services..."); + logger.info("Shutting down all AAS services..."); aasServiceManager.stopServices(); } } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java index 61931b3e..ce5af23f 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/controller/ConfigurationController.java @@ -15,7 +15,14 @@ */ package de.fraunhofer.iosb.app.controller; +import java.net.URL; +import java.util.Map; + +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; + import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; @@ -26,56 +33,72 @@ import de.fraunhofer.iosb.app.model.configuration.Configuration; import jakarta.ws.rs.core.Response; -import java.net.URL; - /** * Handles requests regarding the application's configuration. */ public class ConfigurationController implements Controllable { private final Logger logger; + private final Config sysConfig; private Configuration configuration; private final ObjectMapper objectMapper; private final ObjectReader objectReader; - public ConfigurationController() { + public ConfigurationController(Config config) { + this.sysConfig = config; logger = Logger.getInstance(); configuration = Configuration.getInstance(); objectMapper = JsonMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build(); objectReader = objectMapper.readerForUpdating(configuration); + + initializeConfiguration(); + } + + private void initializeConfiguration() { + try { + configuration = objectReader.readValue(objectMapper.writeValueAsString(sysConfig.getEntries())); + } catch (JsonProcessingException jsonProcessingException) { + logger.severe("Initiailzing AAS extension configuration failed", + jsonProcessingException); + } + } @Override public Response handleRequest(RequestType requestType, URL url, String... requestData) { return switch (requestType) { - case GET -> readConfiguration(); + case GET -> getConfiguration(); case PUT -> updateConfiguration(requestData[0]); default -> Response.status(Response.Status.NOT_IMPLEMENTED).build(); }; } - private Response readConfiguration() { + private Response getConfiguration() { try { var serializedConfiguration = objectMapper.writeValueAsString(configuration); return Response.status(Response.Status.OK).entity(serializedConfiguration).build(); } catch (JsonProcessingException jsonProcessingException) { - logger.error("Serialization of configuration object failed.\n", jsonProcessingException); + logger.severe("Serialization of configuration object failed.\n", jsonProcessingException); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } } private Response updateConfiguration(String newConfigValues) { - // A string containing the new configuration for the response - String newConfigurationAsString; try { - configuration = objectReader.readValue(newConfigValues); - newConfigurationAsString = objectMapper.writeValueAsString(configuration); + // Read config values as map -> edc Config -> merge with old + // -> set as AAS extension config + Config newConfig = ConfigFactory.fromMap(objectMapper.readValue(newConfigValues, + new TypeReference>() { + })); + Config mergedConfig = sysConfig.merge(newConfig); + configuration = objectReader.readValue(objectMapper.writeValueAsString(mergedConfig.getEntries())); } catch (JsonProcessingException jsonProcessingException) { - logger.error("Updating configuration to this configuration failed:\n" + newConfigValues, + logger.severe("Updating configuration to this configuration failed:\n" + newConfigValues, jsonProcessingException); return Response.status(Response.Status.BAD_REQUEST).build(); } - return Response.status(Response.Status.OK).entity(newConfigurationAsString).build(); + + return Response.status(Response.Status.OK).build(); } } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ContractHandler.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ContractHandler.java index 30e6a2d9..6bc3e9f7 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ContractHandler.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/edc/ContractHandler.java @@ -162,7 +162,7 @@ private Optional getPolicyDefinitionFromFile(String filePath) { Policy filePolicy = objectReader.readValue(Path.of(filePath).toFile()); return Optional.of(filePolicy); } catch (IOException ioException) { - logger.error( + logger.severe( format("Could not find a valid policy at path %s. Using internal default policy.", filePath), ioException); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java index c48f179b..5d1d37ee 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/configuration/Configuration.java @@ -15,11 +15,11 @@ */ package de.fraunhofer.iosb.app.model.configuration; -import com.fasterxml.jackson.annotation.JsonProperty; - import java.net.URL; import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * Singleton class. * The configuration of the application. @@ -53,22 +53,6 @@ public class Configuration { @JsonProperty(SETTINGS_PREFIX + "defaultContractPolicyPath") private String defaultContractPolicyPath; - @JsonProperty(SETTINGS_PREFIX + "client.waitForTransferTimeout") - private int waitForTransferTimeout = 10; // Seconds - - @JsonProperty(SETTINGS_PREFIX + "client.waitForAgreementTimeout") - private int waitForAgreementTimeout = 10; // Seconds - - @JsonProperty(SETTINGS_PREFIX + "client.waitForCatalogTimeout") - private int waitForCatalogTimeout = 10; // Seconds - - @JsonProperty(SETTINGS_PREFIX + "client.acceptAllProviderOffers") - private boolean acceptAllProviderOffers = false; - - @JsonProperty(SETTINGS_PREFIX + "client.acceptedPolicyDefinitionsPath") - private String acceptedPolicyDefinitionsPath; - - public static synchronized Configuration getInstance() { if (Objects.isNull(instance)) { instance = new Configuration(); @@ -76,68 +60,36 @@ public static synchronized Configuration getInstance() { return instance; } - public URL getRemoteAasLocation() { return remoteAasLocation; } - public String getLocalAasModelPath() { return localAasModelPath; } - public int getLocalAasServicePort() { return localAasServicePort; } - public String getAasServiceConfigPath() { return aasServiceConfigPath; } - public int getSyncPeriod() { return syncPeriod; } - public boolean isExposeSelfDescription() { return exposeSelfDescription; } - public String getDefaultAccessPolicyPath() { return defaultAccessPolicyPath; } - public String getDefaultContractPolicyPath() { return defaultContractPolicyPath; } - - public int getWaitForAgreementTimeout() { - return waitForAgreementTimeout; - } - - - public int getWaitForTransferTimeout() { - return waitForTransferTimeout; - } - - - public int getWaitForCatalogTimeout() { - return waitForCatalogTimeout; - } - - - public boolean isAcceptAllProviderOffers() { - return acceptAllProviderOffers; - } - - - public String getAcceptedPolicyDefinitionsPath() { - return acceptedPolicyDefinitionsPath; - } } diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/ids/SelfDescription.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/ids/SelfDescription.java index 4eb7e5ba..0402395c 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/ids/SelfDescription.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/model/ids/SelfDescription.java @@ -49,7 +49,7 @@ public String toString() { try { aasEnvString = objectMapper.writeValueAsString(aasEnv); } catch (JsonProcessingException e) { - LOGGER.error("Could not serialize self description", e); + LOGGER.severe("Could not serialize self description", e); return null; } return aasEnvString; @@ -60,7 +60,7 @@ public JsonNode toJsonNode() { try { aasEnvString = objectMapper.readTree(this.toString()); } catch (JsonProcessingException e) { - LOGGER.error("Could not serialize self description", e); + LOGGER.severe("Could not serialize self description", e); return null; } return aasEnvString; diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/Transformer.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/Transformer.java index 78e86b5e..b7f450b5 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/Transformer.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/util/Transformer.java @@ -40,7 +40,7 @@ public static Response okHttpResponseToJakartaResponse(okhttp3.Response response try { body = Objects.requireNonNull(response.body()).string(); } catch (IOException e) { - LOGGER.error("Failed transforming HTTP Response", e); + LOGGER.severe("Failed transforming HTTP Response", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } return Response.status(statusCode).entity(body).build(); diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java index c6b39b55..eff80344 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java @@ -15,6 +15,23 @@ */ package de.fraunhofer.iosb.app; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import org.eclipse.edc.spi.system.configuration.Config; +import org.eclipse.edc.spi.system.configuration.ConfigFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import de.fraunhofer.iosb.app.controller.AasController; import de.fraunhofer.iosb.app.controller.ConfigurationController; import de.fraunhofer.iosb.app.model.configuration.Configuration; @@ -23,19 +40,6 @@ import de.fraunhofer.iosb.app.util.Encoder; import jakarta.ws.rs.core.Response; import okhttp3.OkHttpClient; -import org.eclipse.edc.spi.monitor.Monitor; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; - -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; /** * Not mocking the controllers this endpoint uses, as the mocking/validation @@ -56,7 +60,6 @@ public class EndpointTest { @BeforeAll public static void initialize() throws MalformedURLException { - Logger.getInstance().setMonitor(mock(Monitor.class)); port = 8080; url = new URL(format("http://localhost:%s", port)); } @@ -67,7 +70,8 @@ public void setupEndpoint() { aasController = new AasController(new OkHttpClient()); endpoint = new Endpoint( selfDescriptionRepo, - aasController); + aasController, + new ConfigurationController(mock(Config.class))); } @AfterEach @@ -89,7 +93,7 @@ public void getSelfDescriptionTest() { @Test public void changeSingleConfigValueTest() { var config = FileManager.loadResource("config.json"); - var configController = new ConfigurationController(); + var configController = new ConfigurationController(ConfigFactory.empty()); configController.handleRequest(RequestType.PUT, null, config); configController.handleRequest(RequestType.PUT, null, @@ -148,8 +152,8 @@ public void putAasRequestTest() throws IOException { endpoint.postAasService(url); endpoint.putAasRequest(new URL(format(url.toString(), "/submodels/", - Encoder.encodeBase64("https://example.com/ids/sm/4445_8090_6012_7409"), - "/submodel-elements/GripperUp")), + Encoder.encodeBase64("https://example.com/ids/sm/4445_8090_6012_7409"), + "/submodel-elements/GripperUp")), FileManager.loadResource("submodelElement.json")); // Still null: not synchronized by Synchronizer diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java index 607071ae..d166d9db 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/AasAgentTest.java @@ -15,27 +15,26 @@ */ package de.fraunhofer.iosb.app.aas; -import com.fasterxml.jackson.databind.ObjectMapper; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.testUtils.FileManager; -import io.adminshell.aas.v3.dataformat.DeserializationException; -import okhttp3.OkHttpClient; -import org.eclipse.edc.spi.monitor.Monitor; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockserver.integration.ClientAndServer; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; +import com.fasterxml.jackson.databind.ObjectMapper; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockserver.integration.ClientAndServer.startClientAndServer; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; +import de.fraunhofer.iosb.app.testUtils.FileManager; +import io.adminshell.aas.v3.dataformat.DeserializationException; +import okhttp3.OkHttpClient; /** * Testing AAS Agent. Using mocked AAS service (HTTP endpoints) @@ -50,11 +49,6 @@ public class AasAgentTest { private AasAgent aasAgent; private static ClientAndServer mockServer; - @BeforeAll - public static void initializeLogger() { - Logger.getInstance().setMonitor(mock(Monitor.class)); - } - @BeforeAll public static void startMockServer() { mockServer = startClientAndServer(8080); diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java index 44f04fdf..926639ec 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/aas/FaaastServiceManagerTest.java @@ -15,35 +15,27 @@ */ package de.fraunhofer.iosb.app.aas; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.util.HttpRestClient; -import jakarta.ws.rs.core.Response; -import okhttp3.OkHttpClient; -import org.eclipse.edc.spi.EdcException; -import org.eclipse.edc.spi.monitor.Monitor; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.Mockito.mock; +import org.eclipse.edc.spi.EdcException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import de.fraunhofer.iosb.app.util.HttpRestClient; +import jakarta.ws.rs.core.Response; +import okhttp3.OkHttpClient; public class FaaastServiceManagerTest { private AssetAdministrationShellServiceManager faaastServiceManager; - @BeforeAll - public static void initializeLogger() { - Logger.getInstance().setMonitor(mock(Monitor.class)); - } - @BeforeEach public void initializeFaaastServiceManager() { faaastServiceManager = new FaaastServiceManager(); diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java index 046dc57d..56635dd1 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/authentication/CustomAuthenticationRequestFilterTest.java @@ -15,36 +15,32 @@ */ package de.fraunhofer.iosb.app.authentication; -import de.fraunhofer.iosb.app.Endpoint; -import de.fraunhofer.iosb.app.Logger; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.core.MultivaluedHashMap; -import jakarta.ws.rs.core.MultivaluedMap; -import jakarta.ws.rs.core.UriInfo; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import java.util.Objects; + import org.eclipse.edc.api.auth.spi.AuthenticationService; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.web.spi.exception.AuthenticationFailedException; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Map; -import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import de.fraunhofer.iosb.app.Endpoint; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.UriInfo; public class CustomAuthenticationRequestFilterTest { final AuthenticationService authService = mock(AuthenticationService.class); CustomAuthenticationRequestFilter authRequestFilter; - @BeforeAll - public static void initializeLogger() { - Logger.getInstance().setMonitor(mock(Monitor.class)); - } - @BeforeEach public void initializeTestObject() { authRequestFilter = new CustomAuthenticationRequestFilter(authService, "selfDescription"); diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java index fd2ee1ed..177652aa 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/edc/ContractHandlerTest.java @@ -15,19 +15,18 @@ */ package de.fraunhofer.iosb.app.edc; -import de.fraunhofer.iosb.app.Logger; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; -import org.eclipse.edc.spi.monitor.Monitor; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - public class ContractHandlerTest { private static final String DEFAULT_CONTRACT_NAME = "DEFAULT_CONTRACT"; @@ -35,11 +34,6 @@ public class ContractHandlerTest { private final ContractDefinitionStore mockedContractDefinitionStore = mock(ContractDefinitionStore.class); private final PolicyDefinitionStore mockedPolicyDefinitionStore = mock(PolicyDefinitionStore.class); - @BeforeAll - public static void initializeLogger() { - Logger.getInstance().setMonitor(mock(Monitor.class)); - } - @BeforeEach public void initializeAasAgent() { contractHandler = new ContractHandler(mockedContractDefinitionStore, mockedPolicyDefinitionStore); diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java index e0d7aaf4..3c63e0a8 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/sync/SynchronizerTest.java @@ -15,17 +15,23 @@ */ package de.fraunhofer.iosb.app.sync; -import de.fraunhofer.iosb.app.Logger; -import de.fraunhofer.iosb.app.controller.AasController; -import de.fraunhofer.iosb.app.controller.ResourceController; -import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository; -import de.fraunhofer.iosb.app.testUtils.FileManager; -import okhttp3.OkHttpClient; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; +import static org.mockserver.integration.ClientAndServer.startClientAndServer; +import static org.mockserver.model.HttpRequest.request; +import static org.mockserver.model.HttpResponse.response; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Objects; + import org.eclipse.edc.connector.contract.spi.offer.store.ContractDefinitionStore; import org.eclipse.edc.connector.policy.spi.store.PolicyDefinitionStore; import org.eclipse.edc.spi.EdcException; import org.eclipse.edc.spi.asset.AssetIndex; -import org.eclipse.edc.spi.monitor.Monitor; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -33,16 +39,11 @@ import org.mockserver.integration.ClientAndServer; import org.mockserver.matchers.Times; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Objects; - -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.mock; -import static org.mockserver.integration.ClientAndServer.startClientAndServer; -import static org.mockserver.model.HttpRequest.request; -import static org.mockserver.model.HttpResponse.response; +import de.fraunhofer.iosb.app.controller.AasController; +import de.fraunhofer.iosb.app.controller.ResourceController; +import de.fraunhofer.iosb.app.model.ids.SelfDescriptionRepository; +import de.fraunhofer.iosb.app.testUtils.FileManager; +import okhttp3.OkHttpClient; public class SynchronizerTest { @@ -62,7 +63,6 @@ public class SynchronizerTest { @BeforeAll public static void initialize() throws MalformedURLException { - Logger.getInstance().setMonitor(mock(Monitor.class)); port = 8080; url = new URL(format("http://localhost:%s", port)); } From fdd8e450e40eabf54d6ad55cf8521be916eb51ec Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:51:07 +0100 Subject: [PATCH 13/20] Load correct config in client --- .../iosb/client/ClientExtension.java | 4 ++-- .../dataTransfer/DataTransferController.java | 18 ++++++++++++++++-- .../client/dataTransfer/TransferInitiator.java | 4 ++-- .../negotiation/NegotiationController.java | 3 +-- .../client/policy/PolicyServiceConfig.java | 8 +++----- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java index fbef9f92..e332a5db 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java @@ -51,14 +51,14 @@ public class ClientExtension implements ServiceExtension { @Inject private WebService webService; - public static final String SETTINGS_PREFIX = "edc.client."; + private static final String SETTINGS_PREFIX = "edc.client."; private Monitor monitor; @Override public void initialize(ServiceExtensionContext context) { monitor = context.getMonitor(); - var config = context.getConfig(); + var config = context.getConfig(SETTINGS_PREFIX); var policyController = new PolicyController(monitor, catalogService, transformer, config); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java index 02f008c6..65c54a4d 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/DataTransferController.java @@ -1,6 +1,20 @@ +/* + * Copyright (c) 2021 Fraunhofer IOSB, eine rechtlich nicht selbstaendige + * Einrichtung der Fraunhofer-Gesellschaft zur Foerderung der angewandten + * Forschung e.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package de.fraunhofer.iosb.client.dataTransfer; -import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; import static java.lang.String.format; import java.net.URL; @@ -99,7 +113,7 @@ public String initiateTransferProcess(URL providerUrl, String agreementId, Strin private String waitForData(CompletableFuture dataFuture, String agreementId) throws InterruptedException, ExecutionException { - var waitForTransferTimeout = config.getInteger(SETTINGS_PREFIX + "getWaitForTransferTimeout", + var waitForTransferTimeout = config.getInteger("getWaitForTransferTimeout", WAIT_FOR_TRANSFER_TIMEOUT_DEFAULT); try { // Fetch TransferTimeout everytime to adapt to runtime config changes diff --git a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java index b3439bf4..72c149db 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/dataTransfer/TransferInitiator.java @@ -103,8 +103,8 @@ private URI createOwnUriFromConfigurationValues(Config config) { } catch (IllegalArgumentException | UriBuilderException ownUriBuilderException) { monitor.severe( - "[Client] Could not build own URI, thus cannot transfer data to this EDC. Only data transfers to external endpoints are supported. Exception thrown:", - ownUriBuilderException); + format("[Client] Could not build own URI, thus cannot transfer data to this EDC. Only data transfers to external endpoints are supported. Exception message: %s", + ownUriBuilderException.getMessage())); } return null; } diff --git a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java index 76045d33..795094e6 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/negotiation/NegotiationController.java @@ -15,7 +15,6 @@ */ package de.fraunhofer.iosb.client.negotiation; -import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; import static java.lang.String.format; import java.util.concurrent.CompletableFuture; @@ -71,7 +70,7 @@ public ContractAgreement negotiateContract(ContractRequest contractRequest) private ContractAgreement waitForAgreement(String negotiationId) throws InterruptedException, ExecutionException { var agreementFuture = new CompletableFuture(); - var timeout = config.getInteger(SETTINGS_PREFIX + "waitForAgreementTimeout", + var timeout = config.getInteger("waitForAgreementTimeout", WAIT_FOR_AGREEMENT_TIMEOUT_DEFAULT); listener.addListener(negotiationId, agreementFuture); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java index 4b4dcf34..29fc5c89 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyServiceConfig.java @@ -15,8 +15,6 @@ */ package de.fraunhofer.iosb.client.policy; -import static de.fraunhofer.iosb.client.ClientExtension.SETTINGS_PREFIX; - import org.eclipse.edc.spi.system.configuration.Config; /** @@ -35,15 +33,15 @@ public PolicyServiceConfig(Config config) { } boolean isAcceptAllProviderOffers() { - return config.getBoolean(SETTINGS_PREFIX + "acceptAllProviderOffers", ACCEPT_ALL_PROVIDER_OFFERS_DEFAULT); + return config.getBoolean("acceptAllProviderOffers", ACCEPT_ALL_PROVIDER_OFFERS_DEFAULT); } int getWaitForCatalogTimeout() { - return config.getInteger(SETTINGS_PREFIX + "waitForCatalogTimeout", WAIT_FOR_CATALOG_TIMEOUT_DEFAULT); + return config.getInteger("waitForCatalogTimeout", WAIT_FOR_CATALOG_TIMEOUT_DEFAULT); } String getAcceptedPolicyDefinitionsPath() { - return config.getString(SETTINGS_PREFIX + "acceptedPolicyDefinitionsPath", + return config.getString("acceptedPolicyDefinitionsPath", ACCEPTED_POLICY_DEFINITIONS_PATH_DEFAULT); } From 2b3f760bdf8ee6a88ddbfdadd820490f1953c6b8 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Tue, 28 Nov 2023 16:15:33 +0100 Subject: [PATCH 14/20] Fix endpoint test --- .../src/test/java/de/fraunhofer/iosb/app/EndpointTest.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java index eff80344..0621f1fc 100644 --- a/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java +++ b/edc-extension4aas/src/test/java/de/fraunhofer/iosb/app/EndpointTest.java @@ -19,13 +19,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; -import org.eclipse.edc.spi.system.configuration.Config; import org.eclipse.edc.spi.system.configuration.ConfigFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; @@ -71,7 +69,7 @@ public void setupEndpoint() { endpoint = new Endpoint( selfDescriptionRepo, aasController, - new ConfigurationController(mock(Config.class))); + new ConfigurationController(ConfigFactory.empty())); } @AfterEach From d5ada58397563418e94318337a2b005917e6618f Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:06:22 +0100 Subject: [PATCH 15/20] Code cleanup --- .../iosb/client/ClientEndpoint.java | 55 +++++++++---------- .../iosb/client/ClientExtension.java | 9 +-- .../client/policy/PolicyDefinitionStore.java | 6 +- 3 files changed, 31 insertions(+), 39 deletions(-) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java index 06d48803..b3b7b1d4 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientEndpoint.java @@ -87,7 +87,7 @@ public ClientEndpoint(Monitor monitor, } /** - * Return policyDefinition for assetId that match any policyDefinitions' policy + * Return dataset for assetId that match any policyDefinitions' policy * of the services' policyDefinitionStore instance containing user added * policyDefinitions. If more than one policyDefinitions are provided by the * provider connector, an AmbiguousOrNullException will be thrown. @@ -101,8 +101,7 @@ public ClientEndpoint(Monitor monitor, */ @GET @Path(DATASET_PATH) - public Response getDataset(@QueryParam("providerUrl") URL providerUrl, - @QueryParam("assetId") String assetId) { + public Response getDataset(@QueryParam("providerUrl") URL providerUrl, @QueryParam("assetId") String assetId) { monitor.debug(format("[Client] Received a %s GET request", DATASET_PATH)); if (Objects.isNull(providerUrl)) { @@ -125,7 +124,9 @@ public Response getDataset(@QueryParam("providerUrl") URL providerUrl, * exists for this constellation. * * @param providerUrl Provider EDCs URL (DSP endpoint) + * @param providerId Provider EDCs ID * @param assetId ID of the asset to be retrieved + * @param dataDestinationUrl URL of destination data sink. * @return Asset data */ @POST @@ -136,6 +137,7 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, @QueryParam("dataDestinationUrl") URL dataDestinationUrl) { monitor.debug(format("[Client] Received a %s POST request", NEGOTIATE_PATH)); Objects.requireNonNull(providerUrl, "Provider URL must not be null"); + Objects.requireNonNull(providerId, "Provider ID must not be null"); Objects.requireNonNull(assetId, "Asset ID must not be null"); Pair idPolicyPair; // id means contractOfferId @@ -175,7 +177,7 @@ public Response negotiateContract(@QueryParam("providerUrl") URL providerUrl, } /** - * Negotiate a contract agreement using the given contract offer if no agreement + * Negotiates a contract agreement using the given contract offer if no agreement * exists for this constellation. * * @param contractRequest The contract request to be sent. @@ -206,8 +208,8 @@ public Response negotiateContract(ContractRequest contractRequest) { * @param providerUrl The data provider's url * @param agreementId The basis of the data transfer. * @param assetId The asset of which the data should be transferred - * @return On success, the data of the desired asset. Else, returns an error - * message. + * @param dataDestinationUrl URL of destination data sink. + * @return On success, the data of the desired asset. Else, returns an error message. */ @GET @Path(TRANSFER_PATH) @@ -220,7 +222,8 @@ public Response getData(@QueryParam("providerUrl") URL providerUrl, Objects.requireNonNull(assetId, "assetId must not be null"); try { - var data = transferController.initiateTransferProcess(providerUrl, agreementId, assetId, dataDestinationUrl); + var data = transferController.initiateTransferProcess(providerUrl, agreementId, assetId, + dataDestinationUrl); if (Objects.isNull(dataDestinationUrl)) { return Response.ok(data).build(); } else { @@ -238,24 +241,21 @@ public Response getData(@QueryParam("providerUrl") URL providerUrl, * Adds an accepted contractOffer to match when checking a provider * contractOffer. Only the policies' rules are relevant. * - * @param policyDefinitions policies' rules that are acceptable for an automated - * contract negotiation + * @param policyDefinitions accepted policyDefinitions + * @return "OK"-response if requestBody is not empty */ @POST @Path(ACCEPTED_POLICIES_PATH) public Response addAcceptedPolicyDefinitions(PolicyDefinition[] policyDefinitions) { monitor.debug(format("[Client] Received a %s POST request", ACCEPTED_POLICIES_PATH)); - - if (Objects.isNull(policyDefinitions)) { - return Response.status(Response.Status.BAD_REQUEST).entity("Missing request body").build(); - } + Objects.requireNonNull(policyDefinitions, "policyDefinitions (request body) must not be null"); policyController.addAcceptedPolicyDefinitions(policyDefinitions); return Response.ok().build(); } /** - * Return accepted policyDefinitions + * Returns accepted policyDefinitions as list * * @return Accepted policyDefinitions list */ @@ -269,21 +269,18 @@ public Response getAcceptedPolicyDefinitions() { /** * Removes an accepted policyDefinitions. * - * @param policyDefinitions policyDefinition id of policyDefinition to be - * removed - * @return Optional containing removed policy definition or null + * @param policyDefinitionId ID of policyDefinition to be removed + * @return PolicyDefinitionId of removed policyDefinition or 404 */ @DELETE @Path(ACCEPTED_POLICIES_PATH) public Response deleteAcceptedPolicyDefinition(@QueryParam("policyDefinitionId") String policyDefinitionId) { monitor.debug( format("[Client] Received a %s DELETE request for %s", ACCEPTED_POLICIES_PATH, policyDefinitionId)); - if (Objects.isNull(policyDefinitionId)) { - return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinitionId parameter").build(); - } - var removed = policyController.deleteAcceptedPolicyDefinition(policyDefinitionId); + Objects.requireNonNull(policyDefinitionId, "policyDefinitionId must not be null"); - if (removed.isPresent()) { + if (policyController.deleteAcceptedPolicyDefinition(policyDefinitionId).isPresent()) { + // Found policyDefinition with same ID return Response.ok(policyDefinitionId).build(); } return Response.status(Response.Status.NOT_FOUND).entity("Unknown policyDefinitionId.").build(); @@ -291,21 +288,19 @@ public Response deleteAcceptedPolicyDefinition(@QueryParam("policyDefinitionId") /** * Updates an accepted policyDefinition. + * The policyDefinitionId must match with a stored policyDefinition. * - * @param policyDefinitionId PolicyDefinition id of policyDefinition to be - * updated - * @param policyDefinition Updated PolicyDefinition + * @param policyDefinition Updated policyDefinition + * @return PolicyDefinitionId of updated policyDefinition or 404 */ @PUT @Path(ACCEPTED_POLICIES_PATH) public Response updateAcceptedPolicyDefinition(PolicyDefinition policyDefinition) { monitor.debug(format("[Client] Received a %s PUT request", ACCEPTED_POLICIES_PATH)); - if (Objects.isNull(policyDefinition)) { - return Response.status(Response.Status.BAD_REQUEST).entity("Missing policyDefinition as request body").build(); - } + Objects.requireNonNull(policyDefinition, "policyDefinition (request body) must not be null"); - var updated = policyController.updateAcceptedPolicyDefinition(policyDefinition); - if (updated.isPresent()) { + if (policyController.updateAcceptedPolicyDefinition(policyDefinition).isPresent()) { + // Found policyDefinition with same ID return Response.ok(policyDefinition.getId()).build(); } return Response.status(Response.Status.NOT_FOUND).entity("Unknown policyDefinitionId.").build(); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java index e332a5db..e558207f 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java @@ -22,7 +22,6 @@ import org.eclipse.edc.connector.spi.catalog.CatalogService; import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; import org.eclipse.edc.runtime.metamodel.annotation.Inject; -import org.eclipse.edc.spi.monitor.Monitor; import org.eclipse.edc.spi.system.ServiceExtension; import org.eclipse.edc.spi.system.ServiceExtensionContext; import org.eclipse.edc.transform.spi.TypeTransformerRegistry; @@ -39,8 +38,6 @@ public class ClientExtension implements ServiceExtension { @Inject private CatalogService catalogService; @Inject - private TypeTransformerRegistry transformer; - @Inject private ConsumerContractNegotiationManager consumerNegotiationManager; @Inject private ContractNegotiationObservable contractNegotiationObservable; @@ -49,15 +46,15 @@ public class ClientExtension implements ServiceExtension { @Inject private TransferProcessManager transferProcessManager; @Inject + private TypeTransformerRegistry transformer; + @Inject private WebService webService; private static final String SETTINGS_PREFIX = "edc.client."; - private Monitor monitor; - @Override public void initialize(ServiceExtensionContext context) { - monitor = context.getMonitor(); + var monitor = context.getMonitor(); var config = context.getConfig(SETTINGS_PREFIX); var policyController = new PolicyController(monitor, catalogService, transformer, config); diff --git a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java index ac02b672..82de55ee 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/policy/PolicyDefinitionStore.java @@ -83,8 +83,8 @@ Optional removePolicyDefinition(String policyDefinitionId) { */ Optional updatePolicyDefinitions(PolicyDefinition policyDefinition) { var policyDefinitionId = policyDefinition.getId(); - Objects.requireNonNull(policyDefinitionId, "contractOfferId is null"); - Objects.requireNonNull(policyDefinition, "contractOffer is null"); + Objects.requireNonNull(policyDefinitionId, "policyDefinitionId is null"); + Objects.requireNonNull(policyDefinition, "policyDefinition is null"); if (policyDefinitions.containsKey(policyDefinitionId)) { return Optional.ofNullable(policyDefinitions.put(policyDefinitionId, policyDefinition)); } @@ -101,7 +101,7 @@ void loadPolicyDefinitions(String acceptedPolicyDefinitionsPath) { putPolicyDefinitions(acceptedPolicyDefinitions); } catch (IOException loadAcceptedPolicyException) { monitor.warning( - format("[Client] Could not load accepted ContractOffers from %s", + format("[Client] Could not load accepted PolicyDefinitions from %s", acceptedPolicyDefinitionsPath), loadAcceptedPolicyException); } From 015cd18ea6a30de32bcfddc52a3ce8a1a502fe2e Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:26:11 +0100 Subject: [PATCH 16/20] Add ClientExtensionTest --- .../iosb/client/ClientExtensionTest.java | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java diff --git a/client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java b/client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java new file mode 100644 index 00000000..32733b50 --- /dev/null +++ b/client/src/test/java/de/fraunhofer/iosb/client/ClientExtensionTest.java @@ -0,0 +1,49 @@ +package de.fraunhofer.iosb.client; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import org.eclipse.edc.api.auth.spi.AuthenticationService; +import org.eclipse.edc.connector.contract.spi.negotiation.ConsumerContractNegotiationManager; +import org.eclipse.edc.connector.contract.spi.negotiation.observe.ContractNegotiationObservable; +import org.eclipse.edc.connector.contract.spi.negotiation.store.ContractNegotiationStore; +import org.eclipse.edc.connector.spi.catalog.CatalogService; +import org.eclipse.edc.connector.transfer.spi.TransferProcessManager; +import org.eclipse.edc.junit.extensions.DependencyInjectionExtension; +import org.eclipse.edc.spi.monitor.Monitor; +import org.eclipse.edc.spi.system.ServiceExtensionContext; +import org.eclipse.edc.spi.system.injection.ObjectFactory; +import org.eclipse.edc.web.spi.WebService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +@ExtendWith(DependencyInjectionExtension.class) +public class ClientExtensionTest { + + private ClientExtension clientExtension; + private ServiceExtensionContext context; + + @BeforeEach + void setup(ServiceExtensionContext context, ObjectFactory factory) { + context.registerService(AuthenticationService.class, mock(AuthenticationService.class)); + context.registerService(CatalogService.class, mock(CatalogService.class)); + context.registerService(ConsumerContractNegotiationManager.class, + mock(ConsumerContractNegotiationManager.class)); + context.registerService(ContractNegotiationStore.class, mock(ContractNegotiationStore.class)); + context.registerService(ContractNegotiationObservable.class, mock(ContractNegotiationObservable.class)); + context.registerService(TransferProcessManager.class, mock(TransferProcessManager.class)); + context.registerService(WebService.class, mock(WebService.class)); + context.registerService(Monitor.class, mock(Monitor.class)); + + this.context = spy(context); + clientExtension = factory.constructInstance(ClientExtension.class); + } + + @Test + public void initializeTest() { + // See if initializing the extension works + clientExtension.initialize(this.context); + } + +} From ecece773b18be7c7a01bd0c8d48fe0bd498d3b16 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:30:24 +0100 Subject: [PATCH 17/20] Reformat test code --- .../de/fraunhofer/iosb/client/ClientEndpointTest.java | 7 +++++-- .../fraunhofer/iosb/client/policy/PolicyServiceTest.java | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java index c0da8c39..60db702d 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/ClientEndpointTest.java @@ -121,8 +121,11 @@ public void setup() throws IOException { } private Config mockConfig() { - return ConfigFactory.fromMap(Map.of("edc.dsp.callback.address", "http://localhost:4321/dsp", - "web.http.port", "8080", "web.http.path", "/api")); + return ConfigFactory.fromMap( + Map.of( + "edc.dsp.callback.address", "http://localhost:4321/dsp", + "web.http.port", "8080", + "web.http.path", "/api")); } private TypeTransformerRegistry mockTransformer() { diff --git a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java index d1286017..8a4b4037 100644 --- a/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java +++ b/client/src/test/java/de/fraunhofer/iosb/client/policy/PolicyServiceTest.java @@ -96,14 +96,14 @@ void getContractUnreachableProviderTest() throws MalformedURLException, Interrup policyService.getDatasetForAssetId(new URL("http://fakeUrl:4321/not/working"), "test-asset-id"); fail("This should not complete without throwing an exception"); } catch (EdcException expected) { - } } private PolicyServiceConfig mockConfig() { - return new PolicyServiceConfig( - ConfigFactory.fromMap(Map.of("edc.dsp.callback.address", "http://localhost:4321/dsp", - "web.http.port", "8080", "web.http.path", "/api"))); + return new PolicyServiceConfig(ConfigFactory.fromMap(Map.of( + "edc.dsp.callback.address", "http://localhost:4321/dsp", + "web.http.port", "8080", + "web.http.path", "/api"))); } } From b9777d3016c763b1d4ea2e2525515b135c0ca975 Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:59:22 +0100 Subject: [PATCH 18/20] Update README.md --- README.md | 145 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 8e45668b..089a10be 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,18 @@ model via the EDC. ## Version compatibility -| Specification | Version | -|:-----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------| -| Eclipse Dataspace Connector | v0.3.0 | +| Specification | Version | +|:-----------------------|-------------------------| +| Eclipse Dataspace Connector | v0.4.1 | | AAS - Details of the Asset Administration Shell - Part 1
The exchange of information between partners in the value chain of Industrie 4.0 | Version 3.0RC01
(based on [admin-shell-io/java-model](https://github.com/admin-shell-io/java-model)) | ## Repo Structure The repository contains several material: - +- `client`: Source code for the client extension (automated contract negotiation) - `config`: Checkstyle files for code formatting -- `edc-extension4aas`: Source code for the extension -- `example`: Example use case for the edc-extension4aas with a preconfigured EDC launcher. +- `edc-extension4aas`: Source code for the AAS extension +- `example`: Example use case for the edc-extension4aas and client extension with a preconfigured EDC launcher. @@ -34,8 +34,9 @@ contracts have to be defined for each element. In order to minimize configuration effort and prevent errors, this extension is able to link running AAS into EDC Assets. Furthermore, this extension can also start AAS by reading an AAS model. A default contract can be chosen to be applied for all elements. For critical elements, additional contracts can be placed. -External changes to the model of an AAS are automatically synchronized by the Extension. -Additionally, a client providing API calls for aggregations of processes such as contract negotiation and data transfer +External changes to the model of an AAS are automatically synchronized by the extension. + +Additionally, a client extension providing API calls for aggregations of processes such as contract negotiation and data transfer is available. ### Use Cases @@ -48,76 +49,96 @@ Provide digital twin (AAS) data to business partners in Data Spaces like Catena- #### **Provider Interfaces** -| HTTP Method | Interface (edc:1234/api/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | -|:------------|:------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| GET | config (a) | - | Get current extension configuration values. | -| PUT | config (a) | Body: Updated config values (JSON) (r) | Update config values. | -| POST | client (a) | Query Parameter "url" (r) | Register a standalone AAS service (e.g., FA³ST) to this extension. | -| DELETE | client (a) | Query Parameter "url" (r) | Unregister an AAS service (e.g., FA³ST) from this extension, possibly shutting down the service if it has been started internally. | -| POST | environment (a) | Query Parameter "environment": Path to new AAS environment (r), Query Parameter "port": Port of service to be created , Query Parameter "config": Path of AAS service configuration file | Create a new AAS service. Either (http) "port" or "config" must be given to ensure communication with the AAS service via an HTTP endpoint on the service's side. This command returns the URL of the newly created AAS service on success, which can be used to remove the service using the interface "DELETE /client" | -| POST | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward POST request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | -| DELETE | aas (a) | Query Parameter requestUrl: URL of AAS service to be updated (r) | Forward DELETE request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | -| PUT | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward PUT request to provided host in requestUrl. | -| GET | selfDescription | - | Return self description of extension. | +| HTTP Method | Interface (edc:1234/api/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | +|:-|:-|:-|:-| +| GET | config (a) | - | Get current extension configuration values. | +| PUT | config (a) | Body: Updated config values (JSON) (r) | Update config values. | +| POST | client (a) | Query Parameter "url" (r) | Register a standalone AAS service (e.g., FA³ST) to this extension. | +| DELETE | client (a) | Query Parameter "url" (r) | Unregister an AAS service (e.g., FA³ST) from this extension, possibly shutting down the service if it has been started internally. | +| POST | environment (a) | Query Parameter "environment": Path to new AAS environment (r), Query Parameter "port": Port of service to be created , Query Parameter "config": Path of AAS service configuration file | Create a new AAS service. Either (http) "port" or "config" must be given to ensure communication with the AAS service via an HTTP endpoint on the service's side. This command returns the URL of the newly created AAS service on success, which can be used to remove the service using the interface "DELETE /client" | +| POST | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward POST request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | +| DELETE | aas (a) | Query Parameter requestUrl: URL of AAS service to be updated (r) | Forward DELETE request to provided host in requestUrl. If requestUrl is an AAS service that is registered at this EDC, synchronize assets and self description as well. | +| PUT | aas (a) | Query Parameter "requestUrl": URL of AAS service to be updated (r), request body: AAS element (r) | Forward PUT request to provided host in requestUrl. | +| GET | selfDescription | - | Return self description of extension. | #### **Client Interfaces** -| HTTP Method | Interface (edc:1234/api/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | -|:------------|:------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| POST | negotiate (a) | Query Parameter "providerUrl": URL (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl" | Perform an automated contract negotiation with a provider and get the data stored in the specified asset. Optionally, a data destination URL can be specified where the data is sent to on success. | -| GET | dataset (a) | Query Parameter "providerUrl": URL (r), Query Parameter "assetId": String (r) | Get offered dataset from the specified provider's catalog that contains the specified asset's policies. | -| POST | negotiateContract (a) | Query Parameter "providerUrl": URL (r), request body: contractOffer (r) | Using a contractOffer and a providerUrl, negotiate a contract. Returns an agreementId on success. | -| GET | transfer (a) | Query Parameter "providerUrl": URL (r), Query Parameter "agreementId": String (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl" | Submits a data transfer request to the providerUrl. On success, returns the data behind the specified asset. Optionally, a data destination URL can be specified where the data is then sent to. | -| POST | contractOffers (a) | request body: List of ContractOffers (JSON) (r) | Adds the given ContractOffers to the accepted ContractOffers list: On fully automated negotiation, the provider's ContractOffer is matched against the consumer's accepted ContractOffer list. If any ContractOffer's policies fit the provider's, the negotiation continues. | +| HTTP Method | Interface (edc:1234/api/automated/...) ((a) = only for authenticated users) | Parameters ((r) = required) | Description | +|:-|:-|:-|:-| +| POST | negotiate (a) | Query Parameter "providerUrl": URL (r),Query Parameter "providerId": String (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl": URL | Perform an automated contract negotiation with a provider (given provider URL and ID) and get the data stored for the specified asset. Optionally, a data destination URL can be specified where the data is sent to instead of the extension's log. | +| GET | dataset (a) | Query Parameter "providerUrl": URL (r), Query Parameter "assetId": String (r) | Get dataset from the specified provider's catalog that contains the specified asset's policies. | +| POST | negotiateContract (a) | request body: org.eclipse.edc.connector.contract.spi.types.negotiation.ContractRequest (r) | Using a contractRequest (JSON in http request body), negotiate a contract. Returns the corresponding agreementId on success. | +| GET | transfer (a) | Query Parameter "providerUrl": URL (r), Query Parameter "agreementId": String (r), Query Parameter "assetId": String (r), Query Parameter "dataDestinationUrl" | Submits a data transfer request to the providerUrl. On success, returns the data behind the specified asset. Optionally, a data destination URL can be specified where the data is sent to instead of the extension's log. | + +| POST | acceptedPolicies (a) | request body: List of PolicyDefinitions (JSON) (r) | Adds the given PolicyDefinitions to the accepted PolicyDefinitions list (Explanation: On fully automated negotiation, the provider's PolicyDefinition is matched against the consumer's accepted PolicyDefinitions list. If any PolicyDefinition fits the provider's, the negotiation continues.) Returns "OK"-Response if requestBody is valid. | +| GET | acceptedPolicies (a) | - | Returns the client extension's accepted policy definitions for fully automated negotiation. | +| DELETE | acceptedPolicies (a) | request body: PolicyDefinition: PolicyDefinition (JSON) (r) | Updates the client extension's accepted policy definition with the same policyDefinitionId as the request. | +| PUT | acceptedPolicies (a) | request body: PolicyDefinitionId: String (JSON) (r) | Deletes a client extension's accepted policy definition with the same policyDefinitionId as the request. | + ### Dependencies -| Name | Description | -|:----------------------------------------------|:--------------------------------------------------------------------------------------------------------------| -| de.fraunhofer.iosb.ilt.faaast.service:starter | [FA³ST Service](https://github.com/FraunhoferIOSB/FAAAST-Service) to start AAS services internally. | -| io.admin-shell.aas:dataformat-json | [admin-shell-io java serializer](https://github.com/admin-shell-io/java-serializer) (de-)serialize AAS models | -| io.admin-shell.aas:model | [admin-shell-io java model](https://github.com/admin-shell-io/java-model) (de-)serialize AAS models | -| com.squareup.okhttp3:okhttp | Send HTTP requests to AAS services | -| jakarta.ws.rs:jakarta.ws.rs-api | provides HTTP endpoints of extension | -| org.eclipse.edc:contract-core | Client: Observe contract negotiation state | -| org.eclipse.edc:management-api | EDC asset/contract management | -| org.eclipse.edc:runtime-metamodel | EDC metamodel | -| org.eclipse.edc:dsp-catalog-http-dispatcher | EDC constants | +#### EDC-Extension-for-AAS + +| Name | Description | +|:-|:-| +| de.fraunhofer.iosb.ilt.faaast.service:starter | [FA³ST Service](https://github.com/FraunhoferIOSB/FAAAST-Service) to start AAS services internally. | +| io.admin-shell.aas:dataformat-json | [admin-shell-io java serializer](https://github.com/admin-shell-io/java-serializer) (de-)serialize AAS models | +| io.admin-shell.aas:model | [admin-shell-io java model](https://github.com/admin-shell-io/java-model) (de-)serialize AAS models | +| com.squareup.okhttp3:okhttp | Send HTTP requests to AAS services | +| jakarta.ws.rs:jakarta.ws.rs-api | provides HTTP endpoints of extension | +| org.eclipse.edc:contract-core | Client: Observe contract negotiation state | +| org.eclipse.edc:management-api | EDC asset/contract management | +| org.eclipse.edc:runtime-metamodel | EDC metamodel | +| org.eclipse.edc:dsp-catalog-http-dispatcher | EDC constants | + +#### Client Extension + +| Name | Description | +|:-|:-| +| org.eclipse.edc:contract-core | Client: Observe contract negotiation state | +| org.eclipse.edc:dsp-catalog-http-dispatcher | EDC constants | +| org.eclipse.edc:management-api | EDC asset/contract management | +| org.eclipse.edc:runtime-metamodel | EDC metamodel | +| org.eclipse.edc:data-plane-http-spi | HttpDataAddress | +| jakarta.ws.rs:jakarta.ws.rs-api | provides HTTP endpoints of extension | ### Configurations -| Key | Value Type | Description | -|:----------------------------------|:--------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| edc.aas.remoteAasLocation | URL | A URL of an AAS service (such as FA³ST) that is already running and is conformant with official AAS API specification | -| edc.aas.localAASModelPath | path | A path to a serialized AAS environment compatible to specification version 3.0RC01 (see: https://github.com/FraunhoferIOSB/FAAAST-Service/blob/main/README.md) | -| edc.aas.localAASServicePort | Open port from 1 to 65535 | Port to locally created AAS service. Required, if localAASModelPath is defined and localAASServiceConfigPath is not defined. | -| edc.aas.localAASServiceConfigPath | path | Path to AAS config for locally started AAS service. Required, if localAASServicePort is not defined, but localAASModelPath is defined. | -| edc.aas.syncPeriod | whole number in seconds | Time period in which AAS services should be polled for structural changes (added/deleted elements etc.). Default value is 5 (seconds). Note: This configuration value is only read on startup, the synchronization period cannot be changed at runtime. | -| edc.aas.exposeSelfDescription | true/false | Whether the Self Description should be exposed on {edc}/api/selfDescription. When set to False, the selfDescription is still available for authenticated requests. | -| edc.aas.defaultAccessPolicyPath | path | Path to an access policy file (JSON). This policy will be used as the default access policy for all assets created after the configuration value has been set. | -| edc.aas.defaultContractPolicyPath | path | Path to a contract policy file (JSON). This policy will be used as the default contract policy for all assets created after the configuration value has been set. | - -#### **Client Configurations** - -| Key | Value Type | Description | -|:------------------------------------------|:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| edc.client.waitForAgreementTimeout | whole number in seconds | How long should the extension wait for an agreement when automatically negotiating a contract? Default value is 10(s). | -| edc.client.waitForTransferTimeout | whole number in seconds | How long should the extension wait for a data transfer when automatically negotiating a contract? Default value is 10(s). | -| edc.client.acceptAllProviderOffers | boolean | If true, the client accepts any contractOffer offered by a provider connector on automated contract negotiation (e.g., trusted provider). Default value: false | -| edc.client.acceptedContractOffersPath | path | Path pointing to a JSON-file containing acceptable ContractOffers for automated contract negotiation in a list (only policies must match in a provider's ContractOffer) | +#### **EDC-Extension-for-AAS Configurations** + +| Key| Value Type| Description| +|:-|:-|:-| +| edc.aas.remoteAasLocation| URL| A URL of an AAS service (such as FA³ST) that is already running and is conformant with official AAS API specification| +| edc.aas.localAASModelPath| path| A path to a serialized AAS environment compatible to specification version 3.0RC01 (see: https://github.com/FraunhoferIOSB/FAAAST-Service/blob/main/README.md) | +| edc.aas.localAASServicePort| Open port from 1 to 65535 | Port to locally created AAS service. Required, if localAASModelPath is defined and localAASServiceConfigPath is not defined.| +| edc.aas.localAASServiceConfigPath | path| Path to AAS config for locally started AAS service. Required, if localAASServicePort is not defined, but localAASModelPath is defined.| +| edc.aas.syncPeriod | whole number in seconds | Time period in which AAS services should be polled for structural changes (added/deleted elements etc.). Default value is 5 (seconds). Note: This configuration value is only read on startup, the synchronization period cannot be changed at runtime. | +| edc.aas.exposeSelfDescription| true/false| Whether the Self Description should be exposed on {edc}/api/selfDescription. When set to False, the selfDescription is still available for authenticated requests.| +| edc.aas.defaultAccessPolicyPath| path| Path to an access policy file (JSON). This policy will be used as the default access policy for all assets created after the configuration value has been set.| +| edc.aas.defaultContractPolicyPath | path| Path to a contract policy file (JSON). This policy will be used as the default contract policy for all assets created after the configuration value has been set.| + +#### **Client Extension Configurations** + +| Key | Value Type | Description | +|:-|:-|:-| +| edc.client.waitForAgreementTimeout | whole number in seconds | How long should the extension wait for an agreement when automatically negotiating a contract? Default value is 10(s). | +| edc.client.waitForTransferTimeout | whole number in seconds | How long should the extension wait for a data transfer when automatically negotiating a contract? Default value is 10(s). | +| edc.client.acceptAllProviderOffers | boolean | If true, the client accepts any contractOffer offered by a provider connector on automated contract negotiation (e.g., trusted provider). Default value: false | +| edc.client.acceptedPolicyDefinitionsPath | path | Path pointing to a JSON-file containing acceptable PolicyDefinitions for automated contract negotiation in a list (only policies must match in a provider's PolicyDefinition) | ## Terminology -| Term | Description | -|:--------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| AAS | Asset Administration Shell (see [AAS reading guide](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Asset_Administration_Shell_Reading_Guide.html) or [AAS specification part 1](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Details_of_the_Asset_Administration_Shell_Part1_V3.html)) | -| FA³ST Service | Open Source java implementation of the AAS part 2 see on [GitHub](https://github.com/FraunhoferIOSB/FAAAST-Service) | +| Term | Description | +|:-|:-| +| AAS | Asset Administration Shell (see [AAS reading guide](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Asset_Administration_Shell_Reading_Guide.html) or [AAS specification part 1](https://www.plattform-i40.de/IP/Redaktion/DE/Downloads/Publikation/Details_of_the_Asset_Administration_Shell_Part1_V3.html)) | +| FA³ST Service | Open Source java implementation of the AAS part 2 [see on GitHub](https://github.com/FraunhoferIOSB/FAAAST-Service) | ## Roadmap Features in development: - Graphical interface to simplify providing and requesting AAS ( - see: https://github.com/FraunhoferIOSB/EDC-Extension-for-AAS-Dashboard) -- Built-in client to request AAS data from other EDC (automatic contract negotiation) + see: https://github.com/FraunhoferIOSB/EDC-Extension-for-AAS-Dashboard) ✓ +- Built-in client to request AAS data from other EDC (automatic contract negotiation) ✓ - Docker Hub container deployment From eff58b4265575fac8703ea68002b9ef79ea73c6e Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:34:54 +0100 Subject: [PATCH 19/20] Fix: Config values not read correctly --- .../main/java/de/fraunhofer/iosb/client/ClientExtension.java | 4 +--- .../src/main/java/de/fraunhofer/iosb/app/AasExtension.java | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java index e558207f..5aa31ca7 100644 --- a/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java +++ b/client/src/main/java/de/fraunhofer/iosb/client/ClientExtension.java @@ -50,12 +50,10 @@ public class ClientExtension implements ServiceExtension { @Inject private WebService webService; - private static final String SETTINGS_PREFIX = "edc.client."; - @Override public void initialize(ServiceExtensionContext context) { var monitor = context.getMonitor(); - var config = context.getConfig(SETTINGS_PREFIX); + var config = context.getConfig(); var policyController = new PolicyController(monitor, catalogService, transformer, config); diff --git a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java index 13044252..49131819 100644 --- a/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java +++ b/edc-extension4aas/src/main/java/de/fraunhofer/iosb/app/AasExtension.java @@ -58,7 +58,7 @@ public class AasExtension implements ServiceExtension { @Inject private WebService webService; - private static final String SETTINGS_PREFIX = "edc.aas."; + private static final String SETTINGS_PREFIX = "edc.aas"; private static final Logger logger = Logger.getInstance(); private final ScheduledExecutorService syncExecutor = new ScheduledThreadPoolExecutor(1); private AasController aasController; From f9e67e95e112cbf77b2f0b1ddc1101e614211b7f Mon Sep 17 00:00:00 2001 From: Carlos Schmidt <18703981+carlos-schmidt@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:35:52 +0100 Subject: [PATCH 20/20] Update example README.md --- example/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/example/README.md b/example/README.md index 0d7d82d4..b19f1333 100644 --- a/example/README.md +++ b/example/README.md @@ -1,7 +1,6 @@ # Example Use Case -The example use case starts two connectors with the edc4aas extension. The first connector is a provider of an AAS model -and the second connector is an example consumer which wants to retrieve data from the provider. +The example use case starts two connectors with the edc4aas extension and the client extension. The first connector is a provider of an AAS model and the second connector is an example consumer which wants to retrieve data from the provider. The example has the following structure: @@ -16,6 +15,7 @@ The example has the following structure: - `dataspaceconnector-configuration.properties`: Debugging and quick testing of changes via `./gradlew run --debug-jvm` command - `docker-compose.yml`, `Dockerfile`: Docker files +- `README.md`: This README file ## Getting Started @@ -153,7 +153,7 @@ __Important__: ## Running the Example (manual) -### Info: Manual data transfer example is outdated! Please refer to the edc samples repository +### Warning: Manual data transfer example is outdated! Please refer to the official EDC documentation! Build the EDC with the extensions.