Refactors AASXPackageManager to use Apache POI library to read .aasx

Adds test to ensure AASXPackageManager can load .aasx files created by the AASXFactory

Signed-off-by: Maximilian Conradi <maximilian.conradi@iese.fraunhofer.de>
Change-Id: Ib2f9e240add0253f0fd508b3edd5fbbe47e48543
diff --git a/components/basys.components/basyx.components.docker/basyx.components.AASServer/pom.xml b/components/basys.components/basyx.components.docker/basyx.components.AASServer/pom.xml
index 88e4939..9480492 100644
--- a/components/basys.components/basyx.components.docker/basyx.components.AASServer/pom.xml
+++ b/components/basys.components/basyx.components.docker/basyx.components.AASServer/pom.xml
@@ -54,6 +54,14 @@
 			<artifactId>basyx.sdk</artifactId>
 			<classifier>tests</classifier>
 		</dependency>
+		
+		<!-- Used for reading .aasx files -->
+		<dependency>
+			<groupId>org.apache.poi</groupId>
+			<artifactId>poi-ooxml</artifactId>
+			<version>4.1.2</version>
+		</dependency>
+		
 	</dependencies>
 	
 	<profiles>
diff --git a/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/AASServerComponent.java b/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/AASServerComponent.java
index b09c4e6..1e5566e 100644
--- a/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/AASServerComponent.java
+++ b/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/AASServerComponent.java
@@ -11,6 +11,7 @@
 import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.catalina.servlets.DefaultServlet;
+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.aggregator.restapi.AASAggregatorProvider;
@@ -191,7 +192,7 @@
 	}
 
 	private void loadBundleFromAASX(String aasxPath)
-			throws IOException, ParserConfigurationException, SAXException, URISyntaxException {
+			throws IOException, ParserConfigurationException, SAXException, URISyntaxException, InvalidFormatException {
 		logger.info("Loading aas from aasx \"" + aasxPath + "\"");
 
 		// Instantiate the aasx package manager
@@ -233,7 +234,7 @@
 			} else if (aasSource.endsWith(".xml")) {
 				loadBundleFromXML(aasSource);
 			}
-		} catch (IOException | ParserConfigurationException | SAXException | URISyntaxException e) {
+		} catch (IOException | ParserConfigurationException | SAXException | URISyntaxException | InvalidFormatException e) {
 			logger.error("Could not load initial AAS from source '" + aasSource + "'");
 			logger.info("Starting empty server instead");
 		}
diff --git a/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/aasx/AASXPackageManager.java b/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/aasx/AASXPackageManager.java
index 8a116ab..8998f7f 100644
--- a/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/aasx/AASXPackageManager.java
+++ b/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/main/java/org/eclipse/basyx/components/aas/aasx/AASXPackageManager.java
@@ -2,9 +2,10 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.StringWriter;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
@@ -15,14 +16,16 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
 
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
 import org.eclipse.basyx.components.configuration.BaSyxConfiguration;
 import org.eclipse.basyx.components.xml.XMLAASBundleFactory;
 import org.eclipse.basyx.submodel.metamodel.api.ISubModel;
@@ -33,23 +36,24 @@
 import org.eclipse.basyx.vab.modelprovider.VABPathTools;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
+
 /**
  * The AASX package converter converts a aasx package into a list of aas, a list
  * of submodels a list of assets, a list of Concept descriptions
  * 
  * The aas provides the references to the submodels and assets
  * 
- * @author zhangzai
+ * @author zhangzai, conradi
  *
  */
 public class AASXPackageManager {
 
 
+	private static final String XML_TYPE = "http://www.admin-shell.io/aasx/relationships/aas-spec";
+	private static final String AASX_ORIGIN = "/aasx/aasx-origin";
+	
+	
 	/**
 	 * Path to the AASX package
 	 */
@@ -77,14 +81,16 @@
 		aasxPath = path;
 	}
 
