Skip to content

Commit

Permalink
Fixes AASXDeserializer getRelatedFiles crashes
Browse files Browse the repository at this point in the history
* Only file URLs are tried to resolve
* Non-existing files lead to a warning

Signed-off-by: Frank Schnicke <[email protected]>
  • Loading branch information
FrankSchnicke committed Jan 4, 2024
1 parent 1c4a6b8 commit 3597022
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 53 deletions.
5 changes: 5 additions & 0 deletions dataformat-aasx/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,10 @@
<groupId>${project.groupId}</groupId>
<artifactId>dataformat-core</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
Expand Down Expand Up @@ -123,10 +124,14 @@ public String getXMLResourceString() throws InvalidFormatException, IOException
* if deserialization of the serialized aas environment fails
*/
public List<InMemoryFile> getRelatedFiles() throws InvalidFormatException, IOException, DeserializationException {
List<String> filePaths = parseReferencedFilePathsFromAASX();
List<String> filePaths = parseReferencedFilePathsFromAASX().stream().filter(AASXUtils::isFilePath).collect(Collectors.toList());
List<InMemoryFile> files = new ArrayList<>();
for (String filePath : filePaths) {
files.add(readFile(aasxRoot, filePath));
try {
files.add(readFile(aasxRoot, filePath));
} catch (Exception e) {
logger.warn("Loading file " + filePath + " failed and will not be included. Exception: " + e);
}
}
return files;
}
Expand Down Expand Up @@ -227,7 +232,7 @@ private List<String> parseElements(Collection<SubmodelElement> elements) {
}

private InMemoryFile readFile(OPCPackage aasxRoot, String filePath) throws InvalidFormatException, IOException {
PackagePart part = aasxRoot.getPart(PackagingURIHelper.createPartName(AASXUtils.getPathFromURL(filePath)));
PackagePart part = aasxRoot.getPart(PackagingURIHelper.createPartName(AASXUtils.removeFilePartOfURI(filePath)));
InputStream stream = part.getInputStream();
return new InMemoryFile(stream.readAllBytes(), filePath);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ private void storeFilesInAASX(Environment environment, Collection<InMemoryFile>
&& aas.getAssetInformation().getDefaultThumbnail() != null
&& aas.getAssetInformation().getDefaultThumbnail().getPath() != null)
.forEach(aas -> createParts(files,
AASXUtils.getPathFromURL(aas.getAssetInformation().getDefaultThumbnail().getPath()),
AASXUtils.removeFilePartOfURI(aas.getAssetInformation().getDefaultThumbnail().getPath()),
rootPackage, xmlPart, aas.getAssetInformation().getDefaultThumbnail().getContentType()));
environment.getSubmodels().forEach(sm ->
findFileElements(sm.getSubmodelElements()).forEach(file -> createParts(files,
AASXUtils.getPathFromURL(file.getValue()), rootPackage, xmlPart, file.getContentType())));
AASXUtils.removeFilePartOfURI(file.getValue()), rootPackage, xmlPart, file.getContentType())));
}

