Skip to content

Commit

Permalink
Add function upload fileSubmodelElement (#257)
Browse files Browse the repository at this point in the history
Signed-off-by: Zai Zhang <zai.mueller-zhang@iese.fraunhofer.de>
Co-authored-by: Rene-Pascal Fischer <rene-pascal.fischer@iese.fraunhofer.de>
  • Loading branch information
zhangzai123 and FischerRene committed Mar 31, 2023
1 parent 484d415 commit be3438c
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
import java.util.Collection;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
Expand Down Expand Up @@ -137,6 +139,13 @@ public <T extends AASBundle> Set<T> retrieveAASBundles() throws IOException, Par
return (Set<T>) bundles;
}

public InputStream retrieveFileInputStream(String path) throws InvalidFormatException, IOException {
loadAASX();
PackagePart filePart = aasxRoot.getPart(PackagingURIHelper.createPartName(path));
closeOPCPackage();
return filePart.getInputStream();
}

private void loadAASX() throws IOException, InvalidFormatException {
if (aasxInputStream == null) {
aasxInputStream = FileLoaderHelper.getInputStream(aasxPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@

package org.eclipse.basyx.extensions.aas.aggregator.aasxupload;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.eclipse.basyx.aas.aggregator.api.IAASAggregator;
import org.eclipse.basyx.aas.bundle.AASBundle;
import org.eclipse.basyx.aas.bundle.AASBundleHelper;
Expand All @@ -38,9 +42,14 @@
import org.eclipse.basyx.aas.metamodel.map.AssetAdministrationShell;
import org.eclipse.basyx.extensions.aas.aggregator.aasxupload.api.IAASAggregatorAASXUpload;
import org.eclipse.basyx.submodel.metamodel.api.identifier.IIdentifier;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.ISubmodelElement;
import org.eclipse.basyx.submodel.metamodel.api.submodelelement.ISubmodelElementCollection;
import org.eclipse.basyx.submodel.metamodel.map.submodelelement.SubmodelElementCollection;
import org.eclipse.basyx.submodel.metamodel.map.submodelelement.dataelement.File;
import org.eclipse.basyx.vab.exception.provider.MalformedRequestException;
import org.eclipse.basyx.vab.exception.provider.ResourceNotFoundException;
import org.eclipse.basyx.vab.modelprovider.api.IModelProvider;
import org.xml.sax.SAXException;

/**
* An implementation of the IAASAggregatorAASXUpload interface using maps
Expand All @@ -65,11 +74,49 @@ public void uploadAASX(InputStream aasxStream) {
AASXToMetamodelConverter converter = new AASXToMetamodelConverter(aasxStream);
Set<AASBundle> bundles = converter.retrieveAASBundles();
AASBundleHelper.integrate(this, bundles);
uploadFilesInAASX(converter);
} catch (Exception e) {
throw new MalformedRequestException("invalid request to aasx path without valid aasx input stream");
}
}

public void uploadFilesInAASX(AASXToMetamodelConverter converter) throws InvalidFormatException, IOException, ParserConfigurationException, SAXException {
converter.retrieveAASBundles().forEach(aasBundle -> {
aasBundle.getSubmodels().forEach(submodel -> {
submodel.getSubmodelElements().values().forEach(submodelElement -> {
uploadNestedFiles(aasBundle.getAAS().getIdentification(), converter, submodelElement, getSubmodelElementPath(submodel.getIdShort()));
});
});
});
}

private void uploadNestedFiles(IIdentifier aasIdentification, AASXToMetamodelConverter converter, ISubmodelElement submodelElement, String submodelCollectionPath) {
if (submodelElement instanceof File) {
uploadFileInSubmodelElement(aasIdentification, converter, (File) submodelElement, submodelCollectionPath + "/" + submodelElement.getIdShort());
} else if (submodelElement instanceof SubmodelElementCollection) {
uploadFileInSubmodelElementCollection(aasIdentification, converter, submodelElement, submodelCollectionPath + "/" + submodelElement.getIdShort());
}
}

private void uploadFileInSubmodelElement(IIdentifier aasIdentification, AASXToMetamodelConverter converter, File submodelElement, String submodelElementPath) {
try {
getAASProvider(aasIdentification).createValue(submodelElementPath + "/File/upload", converter.retrieveFileInputStream((String) submodelElement.getValue()));
} catch (InvalidFormatException | IOException e) {
e.printStackTrace();
}
}

private void uploadFileInSubmodelElementCollection(IIdentifier aasIdentification, AASXToMetamodelConverter converter, ISubmodelElement submodelCollection, String submodelCollectionPath) {
ISubmodelElementCollection smeCollection = (ISubmodelElementCollection) submodelCollection;
smeCollection.getSubmodelElements().values().forEach(submodelElement -> {
uploadNestedFiles(aasIdentification, converter, submodelElement, submodelCollectionPath);
});
}

private String getSubmodelElementPath(String submodelIdshort) {
return "aas/submodels/" + submodelIdshort + "/submodel/submodelElements/";
}

@Override
public Collection<IAssetAdministrationShell> getAASList() {
return aggregator.getAASList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public SubmodelProvider() {
/**
* Creates a SubmodelProvider based on the VAB API, wrapping the passed provider
*
* @param provider to be wrapped by submodel API
* @param provider
* to be wrapped by submodel API
*/
public SubmodelProvider(IModelProvider provider) {
submodelAPI = new VABSubmodelAPI(provider);
Expand Down Expand Up @@ -120,8 +121,7 @@ private String removeSubmodelPrefix(String path) {
} else if (path.equals(SUBMODEL)) {
path = "";
} else {
throw new MalformedRequestException(
"The request " + path + " is not allowed for this endpoint. /" + SUBMODEL + " is missing");
throw new MalformedRequestException("The request " + path + " is not allowed for this endpoint. /" + SUBMODEL + " is missing");
}
path = VABPathTools.stripSlashes(path);
return path;
Expand Down Expand Up @@ -204,8 +204,9 @@ private boolean endsWithValue(String[] splitted) {
/**
* Removes a trailing <code>/value</code> from the path if it exists.
*
* @param path The original path, which might or might not end on
* {@value Property.VALUE}.
* @param path
* The original path, which might or might not end on
* {@value Property.VALUE}.
* @return The new path
*/
private String removeValueSuffix(String path) {
Expand Down Expand Up @@ -235,12 +236,10 @@ public void setValue(String path, Object newValue) throws ProviderException {
submodelAPI.updateSubmodelElement(idshortPath, newValue);
} else {

ISubmodelElement element = SubmodelElementFacadeFactory
.createSubmodelElement((Map<String, Object>) newValue);
ISubmodelElement element = SubmodelElementFacadeFactory.createSubmodelElement((Map<String, Object>) newValue);

if (!path.endsWith(element.getIdShort())) {
throw new MalformedRequestException("The idShort of given Element '" + element.getIdShort()
+ "' does not match the ending of the given path '" + path + "'");
throw new MalformedRequestException("The idShort of given Element '" + element.getIdShort() + "' does not match the ending of the given path '" + path + "'");
}

submodelAPI.addSubmodelElement(idshortPath, element);
Expand All @@ -256,7 +255,7 @@ public void createValue(String path, Object newEntity) throws ProviderException
}
String[] splitted = VABPathTools.splitPath(path);
if (endsWithFileUpload(splitted)) {
submodelAPI.uploadSubmodelElementFile(getIdShortFromSplittedPath(splitted), (InputStream) newEntity);
submodelAPI.uploadSubmodelElementFile(getFileIdShortFromSplittedPath4FileUpload(splitted), (InputStream) newEntity);
return;
}
throw new MalformedRequestException("POST on \"" + path + "\" not allowed");
Expand Down Expand Up @@ -319,8 +318,7 @@ private Object invokeSync(String path, Object... parameters) {
}

private Object invokeAsync(String path, Object... parameters) {
String pathWithoutAsyncInvoke = path.replaceFirst(Pattern.quote(Operation.INVOKE + OperationProvider.ASYNC),
"");
String pathWithoutAsyncInvoke = path.replaceFirst(Pattern.quote(Operation.INVOKE + OperationProvider.ASYNC), "");
String strippedPathWithoutAsyncInvoke = VABPathTools.stripSlashes(pathWithoutAsyncInvoke);
return submodelAPI.invokeAsync(strippedPathWithoutAsyncInvoke, parameters);
}
Expand Down Expand Up @@ -349,13 +347,38 @@ private boolean endsWithFileUpload(String[] splitted) {
return splitted[splitted.length - 1].equals(UPLOAD);
}

private String getFileIdShortFromSplittedPath4FileUpload(String[] splitted) {
String idShort = "";
for (int i = 1; i < splitted.length - 2; i++) {
idShort = concatFileIdShortPath(splitted, idShort, i);
}
return idShort;
}

private String getFileIdShortFromSplittedPath4FileDownload(String[] splitted) {
String idShort = "";
for (int i = 1; i < splitted.length - 1; i++) {
idShort = concatFileIdShortPath(splitted, idShort, i);
}
return idShort;
}

private String concatFileIdShortPath(String[] splitted, String idShort, int i) {
if (idShort.isEmpty()) {
idShort = idShort.concat(splitted[i]);
} else {
idShort = idShort.concat("/" + splitted[i]);
}
return idShort;
}

private String getIdShortFromSplittedPath(String[] splitted) {
return splitted[1];
}

@SuppressWarnings("unchecked")
private Object handleFile(String[] splitted) {
String idShortPath = getIdShortFromSplittedPath(splitted);
String idShortPath = getFileIdShortFromSplittedPath4FileDownload(splitted);
Map<String, Object> submodelElement = (Map<String, Object>) submodelAPI.getSubmodelElement(idShortPath);

if (!File.isFile(submodelElement)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,26 @@

package org.eclipse.basyx.testsuite.regression.extensions.aas.aggregator.aasxupload;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Collection;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.client.ClientProtocolException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.eclipse.basyx.aas.aggregator.AASAggregator;
import org.eclipse.basyx.aas.aggregator.api.IAASAggregator;
import org.eclipse.basyx.aas.metamodel.api.IAssetAdministrationShell;
Expand All @@ -52,6 +63,8 @@
import org.eclipse.basyx.vab.protocol.http.server.VABHTTPInterface;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.util.ResourceUtils;
import org.xml.sax.SAXException;

/**
* Test for the {@link AASAggregatorAASXUploadProxy}
Expand All @@ -65,6 +78,7 @@ public class TestAASAggregatorProxyWithAASXProvider extends TestAASAggregatorPro
private static final String CONTEXT_PATH = "aggregator";
private static final String API_URL = "http://" + SERVER + ":" + PORT + "/" + CONTEXT_PATH + "/shells";
public static final String AASX_WITH_EMPTY_BOOLEAN_PATH = "src/test/resources/aas/factory/aasx/aas_with_empty_value.aasx";
public static final String AASX_WITH_FILE = "src/test/resources/aas/factory/aasx/test_img.aasx";
private AASAggregatorAASXUploadProvider provider = new AASAggregatorAASXUploadProvider(new AASAggregatorAASXUpload(new AASAggregator()));

@Rule
Expand All @@ -84,6 +98,37 @@ public void testClientUpload() throws ClientProtocolException, IOException {
TestAASAggregatorAASXUploadSuite.checkAASX(uploadedShells);
}

@Test
public void testClientUploadAASXWithFile() throws MalformedURLException, IOException, InvalidFormatException, ParserConfigurationException, SAXException {
AASAggregatorAASXUploadProxy proxy = new AASAggregatorAASXUploadProxy(API_URL);
proxy.uploadAASX(new FileInputStream(Paths.get(AASX_WITH_FILE).toFile()));

File expectedFileInSme = ResourceUtils.getFile("src/test/resources/aas/marking_ce.png");
File fileInSme = downloadFile("file_image.png", "testfile/File");
assertEquals(readFile(expectedFileInSme.toPath().toString(), Charset.defaultCharset()), readFile(fileInSme.toPath().toString(), Charset.defaultCharset()));
fileInSme.delete();

File expectedFileInSmeCollection = ResourceUtils.getFile("src/test/resources/aas/BaSyx.png");
File fileInSmeCollection = downloadFile("file_image.png", "testSMCollection/testFileInCollection/File");
assertEquals(readFile(expectedFileInSmeCollection.toPath().toString(), Charset.defaultCharset()), readFile(fileInSmeCollection.toPath().toString(), Charset.defaultCharset()));
fileInSmeCollection.delete();
}


private File downloadFile(String filename, String filePath) throws IOException, MalformedURLException, FileNotFoundException {
File actual = new File(filename);
BufferedInputStream in = new BufferedInputStream(new URL(API_URL + "/testaas/aas/submodels/testsm/submodel/submodelElements/" + filePath).openStream());

FileOutputStream fileOutputStream = new FileOutputStream(actual);
byte dataBuffer[] = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
fileOutputStream.write(dataBuffer, 0, bytesRead);
}
fileOutputStream.close();
return actual;
}

@Test
public void testUploadAASXWithEmptyBooleanValue() throws FileNotFoundException {
AASAggregatorAASXUploadProxy proxy = new AASAggregatorAASXUploadProxy(API_URL);
Expand All @@ -103,4 +148,8 @@ public void testUploadAASXWithEmptyBooleanValue() throws FileNotFoundException {
assertNull(intNullValue);
}

static String readFile(String path, Charset encoding) throws IOException {
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
}
Binary file added src/test/resources/aas/BaSyx.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file added src/test/resources/aas/marking_ce.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit be3438c

Please sign in to comment.