-	public Set<AASBundle> retrieveAASBundles() throws IOException, ParserConfigurationException, SAXException {
+	public Set<AASBundle> retrieveAASBundles() throws IOException, ParserConfigurationException, SAXException, InvalidFormatException {
 		
 		// If the XML was already parsed return cached Bundles
 		if(bundles != null) {
 			return bundles;
 		}
 		
-		bundleFactory = new XMLAASBundleFactory(getXMLResourceString(aasxPath));
+		OPCPackage aasxRoot = OPCPackage.open(getInputStream(aasxPath));
+		
+		bundleFactory = new XMLAASBundleFactory(getXMLResourceString(aasxRoot));
 		
 		bundles = bundleFactory.create();
 		
@@ -92,133 +98,37 @@
 	}
 
 	/**
-	 * Find the path of the aas-xml file
-	 * 
-	 * @param stream - Stream of the aasx package
-	 * @return Path of the aas xml file, empty string if not found
-	 * @throws IOException
-	 * @throws ParserConfigurationException
-	 * @throws SAXException
-	 */
-	private String findAASXml(ZipInputStream stream) throws IOException, ParserConfigurationException, SAXException {
-		String path = "";
-		// find the entry of the aasx
-		for (ZipEntry entry; (entry = stream.getNextEntry()) != null;) {
-
-			// get name of the entry
-			String name = entry.getName();
-
-			// find the relationship file in the directory /aasx/_rels/aas_origin.rels
-			if (!entry.isDirectory() && name.startsWith("aasx/_rels")) {
-				// find the file aasx-origin.rels
-				if (name.endsWith("aasx-origin.rels")) {
-					// Get path of the aas xml
-					String aasXmlPath = findAASXMLAddress(stream);
-					if (!aasXmlPath.isEmpty()) {
-						path = aasXmlPath;
-						break;
-					}
-				}
-			}
-		}
-		return path;
-	}
-
-	/**
-	 * Get entry of a file
-	 * 
-	 * @param filename - name of a file with path
-	 * @return a file entry
-	 * @throws IOException
-	 */
-	private ZipInputStream returnFileEntryStream(String filename, ZipInputStream stream) throws IOException {
-		ZipInputStream str = null;
-		if (filename.startsWith("/")) {
-			filename = filename.substring(1);
-		}
-
-		// get all entries of the aasx
-		for (ZipEntry e; (e = stream.getNextEntry()) != null;) {
-			// get name of the entry
-			String name = e.getName();
-			if (name.equals(filename)) {
-				str = stream;
-				break;
-			}
-		}
-		return str;
-	}
-
-	/**
-	 * Parse the relationship file and find the path of the aas-XML file describing
-	 * the aas
-	 * 
-	 * @param ins - input stream of this relationship file
-	 * @return path of the aas-xml file
-	 * @throws ParserConfigurationException
-	 * @throws SAXException
-	 * @throws IOException
-	 */
-	private String findAASXMLAddress(InputStream ins) throws ParserConfigurationException, SAXException, IOException {
-		String path = "";
-
-		// create the XML document parser
-		DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
-		DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
-		Document doc = dBuilder.parse(ins);
-		doc.getDocumentElement().normalize();
-
-		// Get the tag with "Relationships"
-		logger.info("Root element :" + doc.getDocumentElement().getNodeName());
-		NodeList relList = doc.getElementsByTagName("Relationship");
-
-		// If there is only 1 relationship pointing to the aas-xml file, this should be
-		// the case
-		if (relList.getLength() == 1) {
-			Node first = relList.item(0);
-
-			if (first.getNodeType() == Node.ELEMENT_NODE) {
-				logger.info("\nCurrent Element :" + first.getNodeName());
-				// get the target file path
-				String targetFile = ((Element) first).getAttribute("Target");
-				String type = ((Element) first).getAttribute("Type");
-
-				// validate the relationship type
-				if (type.endsWith("aas-spec")) {
-					logger.info("target file name : " + targetFile);
-					path = targetFile;
-				}
-			}
-		}
-		return path;
-	}
-
-	/**
 	 * Return the Content of the xml file in the aasx-package as String
 	 * 
-	 * @param filePath - path to the aasx package
+	 * @param aasxPackage - the root package of the AASX
 	 * @return Content of XML as String
+	 * @throws InvalidFormatException 
 	 * @throws IOException
-	 * @throws ParserConfigurationException
-	 * @throws SAXException
 	 */
-	private String getXMLResourceString(String filePath) throws IOException, ParserConfigurationException, SAXException {
-		String aasXmlPath;
-		// Create the zip input stream
-		try (ZipInputStream stream = getZipInputStream(filePath)) {
+	private String getXMLResourceString(OPCPackage aasxPackage) throws InvalidFormatException, IOException {
 
-			// find the path of the aas xml
-			aasXmlPath = this.findAASXml(stream);
+		// Get the "/aasx/aasx-origin" Part. It is Relationship source for the XML-Document
+		PackagePart originPart = aasxPackage.getPart(PackagingURIHelper.createPartName(AASX_ORIGIN));
+		
+		// Get the Relation to the XML Document
+		PackageRelationshipCollection originRelationships = originPart.getRelationshipsByType(XML_TYPE);
+		
+		
+		// If there is more than one or no XML-Document that is an error
+		if(originRelationships.size() > 1) {
+			throw new RuntimeException("More than one 'aasx-spec' document found in .aasx");
+		} else if(originRelationships.size() == 0) {
+			throw new RuntimeException("No 'aasx-spec' document found in .aasx");
 		}
-
-		try (ZipInputStream stream = getZipInputStream(filePath)) {
-			// Find the entry of the aas xml
-			ZipInputStream streamPointingToEntry = this.returnFileEntryStream(aasXmlPath, stream);
-
-			// create the xml-converter with the input stream
-			String text = IOUtils.toString(streamPointingToEntry, StandardCharsets.UTF_8.name());
-			return text;
-		}
+		
+		// Get the PackagePart of the XML-Document
+		PackagePart xmlPart = originPart.getRelatedPart(originRelationships.getRelationship(0));
+		
+		// Read the content from the PackagePart
+		InputStream stream = xmlPart.getInputStream();
+		StringWriter writer = new StringWriter();
+		IOUtils.copy(stream, writer, StandardCharsets.UTF_8);
+		return writer.toString();
 	}
 
 	/**
@@ -229,10 +139,11 @@
 	 * @throws IOException
 	 * @throws SAXException
 	 * @throws ParserConfigurationException
+	 * @throws InvalidFormatException 
 	 * 
 	 */
 	private List<String> parseReferencedFilePathsFromAASX()
-			throws IOException, ParserConfigurationException, SAXException {
+			throws IOException, ParserConfigurationException, SAXException, InvalidFormatException {
 		
 		Set<AASBundle> bundles = retrieveAASBundles();
 		
@@ -285,14 +196,16 @@
 	 * @throws SAXException
 	 * @throws ParserConfigurationException
 	 * @throws URISyntaxException
+	 * @throws InvalidFormatException 
 	 */
 	public void unzipRelatedFiles()
-			throws IOException, ParserConfigurationException, SAXException, URISyntaxException {
+			throws IOException, ParserConfigurationException, SAXException, URISyntaxException, InvalidFormatException {
 		// load folder which stores the files
 		List<String> files = parseReferencedFilePathsFromAASX();
+		OPCPackage aasxRoot = OPCPackage.open(getInputStream(aasxPath));
 		for (String filePath : files) {
 			// name of the folder
-			unzipFile(filePath, aasxPath);
+			unzipFile(filePath, aasxRoot);
 		}
 	}
 
@@ -316,9 +229,9 @@
 	 * @param aasxPath    - aasx path
 	 * @throws IOException
 	 * @throws URISyntaxException
+	 * @throws InvalidFormatException 
 	 */
-	private void unzipFile(String filePath, String aasxPath)
-			throws IOException, URISyntaxException {
+	private void unzipFile(String filePath, OPCPackage aasxRoot) throws IOException, URISyntaxException, InvalidFormatException {
 		// Create destination directory
 		if (filePath.startsWith("/")) {
 			filePath = filePath.substring(1);
@@ -329,62 +242,31 @@
 		Path destDir = rootPath.resolve(relativePath);
 		logger.info("Unzipping to " + destDir);
 		Files.createDirectories(destDir);
-
-		// create buffer for the folder binary
-		byte[] buffer = new byte[1024];
-
-		// Find the file with the "filePath"
-		try (ZipInputStream stream = getZipInputStream(aasxPath)) {
-			ZipEntry zipEntry = stream.getNextEntry();
-			while (zipEntry != null) {
-				if (!zipEntry.isDirectory() && zipEntry.getName().contains(filePath)) {
-					// Create the file object in the destination directory
-					File newFile = newFile(destDir.toFile(), zipEntry);
-
-					// Create the file output stream
-					try (FileOutputStream fos = new FileOutputStream(newFile)) {
-						int len;
-						// Write the binary to the file
-						while ((len = stream.read(buffer)) > 0) {
-							fos.write(buffer, 0, len);
-						}
-					}
-					return;
-				}
-				zipEntry = stream.getNextEntry();
-			}
+		
+		PackagePart part = aasxRoot.getPart(PackagingURIHelper.createPartName("/" + filePath));
+		
+		if(part == null) {
+			logger.error("File '" + filePath + "' cloud not be unzipped. It does not exist in .aasx.");
+			throw new FileNotFoundException("File '" + filePath + "' cloud not be unzipped. It does not exist in .aasx.");
 		}
+		
+		String targetPath = destDir.toString() + "/" + VABPathTools.getLastElement(filePath);
+		InputStream stream = part.getInputStream();
+		FileUtils.copyInputStreamToFile(stream, new File(targetPath));
 	}
-
-	/**
-	 * Preventing Zip Slip, create a file
-	 * 
-	 * @param destinationDir
-	 * @param zipEntry
-	 * @return
-	 * @throws IOException
-	 */
-	private File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
-		String filename = VABPathTools.getLastElement(zipEntry.getName());
-
-		File destFile = new File(destinationDir, filename);
-
-		String destDirPath = destinationDir.getCanonicalPath();
-		String destFilePath = destFile.getCanonicalPath();
-
-		if (!destFilePath.startsWith(destDirPath + File.separator)) {
-			throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
-		}
-
-		return destFile;
-	}
-
-	private ZipInputStream getZipInputStream(String aasxFilePath) throws IOException {
-		try {
-			return new ZipInputStream(BaSyxConfiguration.getResourceStream(aasxFilePath));
-		} catch (NullPointerException ex) {
+	
+	private InputStream getInputStream(String aasxFilePath) throws IOException {
+		InputStream stream = BaSyxConfiguration.getResourceStream(aasxFilePath);
+		if(stream != null) {
+			return stream;
+		} else {
 			// Alternativ, if resource has not been found: load from a file
-			return new ZipInputStream(new FileInputStream(aasxFilePath));
+			try {
+				return new FileInputStream(aasxFilePath);
+			} catch (FileNotFoundException e) {
+				logger.error("File '" + aasxFilePath + "' to be loaded was not found.");
+				throw e;
+			}
 		}
 	}
 }
diff --git a/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/test/java/org/eclipse/basyx/regression/AASServer/TestAASXPackageManager.java b/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/test/java/org/eclipse/basyx/regression/AASServer/TestAASXPackageManager.java
index 51af080..466192f 100644
--- a/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/test/java/org/eclipse/basyx/regression/AASServer/TestAASXPackageManager.java
+++ b/components/basys.components/basyx.components.docker/basyx.components.AASServer/src/test/java/org/eclipse/basyx/regression/AASServer/TestAASXPackageManager.java
@@ -3,7 +3,9 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -12,19 +14,33 @@
 import java.util.Set;
 
 import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerException;
 
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.eclipse.basyx.aas.factory.aasx.AASXFactory;
+import org.eclipse.basyx.aas.factory.aasx.InMemoryFile;
 import org.eclipse.basyx.aas.metamodel.api.IAssetAdministrationShell;
+import org.eclipse.basyx.aas.metamodel.api.parts.asset.AssetKind;
+import org.eclipse.basyx.aas.metamodel.api.parts.asset.IAsset;
+import org.eclipse.basyx.aas.metamodel.map.AssetAdministrationShell;
+import org.eclipse.basyx.aas.metamodel.map.descriptor.ModelUrn;
+import org.eclipse.basyx.aas.metamodel.map.parts.Asset;
 import org.eclipse.basyx.components.aas.aasx.AASXPackageManager;
 import org.eclipse.basyx.submodel.metamodel.api.ISubModel;
 import org.eclipse.basyx.submodel.metamodel.api.identifier.IdentifierType;
+import org.eclipse.basyx.submodel.metamodel.api.parts.IConceptDescription;
 import org.eclipse.basyx.submodel.metamodel.api.reference.IKey;
 import org.eclipse.basyx.submodel.metamodel.api.reference.IReference;
 import org.eclipse.basyx.submodel.metamodel.api.submodelelement.ISubmodelElement;
+import org.eclipse.basyx.submodel.metamodel.map.SubModel;
+import org.eclipse.basyx.submodel.metamodel.map.reference.Reference;
 import org.eclipse.basyx.submodel.metamodel.map.submodelelement.SubmodelElementCollection;
+import org.eclipse.basyx.submodel.metamodel.map.submodelelement.dataelement.File;
 import org.eclipse.basyx.submodel.metamodel.map.submodelelement.dataelement.property.Property;
 import org.eclipse.basyx.submodel.metamodel.map.submodelelement.dataelement.property.valuetypedef.PropertyValueTypeDef;
 import org.eclipse.basyx.support.bundle.AASBundle;
-import org.junit.Before;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
 import org.xml.sax.SAXException;
 
@@ -33,26 +49,34 @@
  * submodels, assets and concept-descriptions. it also checks whether the aas
  * have correct references to the asets and submodels
  * 
- * @author zhangzai
+ * @author zhangzai, conradi
  *
  */
 public class TestAASXPackageManager {
 	/**
 	 * path to the aasx package
 	 */
-	private String aasxPath = "aasx/01_Festo.aasx";
+	private static final String aasxPath = "aasx/01_Festo.aasx";
+	
+	private static final String CREATED_AASX_PATH = "test.aasx";
 
 	/**
 	 * the aasx package converter
 	 */
-	private AASXPackageManager packageConverter;
+	private static AASXPackageManager packageConverter;
 
 	/**
 	 * this string array is used to check the refs to submodels of aas
 	 */
 	private String[] submodelids = { "www.company.com/ids/sm/6053_5072_7091_5102", "smart.festo.com/demo/sm/instance/1/1/13B7CCD9BF7A3F24", "www.company.com/ids/sm/4343_5072_7091_3242", "www.company.com/ids/sm/2543_5072_7091_2660",
 			"www.company.com/ids/sm/6563_5072_7091_4267"
-
+			
+	};
+	
+	/**
+	 * Files that are unzipped
+	 */
+	private static String[] unzipFiles = { "target/files/aasx/Document/docu.pdf", "target/files/icon.png"
 	};
 
 	/**
@@ -68,8 +92,8 @@
 	/**
 	 * Initialize the AASX package converter
 	 */
-	@Before
-	public void setup() {
+	@BeforeClass
+	public static void setup() {
 		// Create the aasx package converter with the path to the aasx package
 		packageConverter = new AASXPackageManager(aasxPath);
 	}
@@ -82,7 +106,7 @@
 		// Parse aas from the XML and create the AAS Bundle with refs to submodels
 		try {
 			aasBundles = packageConverter.retrieveAASBundles();
-		} catch (ParserConfigurationException | SAXException | IOException e) {
+		} catch (ParserConfigurationException | SAXException | IOException | InvalidFormatException e) {
 			e.printStackTrace();
 		}
 
@@ -92,6 +116,87 @@
 		// Check the submodels
 		checkSubmodels(submodels);
 	}
+	
+	
+	/**
+	 * Creates a new .aasx using the AASXFactory and tries to parse it
+	 */
+	@Test
+	public void testLoadGeneratedAASX()
+			throws InvalidFormatException, IOException, ParserConfigurationException, SAXException, TransformerException, URISyntaxException {
+		
+		List<IAssetAdministrationShell> aasList = new ArrayList<>();
+		List<ISubModel> submodelList = new ArrayList<>();
+		List<IAsset> assetList = new ArrayList<>();
+		List<IConceptDescription> conceptDescriptionList = new ArrayList<>();
+
+		List<InMemoryFile> fileList = new ArrayList<>();
+		
+		Asset asset = new Asset("asset-id", new ModelUrn("ASSET_IDENTIFICATION"), AssetKind.INSTANCE);
+		AssetAdministrationShell aas = new AssetAdministrationShell("aasIdShort", new ModelUrn("aasId"), asset);
+		aas.setAssetReference((Reference) asset.getReference());
+		
+		SubModel sm = new SubModel("smIdShort", new ModelUrn("smId"));
+		
+		// Create File SubmodelElements
+		File file1 = new File("/icon.png", "image/png");
+		file1.setIdShort("file1");
+		File file2 = new File("/aasx/Document/docu.pdf", "application/pdf");
+		file2.setIdShort("file2");
+		
+		SubmodelElementCollection collection = new SubmodelElementCollection("Marking_RCM");
+		collection.addSubModelElement(file1);
+		
+		sm.addSubModelElement(collection);
+		sm.addSubModelElement(file2);
+		aas.addSubModel(sm);
+		
+		aasList.add(aas);
+		submodelList.add(sm);
+		assetList.add(asset);
+
+		// Build InMemoryFiles for .aasx
+		byte[] content1 = {5,6,7,8,9};
+		InMemoryFile file = new InMemoryFile(content1, "/icon.png");
+		fileList.add(file);
+		
+		byte[] content2 = {10,11,12,13,14};
+		file = new InMemoryFile(content2, "aasx/Document/docu.pdf");
+		fileList.add(file);
+		
+		// Build AASX
+		FileOutputStream out = new FileOutputStream(CREATED_AASX_PATH);
+		AASXFactory.buildAASX(aasList, assetList, conceptDescriptionList, submodelList, fileList, out);
+		
+		AASXPackageManager packageManager = new AASXPackageManager(CREATED_AASX_PATH);
+		
+		checkBundle(packageManager.retrieveAASBundles(), aas, sm);
+		
+		// Unzip files from the .aasx
+		packageManager.unzipRelatedFiles();
+		
+		// Check if all expected files are present
+		for(String path: unzipFiles) {
+			assertTrue(new java.io.File(path).exists());
+		}
+		
+	}
+	
+	private void checkBundle(Set<AASBundle> bundles, IAssetAdministrationShell aas, ISubModel sm) {
+		assertEquals(1, bundles.size());
+		AASBundle bundle = bundles.stream().findFirst().get();
+		
+		IAssetAdministrationShell parsedAAS = bundle.getAAS();
+		assertEquals(aas.getIdShort(), parsedAAS.getIdShort());
+		assertEquals(aas.getIdentification().getId(), parsedAAS.getIdentification().getId());
+		
+		assertEquals(1, bundle.getSubmodels().size());
+		ISubModel parsedSubmodel = bundle.getSubmodels().stream().findFirst().get();
+		assertEquals(sm.getIdShort(), parsedSubmodel.getIdShort());
+		assertEquals(sm.getIdentification().getId(), parsedSubmodel.getIdentification().getId());
+		assertEquals(sm.getSubmodelElements().size(), parsedSubmodel.getSubmodelElements().size());
+	}
+	
 
 	/**
 	 * Check the parsed aas with expected ones
@@ -259,5 +364,17 @@
 		assertEquals("Street", prop2.getIdShort());
 		assertEquals("Ruiter Straße 82", prop2.get());
 	}
+	
+	
+	/**
+	 * Delete created files
+	 */
+	@AfterClass
+	public static void cleanUp() {
+		for(String path: unzipFiles) {
+			new java.io.File(path).delete();
+		}
+		new java.io.File(CREATED_AASX_PATH).delete();
+	}
 
 }
diff --git a/examples/basys.examples/src/main/java/org/eclipse/basyx/examples/scenarios/staticdynamic/StaticDynamicScenario.java b/examples/basys.examples/src/main/java/org/eclipse/basyx/examples/scenarios/staticdynamic/StaticDynamicScenario.java
index b1e858d..0c330b2 100644
--- a/examples/basys.examples/src/main/java/org/eclipse/basyx/examples/scenarios/staticdynamic/StaticDynamicScenario.java
+++ b/examples/basys.examples/src/main/java/org/eclipse/basyx/examples/scenarios/staticdynamic/StaticDynamicScenario.java
@@ -1,13 +1,13 @@
 package org.eclipse.basyx.examples.scenarios.staticdynamic;
 
 import java.io.IOException;
-import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
 import javax.xml.parsers.ParserConfigurationException;
 
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.eclipse.basyx.aas.aggregator.proxy.AASAggregatorProxy;
 import org.eclipse.basyx.aas.registration.proxy.AASRegistryProxy;
 import org.eclipse.basyx.components.IComponent;
@@ -45,11 +45,11 @@
 	
 	
 	
-	public static void main(String[] args) throws IOException, ParserConfigurationException, SAXException, URISyntaxException {
+	public static void main(String[] args) throws InvalidFormatException, IOException, ParserConfigurationException, SAXException {
 		new StaticDynamicScenario();
 	}
 
-	public StaticDynamicScenario() throws IOException, ParserConfigurationException, SAXException, URISyntaxException {
+	public StaticDynamicScenario() throws InvalidFormatException, IOException, ParserConfigurationException, SAXException {
 		
 		// Startup the registry server
 		startRegistry();