Skip to content

Commit

Permalink
Fixes file collisions for multiple aasx source files
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Espen <Daniel.Espen@iese.fraunhofer.de>
  • Loading branch information
Daespen committed Jul 7, 2023
1 parent c9c6c37 commit 27f6daf
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -125,19 +124,17 @@ public class AASServerComponent implements IComponent {
private BaSyxMongoDBConfiguration mongoDBConfig;
private BaSyxSecurityConfiguration securityConfig;

private List<IAASServerFeature> aasServerFeatureList = new ArrayList<IAASServerFeature>();

// Initial AASBundle
protected Collection<AASBundle> aasBundles;
private List<IAASServerFeature> aasServerFeatureList = new ArrayList<>();
protected List<Collection<AASBundle>> aasBundles = new ArrayList<>();

private IAASAggregator aggregator;
// Watcher for AAS Aggregator functionality
private boolean isAASXUploadEnabled = false;

private static final String PREFIX_SUBMODEL_PATH = "/aas/submodels/";
private static final String AASX_RES_FILE_CONTEXT_PATH = "/basyx-temp";
private static final String AASX_RES_FILE_DOCBASE_PATH = System.getProperty("java.io.tmpdir") + AASX_RES_FILE_CONTEXT_PATH;
private static final String AASX_RES_FILE_SERVLET_MAPPING_PATTERN = "/files/*";
private static final String AASX_RES_FILE_CONTEXT_PATH = AASXToMetamodelConverter.TEMP_DIRECTORY;
private static final String AASX_RES_FILE_DOCBASE_PATH = VABPathTools.append(System.getProperty("java.io.tmpdir"), AASX_RES_FILE_CONTEXT_PATH);
private static final String AASX_RES_FILE_SERVLET_MAPPING_PATTERN = "/*";

/**
* Constructs an empty AAS server using the passed context
Expand Down Expand Up @@ -217,7 +214,8 @@ public void setRegistry(IAASRegistry registry) {
* The bundles that will be loaded during startup
*/
public void setAASBundles(Collection<AASBundle> aasBundles) {
this.aasBundles = aasBundles;
this.aasBundles = new ArrayList<>();
this.aasBundles.add(aasBundles);
}

/**
Expand All @@ -227,7 +225,9 @@ public void setAASBundles(Collection<AASBundle> aasBundles) {
* The bundle that will be loaded during startup
*/
public void setAASBundle(AASBundle aasBundle) {
this.aasBundles = Collections.singleton(aasBundle);
this.aasBundles = new ArrayList<Collection<AASBundle>>();
Collection<AASBundle> firstBundleSet = Collections.singleton(aasBundle);
this.aasBundles.add(firstBundleSet);
}

/**
Expand Down Expand Up @@ -263,7 +263,7 @@ public void startComponent() {
addAasxFilesResourceServlet(context);

// 2. Fix the file paths according to the servlet configuration
modifyFilePaths(contextConfig.getHostname(), contextConfig.getPort(), contextConfig.getContextPath() + AASX_RES_FILE_CONTEXT_PATH);
modifyFilePaths(contextConfig.getHostname(), contextConfig.getPort(), getRootFilePathWithContext(contextConfig.getContextPath()));

registerWhitelistedSubmodels();
}
Expand All @@ -275,6 +275,10 @@ public void startComponent() {
registerPreexistingAASAndSMIfPossible();
}

private String getRootFilePathWithContext(String contextPath) {
return VABPathTools.append(contextPath, AASX_RES_FILE_CONTEXT_PATH);
}

private DefaultServlet createDefaultServlet() {
if (aasConfig.isAuthorizationEnabled()) {
final AuthorizedDefaultServletParams<?> params = getAuthorizedDefaultServletParams();
Expand Down Expand Up @@ -551,13 +555,13 @@ private Set<AASBundle> loadBundleFromJSON(String jsonPath) throws IOException {
return new JSONAASBundleFactory(jsonContent).create();
}

private static Set<AASBundle> loadBundleFromAASX(String aasxPath) throws IOException, ParserConfigurationException, SAXException, InvalidFormatException, URISyntaxException {
private static Set<AASBundle> loadBundleFromAASX(String aasxPath, String childFilePath) throws IOException, ParserConfigurationException, SAXException, InvalidFormatException, URISyntaxException {
logger.info("Loading aas from aasx \"" + aasxPath + "\"");

// Instantiate the aasx package manager
try (AASXToMetamodelConverter packageManager = new AASXToMetamodelConverter(aasxPath)) {
// Unpack the files referenced by the aas
packageManager.unzipRelatedFiles();
packageManager.unzipRelatedFilesToChildPath(childFilePath);

// Retrieve the aas from the package
return packageManager.retrieveAASBundles();
Expand All @@ -571,13 +575,21 @@ private void addAASServerFeaturesToContext(BaSyxContext context) {
}
}

private Collection<AASBundle> getFlatAASBundles() {
Collection<AASBundle> result = new ArrayList<AASBundle>();
for (Collection<AASBundle> bundle : this.aasBundles) {
result.addAll(bundle);
}
return result;
}

private VABHTTPInterface<?> createAggregatorServlet() {
aggregator = createAASAggregator();
loadAASBundles();

if (aasBundles != null) {
try (final var ignored = ElevatedCodeAuthentication.enterElevatedCodeAuthenticationArea()) {
AASBundleHelper.integrate(aggregator, aasBundles);
AASBundleHelper.integrate(aggregator, getFlatAASBundles());
}
}

Expand Down Expand Up @@ -623,30 +635,39 @@ private List<IAASServerDecorator> createAASServerDecoratorList() {
}

private void loadAASBundles() {
if (aasBundles != null) {
if (!aasBundles.isEmpty()) {
return;
}

List<String> aasSources = aasConfig.getAASSourceAsList();
aasBundles = loadAASFromSource(aasSources);
}

private Set<AASBundle> loadAASFromSource(List<String> aasSources) {
private List<Collection<AASBundle>> loadAASFromSource(List<String> aasSources) {
if (aasSources.isEmpty()) {
return Collections.emptySet();
return new ArrayList<>();
}

Set<AASBundle> aasBundlesSet = new HashSet<>();
List<Collection<AASBundle>> aasBundlesSet = new ArrayList<>();

aasSources.stream().map(this::loadBundleFromFile).forEach(aasBundlesSet::addAll);
for (int i = 0; i < aasSources.size(); i++) {
String aasSource = aasSources.get(i);
String subFilePath = getAASXFileSubPath(i);
Set<AASBundle> loadedBundles = loadBundleFromFile(aasSource, subFilePath);
aasBundlesSet.add(loadedBundles);
}

return aasBundlesSet;
}

private Set<AASBundle> loadBundleFromFile(String aasSource) {
private String getAASXFileSubPath(int aasxIndex) {
return "aasx" + Integer.toString(aasxIndex);
}

private Set<AASBundle> loadBundleFromFile(String aasSource, String childFilePath) {
try {
if (aasSource.endsWith(".aasx")) {
return loadBundleFromAASX(aasSource);
return loadBundleFromAASX(aasSource, childFilePath);
} else if (aasSource.endsWith(".json")) {
return loadBundleFromJSON(aasSource);
} else if (aasSource.endsWith(".xml")) {
Expand Down Expand Up @@ -739,7 +760,8 @@ private String getSMEndpoint(IIdentifier smId) {
}

private String getSMIdShortFromSMId(IIdentifier smId) {
for (AASBundle bundle : aasBundles) {
Collection<AASBundle> flatAasBundles = getFlatAASBundles();
for (AASBundle bundle : flatAasBundles) {
for (ISubmodel sm : bundle.getSubmodels()) {
if (smId.getId().equals(sm.getIdentification().getId())) {
return sm.getIdShort();
Expand All @@ -750,7 +772,8 @@ private String getSMIdShortFromSMId(IIdentifier smId) {
}

private String getAASIdFromSMId(IIdentifier smId) {
for (AASBundle bundle : aasBundles) {
Collection<AASBundle> flatAasBundles = getFlatAASBundles();
for (AASBundle bundle : flatAasBundles) {
for (ISubmodel sm : bundle.getSubmodels()) {
if (smId.getId().equals(sm.getIdentification().getId())) {
return bundle.getAAS().getIdentification().getId();
Expand All @@ -765,11 +788,18 @@ private String getAASIdFromSMId(IIdentifier smId) {
* configuration
*/
private void modifyFilePaths(String hostName, int port, String rootPath) {
rootPath = rootPath + "/files";
for (AASBundle bundle : aasBundles) {
for (int i = 0; i < aasBundles.size(); i++) {
Collection<AASBundle> bundleSet = aasBundles.get(i);
String bundleFileRootPath = VABPathTools.concatenatePaths(rootPath, getAASXFileSubPath(i), "files");
modifyFilePathsInBundleSet(bundleSet, hostName, port, bundleFileRootPath);
}
}

private void modifyFilePathsInBundleSet(Collection<AASBundle> bundleSet, String hostName, int port, String bundleFileRootPath) {
for (AASBundle bundle : bundleSet) {
Set<ISubmodel> submodels = bundle.getSubmodels();
for (ISubmodel sm : submodels) {
SubmodelFileEndpointLoader.setRelativeFileEndpoints(sm, hostName, port, rootPath);
SubmodelFileEndpointLoader.setRelativeFileEndpoints(sm, hostName, port, bundleFileRootPath);
}
}
}
Expand All @@ -778,7 +808,11 @@ private String getMqttAASClientId() {
if (aasBundles == null || aasBundles.isEmpty()) {
return "defaultNoShellId";
}
return aasBundles.stream().findFirst().get().getAAS().getIdShort();
Collection<AASBundle> firstBundleSet = aasBundles.get(0);
if (firstBundleSet == null || firstBundleSet.isEmpty()) {
return "defaultNoShellId";
}
return firstBundleSet.stream().findFirst().get().getAAS().getIdShort();
}

private String getMqttSubmodelClientId() {
Expand All @@ -788,8 +822,7 @@ private String getMqttSubmodelClientId() {
private void addAasxFilesResourceServlet(BaSyxContext context) {
HttpServlet httpServlet = createDefaultServlet();

String childContextPath = contextConfig.getContextPath() + AASX_RES_FILE_CONTEXT_PATH;

String childContextPath = VABPathTools.append(contextConfig.getContextPath(), AASX_RES_FILE_CONTEXT_PATH);
Context childContext = createChildContextForAasxResourceFiles(childContextPath, AASX_RES_FILE_DOCBASE_PATH);

context.addChildContext(new BaSyxChildContext(childContext, httpServlet, AASX_RES_FILE_SERVLET_MAPPING_PATTERN));
Expand All @@ -800,7 +833,6 @@ private Context createChildContextForAasxResourceFiles(String childContextPath,
childContext.setPath(childContextPath);
childContext.setDocBase(childDocbasePath);
childContext.addLifecycleListener(new Tomcat.FixContextListener());

return childContext;
}

Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.eclipse.basyx.aas.manager.ConnectedAssetAdministrationShellManager;
import org.eclipse.basyx.aas.metamodel.connected.ConnectedAssetAdministrationShell;
import org.eclipse.basyx.aas.metamodel.map.descriptor.AASDescriptor;
import org.eclipse.basyx.aas.metamodel.map.descriptor.CustomId;
import org.eclipse.basyx.aas.metamodel.map.descriptor.ModelUrn;
import org.eclipse.basyx.aas.metamodel.map.descriptor.SubmodelDescriptor;
import org.eclipse.basyx.aas.registration.api.IAASRegistry;
Expand Down Expand Up @@ -72,11 +73,25 @@ public abstract class AASXSuite {
protected static final String aasShortId = "Festo_3S7PM0CP4BD";
protected static final ModelUrn aasId = new ModelUrn("smart.festo.com/demo/aas/1/1/454576463545648365874");
protected static final ModelUrn smId = new ModelUrn("www.company.com/ids/sm/4343_5072_7091_3242");
protected static final String smShortId = "Nameplate";
protected static final String smIdShort = "Nameplate";

protected static final String aasAIdShort = "aasA";
protected static final String smAIdShort = "a";
protected static final CustomId aasAId = new CustomId("AssetAdministrationShell---51A6D8AE");
protected static final CustomId smAId = new CustomId("fileTestA");
protected static final String aasBIdShort = "aasB";
protected static final String smBIdShort = "b";
protected static final CustomId aasBId = new CustomId("AssetAdministrationShell---51A6D8AF");
protected static final CustomId smBId = new CustomId("fileTestB");
protected static final String fileShortIdPath = "file";

// Has to be individualized by each test inheriting from this suite
protected static String aasEndpoint;
protected static String smEndpoint;
protected static String aasAEndpoint;
protected static String smAEndpoint;
protected static String aasBEndpoint;
protected static String smBEndpoint;
protected static String rootEndpoint;

private ConnectedAssetAdministrationShellManager manager;
Expand All @@ -93,14 +108,53 @@ public void setUp() {
// Create a dummy registry to test integration of XML AAS
aasRegistry = new InMemoryRegistry();
AASDescriptor descriptor = new AASDescriptor(aasShortId, aasId, aasEndpoint);
descriptor.addSubmodelDescriptor(new SubmodelDescriptor(smShortId, smId, smEndpoint));
descriptor.addSubmodelDescriptor(new SubmodelDescriptor(smIdShort, smId, smEndpoint));
aasRegistry.register(descriptor);
AASDescriptor descriptorA = new AASDescriptor(aasAIdShort, aasAId, aasAEndpoint);
descriptorA.addSubmodelDescriptor(new SubmodelDescriptor(smAIdShort, smAId, smAEndpoint));
aasRegistry.register(descriptorA);
AASDescriptor descriptorB = new AASDescriptor(aasBIdShort, aasBId, aasBEndpoint);
descriptorB.addSubmodelDescriptor(new SubmodelDescriptor(smBIdShort, smBId, smBEndpoint));
aasRegistry.register(descriptorB);

// Create a ConnectedAssetAdministrationShell using a
// ConnectedAssetAdministrationShellManager
IConnectorFactory connectorFactory = new HTTPConnectorFactory();
manager = new ConnectedAssetAdministrationShellManager(aasRegistry, connectorFactory);
}
// 0
// smart.festo.com/demo/aas/1/1/454576463545648365874={modelType={name=AssetAdministrationShellDescriptor},
// idShort=Festo_3S7PM0CP4BD, identification={idType=IRI,
// id=smart.festo.com/demo/aas/1/1/454576463545648365874},
// endpoints=[{type=http,
// address=http://localhost:4001/aasServer//shells/smart.festo.com%2Fdemo%2Faas%2F1%2F1%2F454576463545648365874/aas}],
// submodels=[{modelType={name=SubmodelDescriptor}, idShort=Nameplate,
// identification={idType=IRI, id=www.company.com/ids/sm/4343_5072_7091_3242},
// endpoints=[{type=http,
// address=http://localhost:4001/aasServer//shells/smart.festo.com%2Fdemo%2Faas%2F1%2F1%2F454576463545648365874/aas/submodels/Nameplate/submodel}]}]}
// 1
// AssetAdministrationShell---51A6D8AE={
// modelType={name=AssetAdministrationShellDescriptor},
// idShort=aasA,
// identification={idType=Custom, id=AssetAdministrationShell---51A6D8AE},
// endpoints=[
// {type=http,
// address=http://localhost:4001/aasServer//shells/AssetAdministrationShell---51A6D8AE/aas}],
// submodels=[
// {modelType={name=SubmodelDescriptor}, idShort=a,
// identification={idType=Custom, id=fileTestA},
// endpoints=[{type=http,
// address=http://localhost:4001/aasServer//shells/smart.festo.com%2Fdemo%2Faas%2F1%2F1%2F454576463545648365874/aas/submodels/a/submodel}]}]}
// 2
// AssetAdministrationShell---51A6D8AF={modelType={name=AssetAdministrationShellDescriptor},
// idShort=aasB, identification={idType=Custom,
// id=AssetAdministrationShell---51A6D8AF}, endpoints=[{type=http,
// address=null}], submodels=[{modelType={name=SubmodelDescriptor}, idShort=b,
// identification={idType=Custom, id=fileTestB}, endpoints=[{type=http,
// address=http://localhost:4001/aasServer//shells/smart.festo.com%2Fdemo%2Faas%2F1%2F1%2F454576463545648365874/aas/submodels/b/submodel}]}]}
// AssetAdministrationShell---51A6D8AF
// not found (A)
// AssetAdministrationShell---51A6D8AE

@Test
public void testGetSingleAAS() throws Exception {
Expand All @@ -110,18 +164,18 @@ public void testGetSingleAAS() throws Exception {

@Test
public void testGetSingleSubmodel() throws Exception {
ISubmodel subModel = getConnectedSubmodel();
assertEquals(smShortId, subModel.getIdShort());
ISubmodel subModel = manager.retrieveSubmodel(aasId, smId);
assertEquals(smIdShort, subModel.getIdShort());
}

@Test
public void testGetSingleModule() throws Exception {
final String FILE_ENDING = "basyx-temp/files/aasx/Nameplate/marking_rcm.jpg";
final String FILE_PATH = rootEndpoint + "basyx-temp/files/aasx/Nameplate/marking_rcm.jpg";
final String FILE_ENDING = "basyx-temp/aasx0/files/aasx/Nameplate/marking_rcm.jpg";
final String FILE_PATH = rootEndpoint + "basyx-temp/aasx0/files/aasx/Nameplate/marking_rcm.jpg";
checkFile(FILE_PATH);

// Get the submdoel nameplate
ISubmodel nameplate = getConnectedSubmodel();
ISubmodel nameplate = manager.retrieveSubmodel(aasId, smId);
// Get the submodel element collection marking_rcm
ConnectedSubmodelElementCollection marking_rcm = (ConnectedSubmodelElementCollection) nameplate.getSubmodelElements().get("Marking_RCM");
Collection<ISubmodelElement> values = marking_rcm.getValue();
Expand All @@ -139,6 +193,24 @@ public void testGetSingleModule() throws Exception {
}
}
}

@Test
public void testCollidingFiles() throws Exception {
final String FILE_ENDING_A = "basyx-temp/aasx1/files/aasx/files/text.txt";
final String FILE_ENDING_B = "basyx-temp/aasx2/files/aasx/files/text.txt";

checkFile(rootEndpoint + FILE_ENDING_A);
checkFile(rootEndpoint + FILE_ENDING_B);

ISubmodel smA = manager.retrieveSubmodel(aasAId, smAId);
ISubmodel smB = manager.retrieveSubmodel(aasBId, smBId);

String fileAValue = (String) smA.getSubmodelElement("file").getValue();
String fileBValue = (String) smB.getSubmodelElement("file").getValue();

assertTrue(fileAValue.endsWith(FILE_ENDING_A));
assertTrue(fileBValue.endsWith(FILE_ENDING_B));
}

@Test
public void testAllFiles() throws Exception {
Expand Down Expand Up @@ -187,14 +259,4 @@ private void checkFile(String absolutePath) {
private ConnectedAssetAdministrationShell getConnectedAssetAdministrationShell() throws Exception {
return manager.retrieveAAS(aasId);
}

/**
* Gets the connected Submodel
*
* @return connected SM
* @throws Exception
*/
private ISubmodel getConnectedSubmodel() {
return manager.retrieveSubmodel(aasId, smId);
}
}
Loading

0 comments on commit 27f6daf

Please sign in to comment.