diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/base/DefaultAASEnvironment.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/base/DefaultAASEnvironment.java index c09f78268..70539ee9c 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/base/DefaultAASEnvironment.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/main/java/org/eclipse/digitaltwin/basyx/aasenvironment/base/DefaultAASEnvironment.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2024 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,14 +24,12 @@ ******************************************************************************/ package org.eclipse.digitaltwin.basyx.aasenvironment.base; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.io.*; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; @@ -40,12 +38,8 @@ import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.SerializationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonSerializer; import org.eclipse.digitaltwin.aas4j.v3.dataformat.xml.XmlSerializer; -import org.eclipse.digitaltwin.aas4j.v3.model.AssetAdministrationShell; -import org.eclipse.digitaltwin.aas4j.v3.model.ConceptDescription; -import org.eclipse.digitaltwin.aas4j.v3.model.Environment; -import org.eclipse.digitaltwin.aas4j.v3.model.Submodel; -import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement; -import org.eclipse.digitaltwin.aas4j.v3.model.Resource; +import org.eclipse.digitaltwin.aas4j.v3.model.*; +import org.eclipse.digitaltwin.aas4j.v3.model.File; import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.AasEnvironment; import org.eclipse.digitaltwin.basyx.aasenvironment.ConceptDescriptionIdCollector; @@ -60,6 +54,7 @@ import org.eclipse.digitaltwin.basyx.aasrepository.AasRepository; import org.eclipse.digitaltwin.basyx.conceptdescriptionrepository.ConceptDescriptionRepository; import org.eclipse.digitaltwin.basyx.core.exceptions.ElementDoesNotExistException; +import org.eclipse.digitaltwin.basyx.core.exceptions.FileDoesNotExistException; import org.eclipse.digitaltwin.basyx.submodelrepository.SubmodelRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,7 +64,7 @@ /** * Default implementation of {@link AasEnvironment} * - * @author zhangzai, danish + * @author zhangzai, danish, fried */ public class DefaultAASEnvironment implements AasEnvironment { @@ -83,7 +78,8 @@ public class DefaultAASEnvironment implements AasEnvironment { private AASXSerializer aasxSerializer = new AASXSerializer(); private MetamodelCloneCreator cloneCreator = new MetamodelCloneCreator(); private IdentifiableAssertion checker; - + private static String aasxFilePathPrefix = "/aasx/files/"; + public DefaultAASEnvironment(AasRepository aasRepository, SubmodelRepository submodelRepository, ConceptDescriptionRepository conceptDescriptionRepository) { this.aasRepository = aasRepository; this.submodelRepository = submodelRepository; @@ -108,11 +104,41 @@ public String createXMLAASEnvironmentSerialization(@Valid List aasIds, @ public byte[] createAASXAASEnvironmentSerialization(@Valid List aasIds, @Valid List submodelIds, @Valid boolean includeConceptDescriptions) throws SerializationException, IOException { Environment aasEnvironment = createEnvironment(aasIds, submodelIds, includeConceptDescriptions); + List relatedFiles = new ArrayList<>(); + HashMap> fileSubmodelElements = new HashMap<>(); + + aasEnvironment.getSubmodels().forEach(sm-> fileSubmodelElements.put(sm.getId(),getAllFileSubmodelElements(sm))); + + addFilesToRelatedFiles(fileSubmodelElements, relatedFiles); + aasEnvironment.getAssetAdministrationShells().forEach(aas->addThumbnailToRelatedFiles(aas.getId(),aas.getAssetInformation().getDefaultThumbnail(),relatedFiles)); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - aasxSerializer.write(aasEnvironment, null, outputStream); + aasxSerializer.write(aasEnvironment, relatedFiles, outputStream); return outputStream.toByteArray(); } - + + public List getAllFileSubmodelElements(Submodel submodel) { + List files = new ArrayList<>(); + Stack stack = new Stack<>(); + + stack.addAll(submodel.getSubmodelElements()); + + while (!stack.isEmpty()) { + SubmodelElement submodelElement = stack.pop(); + + if (submodelElement instanceof File file) { + if(!isURL(file.getValue())) + files.add(file); + } else if (submodelElement instanceof SubmodelElementCollection collection) { + stack.addAll(collection.getValue()); + } else if (submodelElement instanceof SubmodelElementList list) { + stack.addAll(list.getValue()); + } + } + + return files; + } + public void loadEnvironment(CompleteEnvironment completeEnvironment) { Environment environment = completeEnvironment.getEnvironment(); List relatedFiles = completeEnvironment.getRelatedFiles(); @@ -295,4 +321,91 @@ private ConceptDescription fetchConceptDescriptionFromRepo(String conceptDescrip } } + private void addFilesToRelatedFiles(HashMap> submodelFileSMEMap, List relatedFiles) { + submodelFileSMEMap.forEach((submodelId, fileSubmodelElementList) -> fileSubmodelElementList.forEach(file -> { + try { + if (isFileAlreadyAdded(file.getValue())) { + return; + } + processFile(submodelId, file, relatedFiles); + } catch (IOException | NullPointerException | FileDoesNotExistException e) { + handleFileError(file); + } + })); + } + + private void processFile(String key, File file, List relatedFiles) throws IOException { + byte[] fileContent = getFileContent(key, file); + String path = getFilePathInAASX(file.getValue()); + relatedFiles.add(new InMemoryFile(fileContent, path)); + file.setValue(path); + } + + private void handleFileError(File file) { + logger.error("File {} does not exist in the repository", file.getValue()); + } + + + private byte[] getFileContent(String submodelId, File file) throws IOException { + return submodelRepository.getFileByFilePath(submodelId, file.getValue()).readAllBytes(); + } + + private boolean isFileAlreadyAdded(String filePath){ + return filePath.startsWith(aasxFilePathPrefix); + } + + private void addThumbnailToRelatedFiles(String aasId, Resource thumbnail, List relatedFiles) { + if(!isThumbnailSet(thumbnail)) { + logger.info("No thumbnail specified for aas {}", aasId); + }else { + try { + String newPath = getThumbnailPathInAASX(thumbnail.getPath()); + relatedFiles.add( + new InMemoryFile( + Files.readAllBytes(aasRepository.getThumbnail(aasId).toPath()), + newPath + ) + ); + thumbnail.setPath(newPath); + } catch (IOException | FileDoesNotExistException e) { + logger.error("Thumbnail file {} does not exist in the repository", thumbnail.getPath()); + } + } + } + + private static boolean isThumbnailSet(Resource thumbnail) { + return thumbnail != null && thumbnail.getPath() != null; + } + + private static String getThumbnailPathInAASX(String path) { + try { + return "/" + getHashedFilePath(path) + "." + getFileExtensionFromPath(path); + } catch (NoSuchAlgorithmException e) { + logger.error("Could not hash thumbnail path {}", path); + throw new RuntimeException(e); + } + } + + private static String getFilePathInAASX(String path) { + try { + return aasxFilePathPrefix + getHashedFilePath(path) + "." + getFileExtensionFromPath(path); + } catch (NoSuchAlgorithmException e) { + logger.error("Could not hash file path {}", path); + throw new RuntimeException(e); + } + } + + private static String getFileExtensionFromPath(String file) { + String[] split = file.split("\\."); + return split[split.length - 1]; + } + + private static String getHashedFilePath(String filePath) throws NoSuchAlgorithmException { + int hashCode = filePath.hashCode(); + return Integer.toHexString(hashCode); + } + + private static boolean isURL(String path) { + return path.startsWith("http"); + } } diff --git a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java index 9072f09fc..3734e36ea 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java +++ b/basyx.aasenvironment/basyx.aasenvironment-core/src/test/java/org/eclipse/digitaltwin/basyx/aasenvironment/TestAASEnvironmentSerialization.java @@ -25,9 +25,6 @@ package org.eclipse.digitaltwin.basyx.aasenvironment; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -39,6 +36,7 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.InvalidOperationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.AASXDeserializer; +import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.InMemoryFile; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.DeserializationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.SerializationException; import org.eclipse.digitaltwin.aas4j.v3.dataformat.json.JsonDeserializer; @@ -72,6 +70,8 @@ import org.junit.Test; import org.xml.sax.SAXException; +import static org.junit.Assert.*; + public class TestAASEnvironmentSerialization { private static final PaginationInfo NO_LIMIT_PAGINATION_INFO = new PaginationInfo(0, ""); @@ -186,11 +186,16 @@ public static void validateXml(String actual, boolean areAASsIncluded, boolean a public static void checkAASX(InputStream inputStream, boolean areAASsIncluded, boolean areSubmodelsIncluded, boolean includeConceptDescription) throws IOException, InvalidFormatException, DeserializationException { AASXDeserializer aasxDeserializer = new AASXDeserializer(inputStream); Environment environment = aasxDeserializer.read(); - checkAASEnvironment(environment, areAASsIncluded, areSubmodelsIncluded, includeConceptDescription); inputStream.close(); } + public static void checkAASXFiles(InputStream inputStream) throws IOException, InvalidFormatException, DeserializationException { + AASXDeserializer aasxDeserializer = new AASXDeserializer(inputStream); + List files = aasxDeserializer.getRelatedFiles(); + assertEquals(2,files.size()); + } + public static Collection createDummyConceptDescriptions() { Collection conceptDescriptions = new ArrayList<>(); diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java index 26f6a42da..dd3ff2b92 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/java/org/eclipse/basyx/digitaltwin/aasenvironment/http/TestAasEnvironmentHTTP.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2024 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -140,6 +140,14 @@ public void testAASEnvironmentSertializationWithAASX() throws IOException, Parse TestAASEnvironmentSerialization.checkAASX(response.getEntity().getContent(), aasIdsIncluded, submodelIdsIncluded, includeConceptDescription); } + @Test + public void testAASEnvironmentSertializationWithAASXAndFiles() throws IOException, ParseException, DeserializationException, InvalidFormatException { + CloseableHttpResponse response = executeGetOnURL(createSerializationURLForFiles(), AASX_MIMETYPE); + assertEquals(HttpStatus.OK.value(), response.getCode()); + + TestAASEnvironmentSerialization.checkAASXFiles(response.getEntity().getContent()); + } + @Test public void testAASEnvironmentSertializationWithAASXExcludeCD() throws IOException, ParseException, DeserializationException, InvalidFormatException { boolean includeConceptDescription = false; @@ -252,6 +260,11 @@ public static String createSerializationURL(boolean includeConceptDescription) { createIdCollection(DummyAASEnvironmentComponent.SUBMODEL_OPERATIONAL_DATA_ID, DummyAASEnvironmentComponent.SUBMODEL_TECHNICAL_DATA_ID), includeConceptDescription); } + public static String createSerializationURLForFiles() { + return getSerializationURL(createIdCollection("https://example.com/ids/AssetAdministrationShell/3982_3381_6308_9332"), + createIdCollection("https://example.com/ids/Submodel/3293_1019_6578_9120"), false); + } + public static CloseableHttpResponse executeGetOnURL(String url, String header) throws IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpGet getRequest = createGetRequestWithHeader(url, header); diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/application.properties b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/application.properties index 471b9a61d..65b3c5e1f 100644 --- a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/application.properties +++ b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/application.properties @@ -1,2 +1,4 @@ server.port=8081 -basyx.backend = InMemory \ No newline at end of file +basyx.backend = InMemory + +basyx.environment = classpath:fileSerializationTest.aasx \ No newline at end of file diff --git a/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/fileSerializationTest.aasx b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/fileSerializationTest.aasx new file mode 100644 index 000000000..79c53658d Binary files /dev/null and b/basyx.aasenvironment/basyx.aasenvironment-http/src/test/resources/fileSerializationTest.aasx differ diff --git a/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java index aa5685dd1..b1f80f289 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-backend/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/backend/CrudSubmodelRepository.java @@ -332,4 +332,11 @@ public void patchSubmodelElements(String submodelId, List submo submodelBackend.save(submodel); } + @Override + public InputStream getFileByFilePath(String submodelId, String filePath) { + SubmodelService submodelService = getSubmodelServiceOrThrow(submodelId); + + return submodelService.getFileByFilePath(filePath); + } + } diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java index a4d10f14f..0fc9c551b 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/ConnectedSubmodelRepository.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2024 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -246,4 +246,9 @@ public void patchSubmodelElements(String submodelId, List submo getConnectedSubmodelService(submodelId).patchSubmodelElements(submodelElementList); } + @Override + public InputStream getFileByFilePath(String submodelId, String filePath) { + return getConnectedSubmodelService(submodelId).getFileByFilePath(filePath); + } + } diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestAuthorizedConnectedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestAuthorizedConnectedSubmodelRepository.java index 18d9a0239..8d64e339d 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestAuthorizedConnectedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestAuthorizedConnectedSubmodelRepository.java @@ -155,4 +155,9 @@ public void invokeNonOperation() { // TODO throw new NotInvokableException(); } + + @Override + public void getFileByFilePath(){ + // Not Implemented for Client so Override Test + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java index b8ecc55fa..9946f1c8e 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/client/TestConnectedSubmodelRepository.java @@ -101,4 +101,9 @@ protected boolean fileExistsInStorage(String fileValue) { return file.exists(); } + + @Override + public void getFileByFilePath(){ + // Not Implemented for Client so Override Test + } } diff --git a/basyx.submodelrepository/basyx.submodelrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelRepository.java index 2b0381ee0..d34139fa9 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/SubmodelRepository.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2024 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -44,7 +44,7 @@ /** * Specifies the overall SubmodelRepository API * - * @author schnicke, danish, kammognie + * @author schnicke, danish, kammognie, fried * */ public interface SubmodelRepository { @@ -272,4 +272,15 @@ public default String getName() { * @param submodelElementList */ public void patchSubmodelElements(String submodelId, List submodelElementList); + + /** + * Retrieves the file of a file submodelelement via its absolute path + * + * @param submodelId + * the Submodel id + * @param filePath + * the path of the file + * @return File InputStream + */ + public InputStream getFileByFilePath(String submodelId, String filePath); } diff --git a/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java b/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java index 1a0faae40..4716e9e8c 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java +++ b/basyx.submodelrepository/basyx.submodelrepository-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelrepository/core/SubmodelRepositorySubmodelServiceWrapper.java @@ -134,4 +134,9 @@ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistExcept repoApi.deleteFileValue(submodelId, idShortPath); } + @Override + public InputStream getFileByFilePath(String filePath) { + return repoApi.getFileByFilePath(submodelId, filePath); + } + } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java index bf221b333..e75bc4a2b 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-authorization/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/authorization/AuthorizedSubmodelRepository.java @@ -243,7 +243,12 @@ public void patchSubmodelElements(String submodelId, List submo decorated.patchSubmodelElements(submodelId, submodelElementList); } - + + @Override + public InputStream getFileByFilePath(String submodelId, String filePath) { + return decorated.getFileByFilePath(submodelId, filePath); + } + private List getIdAsList(String id) { return new ArrayList<>(Arrays.asList(id)); } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java index 5eab00484..c5b0a5f8f 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/mqtt/MqttSubmodelRepository.java @@ -217,4 +217,9 @@ public void patchSubmodelElements(String submodelId, List submo decorated.patchSubmodelElements(submodelId, submodelElementList); } + @Override + public InputStream getFileByFilePath(String submodelId, String filePath) { + return decorated.getFileByFilePath(submodelId, filePath); + } + } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/OperationDelegationSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/OperationDelegationSubmodelRepository.java index 5efbc202f..6404475cb 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/OperationDelegationSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-operation-delegation/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/operation/delegation/OperationDelegationSubmodelRepository.java @@ -173,5 +173,10 @@ public String getName() { public void patchSubmodelElements(String submodelId, List submodelElementList) { decorated.patchSubmodelElements(submodelId, submodelElementList); } - + + @Override + public InputStream getFileByFilePath(String submodelId, String filePath) { + return decorated.getFileByFilePath(submodelId, filePath); + } + } diff --git a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java index 43a6c87c0..071c0e8e1 100644 --- a/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java +++ b/basyx.submodelrepository/basyx.submodelrepository-feature-registry-integration/src/main/java/org/eclipse/digitaltwin/basyx/submodelrepository/feature/registry/integration/RegistryIntegrationSubmodelRepository.java @@ -215,4 +215,9 @@ public void patchSubmodelElements(String submodelId, List submo decorated.patchSubmodelElements(submodelId, submodelElementList); } + @Override + public InputStream getFileByFilePath(String submodelId, String filePath) { + return decorated.getFileByFilePath(submodelId, filePath); + } + } diff --git a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java index ac9eaaccd..ab232f2ab 100644 --- a/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-backend-inmemory/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/InMemorySubmodelService.java @@ -299,6 +299,11 @@ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistExcept setSubmodelElementValue(idShortPath, fileValue); } + @Override + public InputStream getFileByFilePath(String filePath) throws FileDoesNotExistException{ + return fileRepository.find(filePath); + } + private void deleteAssociatedFileIfAny(String idShortPath) { try { deleteFileValue(idShortPath); diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java index df7baf690..78556c15c 100644 --- a/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-client/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/client/ConnectedSubmodelService.java @@ -30,6 +30,7 @@ import java.io.InputStream; import java.util.List; +import org.apache.commons.lang3.NotImplementedException; import org.eclipse.digitaltwin.aas4j.v3.model.OperationRequest; import org.eclipse.digitaltwin.aas4j.v3.model.OperationResult; import org.eclipse.digitaltwin.aas4j.v3.model.OperationVariable; @@ -196,6 +197,20 @@ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistExcept } } + /** + * NOTE: This method is not implemented in the client + * + * @throws NotImplementedException Method not Implemented + * + * @param filePath + * the path of the file + * @return NotImplementedException + */ + @Override + public InputStream getFileByFilePath(String filePath) { + throw new NotImplementedException("This Method is not implemented in the Client"); + } + private RuntimeException mapExceptionFileAccess(String idShortPath, ApiException e) { if (e.getCode() == HttpStatus.NOT_FOUND.value()) return new FileDoesNotExistException(idShortPath); diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestAuthorizedConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestAuthorizedConnectedSubmodelService.java index 3bc805e86..039b832a9 100644 --- a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestAuthorizedConnectedSubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestAuthorizedConnectedSubmodelService.java @@ -176,4 +176,10 @@ public void invokeNonOperation() { throw new NotInvokableException(); } + + @Override + public void getFileByFilePath(){ + // Not Implemented for Client so Override Test + } + } diff --git a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java index 7ffb7b6ed..a6cda20f4 100644 --- a/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-client/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/client/TestConnectedSubmodelService.java @@ -80,4 +80,9 @@ protected boolean fileExistsInStorage(String fileValue) { return file.exists(); } + + @Override + public void getFileByFilePath(){ + // Not Implemented for Client so Override Test + } } diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java b/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java index 776b2b909..90381c531 100644 --- a/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-core/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelService.java @@ -185,4 +185,13 @@ public interface SubmodelService { */ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistException, ElementNotAFileException, FileDoesNotExistException; + + /** + * Retrieves the file of a file submodelelement via its absolute path + * + * @param filePath + * the path of the file + * @return File InputStream + */ + public InputStream getFileByFilePath(String filePath); } diff --git a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java index 9e73e7d40..e3f1e43b6 100644 --- a/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java +++ b/basyx.submodelservice/basyx.submodelservice-core/src/test/java/org/eclipse/digitaltwin/basyx/submodelservice/SubmodelServiceSuite.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (C) 2023 the Eclipse BaSyx Authors + * Copyright (C) 2024 the Eclipse BaSyx Authors * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -40,6 +40,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.NotImplementedException; import org.eclipse.digitaltwin.aas4j.v3.model.DataTypeDefXsd; import org.eclipse.digitaltwin.aas4j.v3.model.Entity; import org.eclipse.digitaltwin.aas4j.v3.model.File; @@ -553,6 +554,22 @@ public void getFile() throws FileNotFoundException, IOException { assertStoredFileContentEquals(submodelService, SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, DUMMY_JSON_1); } + @Test + public void getFileByFilePath() throws IOException { + Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel(); + SubmodelService submodelService = getSubmodelService(technicalDataSubmodel); + + submodelService.setFileValue(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT, "jsonFile1.json", getInputStreamOfDummyFile(DUMMY_JSON_1)); + + String filePath = ((File)submodelService.getSubmodelElement(SubmodelServiceHelper.SUBMODEL_TECHNICAL_DATA_FILE_ID_SHORT)).getValue(); + + InputStream retrievedValue = submodelService.getFileByFilePath(filePath); + InputStream expectedValue = getInputStreamOfDummyFile(DUMMY_JSON_1); + + assertEquals(expectedValue.readAllBytes().length, retrievedValue.readAllBytes().length); + + } + @Test(expected = FileDoesNotExistException.class) public void getNonExistingFile() { Submodel technicalDataSubmodel = DummySubmodelFactory.createTechnicalDataSubmodel(); diff --git a/basyx.submodelservice/basyx.submodelservice-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/feature/mqtt/MqttSubmodelService.java b/basyx.submodelservice/basyx.submodelservice-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/feature/mqtt/MqttSubmodelService.java index 69e41f74c..3d8beed4c 100644 --- a/basyx.submodelservice/basyx.submodelservice-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/feature/mqtt/MqttSubmodelService.java +++ b/basyx.submodelservice/basyx.submodelservice-feature-mqtt/src/main/java/org/eclipse/digitaltwin/basyx/submodelservice/feature/mqtt/MqttSubmodelService.java @@ -149,6 +149,11 @@ public void deleteFileValue(String idShortPath) throws ElementDoesNotExistExcept decorated.deleteFileValue(idShortPath); } + @Override + public InputStream getFileByFilePath(String filePath) throws FileDoesNotExistException{ + return decorated.getFileByFilePath(filePath); + } + private void submodelElementCreated(SubmodelElement submodelElement, String idShort) { sendMqttMessage(topicFactory.createCreateSubmodelElementTopic(idShort), SubmodelElementSerializer.serializeSubmodelElement(submodelElement)); }