/**
Expand Down Expand Up @@ -265,7 +265,7 @@ private void prepareFilePaths(Collection<Submodel> submodels) {
*/
private InMemoryFile findFileByPath(Collection<InMemoryFile> files, String path) {
for (InMemoryFile file : files) {
if (AASXUtils.getPathFromURL(file.getPath()).equals(path)) {
if (AASXUtils.removeFilePartOfURI(file.getPath()).equals(path)) {
return file;
}
}
Expand All @@ -279,11 +279,10 @@ private InMemoryFile findFileByPath(Collection<InMemoryFile> files, String path)
* @return the prepared path
*/
private String preparePath(String path) {
String newPath = AASXUtils.getPathFromURL(path);
if (!newPath.startsWith("file://")) {
newPath = "file://" + newPath;
if (path.startsWith("/")) {
path = "file://" + path;
}
return newPath;
return path;
}

}
Original file line number Diff line number Diff line change
@@ -1,40 +1,48 @@
/*
* Copyright (c) 2024 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 org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.internal;

/**
* @author schnicke
*/
public class AASXUtils {


/**
* Gets the path from a URL e.g "http://localhost:8080/path/to/test.file"
* results in "/path/to/test.file"
* Removes the file: or file:// suffix of an URI
*
* @param url URL to get the path for
* @return the path from the URL
* @param uri
* URI to remove the file suffix from
* @return the URI without the file suffix
*/
public static String getPathFromURL(String url) {
if (url == null) {
public static String removeFilePartOfURI(String uri) {
if (uri == null) {
return null;
}

if (url.contains("://")) {

// Find the ":" and and remove the "http://" from the url
int index = url.indexOf(":") + 3;
url = url.substring(index);

// Find the first "/" from the URL (now without the "http://") and remove
// everything before that
index = url.indexOf("/");
url = url.substring(index);

// Recursive call to deal with more than one server parts
// (e.g. basyx://127.0.0.1:6998//https://localhost/test/)
return getPathFromURL(url);
} else {
// Make sure the path has a / at the start
if (!url.startsWith("/")) {
url = "/" + url;
}
return url;
if (uri.startsWith("file://")) {
return uri.replaceFirst("file://", "");
} else if (uri.startsWith("file:")) {
return uri.replaceFirst("file:", "");
}

return uri;
}

public static boolean isFilePath(String uri) {
return uri.startsWith("/") || uri.startsWith("file:") || uri.startsWith("./") || uri.startsWith("../");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@
*/
package org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.deserialization;

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

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.eclipse.digitaltwin.aas4j.v3.dataformat.DeserializationException;
Expand All @@ -23,31 +37,25 @@
import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.AASXSerializer;
import org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.InMemoryFile;
import org.eclipse.digitaltwin.aas4j.v3.dataformat.core.AASSimple;
import org.eclipse.digitaltwin.aas4j.v3.model.Environment;
import org.eclipse.digitaltwin.aas4j.v3.model.File;
import org.eclipse.digitaltwin.aas4j.v3.model.Submodel;
import org.eclipse.digitaltwin.aas4j.v3.model.SubmodelElement;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultEnvironment;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultFile;
import org.eclipse.digitaltwin.aas4j.v3.model.impl.DefaultSubmodel;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

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

public class AASXDeserializerTest {

@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();

@Test
public void testRoundTrip() throws SerializationException, IOException, InvalidFormatException, DeserializationException, ParserConfigurationException, SAXException {

public void roundTrip() throws SerializationException, IOException, InvalidFormatException, DeserializationException, ParserConfigurationException, SAXException {
List<InMemoryFile> fileList = new ArrayList<>();
byte[] operationManualContent = { 0, 1, 2, 3, 4 };
byte[] thumbnail = { 0, 1, 2, 3, 4 };
Expand All @@ -56,7 +64,7 @@ public void testRoundTrip() throws SerializationException, IOException, InvalidF
fileList.add(inMemoryFile);
fileList.add(inMemoryFileThumbnail);

File file = tempFolder.newFile("output.aasx");
java.io.File file = tempFolder.newFile("output.aasx");

new AASXSerializer().write(AASSimple.createEnvironment(), fileList, new FileOutputStream(file));

Expand All @@ -66,4 +74,27 @@ public void testRoundTrip() throws SerializationException, IOException, InvalidF
assertEquals(AASSimple.createEnvironment(), deserializer.read());
assertTrue(CollectionUtils.isEqualCollection(fileList, deserializer.getRelatedFiles()));
}

@Test
public void relatedFilesAreOnlyResolvedIfWithinAASX() throws IOException, SerializationException, InvalidFormatException, DeserializationException {
Submodel fileSm = new DefaultSubmodel.Builder().id("doesNotMatter").submodelElements(createFileSubmodelElements()).build();
Environment env = new DefaultEnvironment.Builder().submodels(fileSm).build();

byte[] image = { 0, 1, 2, 3, 4 };
InMemoryFile inMemoryFile = new InMemoryFile(image, "file:///aasx/internalFile.jpg");

java.io.File file = tempFolder.newFile("output.aasx");
new AASXSerializer().write(env, Collections.singleton(inMemoryFile), new FileOutputStream(file));

InputStream in = new FileInputStream(file);
AASXDeserializer deserializer = new AASXDeserializer(in);

assertEquals(Collections.singletonList(inMemoryFile), deserializer.getRelatedFiles());
}

private static List<SubmodelElement> createFileSubmodelElements() {
File internalFile = new DefaultFile.Builder().idShort("internalFile").contentType("image/jpeg").value("file:///aasx/internalFile.jpg").build();
File externalFile = new DefaultFile.Builder().idShort("externalFile").contentType("image/jpeg").value("http://doesNotMatter.com/image").build();
return Arrays.asList(internalFile, externalFile);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2024 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 org.eclipse.digitaltwin.aas4j.v3.dataformat.aasx.internal;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

/**
* @author schnicke
*/
public class TestAASXUtils {

@Test
public void isFilePath() {
// Cf. RFC8089
String[] filePaths = {
"file://a", "file:a", "./b/c", "../b/c/d", "/a"
};

String[] notFilePaths = {
"http://admin-shell.io/example", "ftp://admin-shell.io/example"
};

for (String filePath : filePaths) {
assertTrue(AASXUtils.isFilePath(filePath));
}

for (String filePath : notFilePaths) {
assertFalse(AASXUtils.isFilePath(filePath));
}
}

@Test
public void removeFilePartOfURI() {
String[] filePaths = {
"file:///a", "file:/a", "/a"
};

for (String filePath : filePaths) {
assertEquals("/a", AASXUtils.removeFilePartOfURI(filePath));
}
}
}
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
<revision.major>1</revision.major>
<revision.minor>0</revision.minor>
<revision.patch>0</revision.patch>
<revision.suffix>-milestone-04</revision.suffix>
<revision.suffix>-SNAPSHOT</revision.suffix>
<revision>${revision.major}.${revision.minor}.${revision.patch}${revision.suffix}</revision>
<model.version>1.0.0-milestone-04</model.version>
<model.version>1.0.0-SNAPSHOT</model.version>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
Expand Down

0 comments on commit 3597022

Please sign in to comment.