2424 ******************************************************************************/
2525package org .eclipse .basyx .components .aas ;
2626
27+ import java .io .File ;
2728import java .io .IOException ;
2829import java .net .URISyntaxException ;
2930import java .nio .charset .StandardCharsets ;
3031import java .util .ArrayList ;
3132import java .util .Collection ;
3233import java .util .Collections ;
33- import java .util .HashSet ;
3434import java .util .List ;
3535import java .util .Map ;
3636import java .util .Set ;
3737
38+ import javax .servlet .http .HttpServlet ;
3839import javax .xml .parsers .ParserConfigurationException ;
3940
41+ import org .apache .catalina .Context ;
42+ import org .apache .catalina .core .StandardContext ;
4043import org .apache .catalina .servlets .DefaultServlet ;
44+ import org .apache .catalina .startup .Tomcat ;
4145import org .apache .commons .io .IOUtils ;
4246import org .apache .poi .openxml4j .exceptions .InvalidFormatException ;
4347import org .eclipse .basyx .aas .aggregator .AASAggregatorAPIHelper ;
6165import org .eclipse .basyx .components .aas .aascomponent .IAASServerFeature ;
6266import org .eclipse .basyx .components .aas .aascomponent .InMemoryAASServerComponentFactory ;
6367import org .eclipse .basyx .components .aas .aascomponent .MongoDBAASServerComponentFactory ;
64- import org .eclipse .basyx .components .aas .aasx .AASXPackageManager ;
6568import org .eclipse .basyx .components .aas .authorization .AuthorizedAASServerFeature ;
6669import org .eclipse .basyx .components .aas .authorization .internal .AuthorizedAASServerFeatureFactory ;
6770import org .eclipse .basyx .components .aas .authorization .internal .AuthorizedDefaultServlet ;
9396import org .eclipse .basyx .vab .exception .provider .ProviderException ;
9497import org .eclipse .basyx .vab .exception .provider .ResourceNotFoundException ;
9598import org .eclipse .basyx .vab .modelprovider .VABPathTools ;
99+ import org .eclipse .basyx .vab .protocol .http .server .BaSyxChildContext ;
96100import org .eclipse .basyx .vab .protocol .http .server .BaSyxContext ;
97101import org .eclipse .basyx .vab .protocol .http .server .BaSyxHTTPServer ;
98102import org .eclipse .basyx .vab .protocol .http .server .VABHTTPInterface ;
107111 *
108112 * @author schnicke, espen, fried, fischer, danish, wege
109113 */
110- @ SuppressWarnings ("deprecation" )
111114public class AASServerComponent implements IComponent {
112115 private static Logger logger = LoggerFactory .getLogger (AASServerComponent .class );
113116
@@ -121,16 +124,17 @@ public class AASServerComponent implements IComponent {
121124 private BaSyxMongoDBConfiguration mongoDBConfig ;
122125 private BaSyxSecurityConfiguration securityConfig ;
123126
124- private List <IAASServerFeature > aasServerFeatureList = new ArrayList <IAASServerFeature >();
125-
126- // Initial AASBundle
127- protected Collection <AASBundle > aasBundles ;
127+ private List <IAASServerFeature > aasServerFeatureList = new ArrayList <>();
128+ protected List <Collection <AASBundle >> aasBundles = new ArrayList <>();
128129
129130 private IAASAggregator aggregator ;
130131 // Watcher for AAS Aggregator functionality
131132 private boolean isAASXUploadEnabled = false ;
132133
133134 private static final String PREFIX_SUBMODEL_PATH = "/aas/submodels/" ;
135+ private static final String AASX_RES_FILE_CONTEXT_PATH = AASXToMetamodelConverter .TEMP_DIRECTORY ;
136+ private static final String AASX_RES_FILE_DOCBASE_PATH = VABPathTools .append (System .getProperty ("java.io.tmpdir" ), AASX_RES_FILE_CONTEXT_PATH );
137+ private static final String AASX_RES_FILE_SERVLET_MAPPING_PATTERN = "/*" ;
134138
135139 /**
136140 * Constructs an empty AAS server using the passed context
@@ -210,7 +214,8 @@ public void setRegistry(IAASRegistry registry) {
210214 * The bundles that will be loaded during startup
211215 */
212216 public void setAASBundles (Collection <AASBundle > aasBundles ) {
213- this .aasBundles = aasBundles ;
217+ this .aasBundles = new ArrayList <>();
218+ this .aasBundles .add (aasBundles );
214219 }
215220
216221 /**
@@ -220,7 +225,9 @@ public void setAASBundles(Collection<AASBundle> aasBundles) {
220225 * The bundle that will be loaded during startup
221226 */
222227 public void setAASBundle (AASBundle aasBundle ) {
223- this .aasBundles = Collections .singleton (aasBundle );
228+ this .aasBundles = new ArrayList <>();
229+ Collection <AASBundle > firstBundleSet = Collections .singleton (aasBundle );
230+ this .aasBundles .add (firstBundleSet );
224231 }
225232
226233 /**
@@ -251,11 +258,12 @@ public void startComponent() {
251258
252259 // An initial AAS has been loaded from the drive?
253260 if (aasBundles != null ) {
254- // 1. Also provide the files
255- context .addServletMapping ("/files/*" , createDefaultServlet ());
261+ createBasyxResourceDirectoryIfNotExists ();
262+
263+ addAasxFilesResourceServlet (context );
256264
257265 // 2. Fix the file paths according to the servlet configuration
258- modifyFilePaths (contextConfig .getHostname (), contextConfig .getPort (), contextConfig .getContextPath ());
266+ modifyFilePaths (contextConfig .getHostname (), contextConfig .getPort (), getRootFilePathWithContext ( contextConfig .getContextPath () ));
259267
260268 registerWhitelistedSubmodels ();
261269 }
@@ -267,6 +275,10 @@ public void startComponent() {
267275 registerPreexistingAASAndSMIfPossible ();
268276 }
269277
278+ private String getRootFilePathWithContext (String contextPath ) {
279+ return VABPathTools .append (contextPath , AASX_RES_FILE_CONTEXT_PATH );
280+ }
281+
270282 private DefaultServlet createDefaultServlet () {
271283 if (aasConfig .isAuthorizationEnabled ()) {
272284 final AuthorizedDefaultServletParams <?> params = getAuthorizedDefaultServletParams ();
@@ -543,17 +555,18 @@ private Set<AASBundle> loadBundleFromJSON(String jsonPath) throws IOException {
543555 return new JSONAASBundleFactory (jsonContent ).create ();
544556 }
545557
546- private static Set <AASBundle > loadBundleFromAASX (String aasxPath ) throws IOException , ParserConfigurationException , SAXException , InvalidFormatException , URISyntaxException {
558+ private static Set <AASBundle > loadBundleFromAASX (String aasxPath , String childFilePath ) throws IOException , ParserConfigurationException , SAXException , InvalidFormatException , URISyntaxException {
547559 logger .info ("Loading aas from aasx \" " + aasxPath + "\" " );
548560
549561 // Instantiate the aasx package manager
550- AASXToMetamodelConverter packageManager = new AASXPackageManager (aasxPath );
551-
552- // Unpack the files referenced by the aas
553- packageManager .unzipRelatedFiles ();
562+ try (AASXToMetamodelConverter packageManager = new AASXToMetamodelConverter (aasxPath )) {
563+ // Unpack the files referenced by the aas
564+ packageManager .unzipRelatedFilesToChildPath (childFilePath );
554565
555- // Retrieve the aas from the package
556- return packageManager .retrieveAASBundles ();
566+ // Retrieve the aas from the package
567+ return packageManager .retrieveAASBundles ();
568+ }
569+
557570 }
558571
559572 private void addAASServerFeaturesToContext (BaSyxContext context ) {
@@ -562,13 +575,21 @@ private void addAASServerFeaturesToContext(BaSyxContext context) {
562575 }
563576 }
564577
578+ private Collection <AASBundle > getFlatAASBundles () {
579+ Collection <AASBundle > result = new ArrayList <AASBundle >();
580+ for (Collection <AASBundle > bundle : this .aasBundles ) {
581+ result .addAll (bundle );
582+ }
583+ return result ;
584+ }
585+
565586 private VABHTTPInterface <?> createAggregatorServlet () {
566587 aggregator = createAASAggregator ();
567588 loadAASBundles ();
568589
569590 if (aasBundles != null ) {
570591 try (final var ignored = ElevatedCodeAuthentication .enterElevatedCodeAuthenticationArea ()) {
571- AASBundleHelper .integrate (aggregator , aasBundles );
592+ AASBundleHelper .integrate (aggregator , getFlatAASBundles () );
572593 }
573594 }
574595
@@ -614,30 +635,39 @@ private List<IAASServerDecorator> createAASServerDecoratorList() {
614635 }
615636
616637 private void loadAASBundles () {
617- if (aasBundles != null ) {
638+ if (! aasBundles . isEmpty () ) {
618639 return ;
619640 }
620641
621642 List <String > aasSources = aasConfig .getAASSourceAsList ();
622643 aasBundles = loadAASFromSource (aasSources );
623644 }
624645
625- private Set < AASBundle > loadAASFromSource (List <String > aasSources ) {
646+ private List < Collection < AASBundle > > loadAASFromSource (List <String > aasSources ) {
626647 if (aasSources .isEmpty ()) {
627- return Collections . emptySet ();
648+ return new ArrayList <> ();
628649 }
629650
630- Set < AASBundle > aasBundlesSet = new HashSet <>();
651+ List < Collection < AASBundle >> aasBundlesSet = new ArrayList <>();
631652
632- aasSources .stream ().map (this ::loadBundleFromFile ).forEach (aasBundlesSet ::addAll );
653+ for (int i = 0 ; i < aasSources .size (); i ++) {
654+ String aasSource = aasSources .get (i );
655+ String subFilePath = getAASXFileSubPath (i );
656+ Set <AASBundle > loadedBundles = loadBundleFromFile (aasSource , subFilePath );
657+ aasBundlesSet .add (loadedBundles );
658+ }
633659
634660 return aasBundlesSet ;
635661 }
636662
637- private Set <AASBundle > loadBundleFromFile (String aasSource ) {
663+ private String getAASXFileSubPath (int aasxIndex ) {
664+ return "aasx" + Integer .toString (aasxIndex );
665+ }
666+
667+ private Set <AASBundle > loadBundleFromFile (String aasSource , String childFilePath ) {
638668 try {
639669 if (aasSource .endsWith (".aasx" )) {
640- return loadBundleFromAASX (aasSource );
670+ return loadBundleFromAASX (aasSource , childFilePath );
641671 } else if (aasSource .endsWith (".json" )) {
642672 return loadBundleFromJSON (aasSource );
643673 } else if (aasSource .endsWith (".xml" )) {
@@ -730,7 +760,8 @@ private String getSMEndpoint(IIdentifier smId) {
730760 }
731761
732762 private String getSMIdShortFromSMId (IIdentifier smId ) {
733- for (AASBundle bundle : aasBundles ) {
763+ Collection <AASBundle > flatAasBundles = getFlatAASBundles ();
764+ for (AASBundle bundle : flatAasBundles ) {
734765 for (ISubmodel sm : bundle .getSubmodels ()) {
735766 if (smId .getId ().equals (sm .getIdentification ().getId ())) {
736767 return sm .getIdShort ();
@@ -741,7 +772,8 @@ private String getSMIdShortFromSMId(IIdentifier smId) {
741772 }
742773
743774 private String getAASIdFromSMId (IIdentifier smId ) {
744- for (AASBundle bundle : aasBundles ) {
775+ Collection <AASBundle > flatAasBundles = getFlatAASBundles ();
776+ for (AASBundle bundle : flatAasBundles ) {
745777 for (ISubmodel sm : bundle .getSubmodels ()) {
746778 if (smId .getId ().equals (sm .getIdentification ().getId ())) {
747779 return bundle .getAAS ().getIdentification ().getId ();
@@ -756,11 +788,18 @@ private String getAASIdFromSMId(IIdentifier smId) {
756788 * configuration
757789 */
758790 private void modifyFilePaths (String hostName , int port , String rootPath ) {
759- rootPath = rootPath + "/files" ;
760- for (AASBundle bundle : aasBundles ) {
791+ for (int i = 0 ; i < aasBundles .size (); i ++) {
792+ Collection <AASBundle > bundleSet = aasBundles .get (i );
793+ String bundleFileRootPath = VABPathTools .concatenatePaths (rootPath , getAASXFileSubPath (i ), "files" );
794+ modifyFilePathsInBundleSet (bundleSet , hostName , port , bundleFileRootPath );
795+ }
796+ }
797+
798+ private void modifyFilePathsInBundleSet (Collection <AASBundle > bundleSet , String hostName , int port , String bundleFileRootPath ) {
799+ for (AASBundle bundle : bundleSet ) {
761800 Set <ISubmodel > submodels = bundle .getSubmodels ();
762801 for (ISubmodel sm : submodels ) {
763- SubmodelFileEndpointLoader .setRelativeFileEndpoints (sm , hostName , port , rootPath );
802+ SubmodelFileEndpointLoader .setRelativeFileEndpoints (sm , hostName , port , bundleFileRootPath );
764803 }
765804 }
766805 }
@@ -769,10 +808,41 @@ private String getMqttAASClientId() {
769808 if (aasBundles == null || aasBundles .isEmpty ()) {
770809 return "defaultNoShellId" ;
771810 }
772- return aasBundles .stream ().findFirst ().get ().getAAS ().getIdShort ();
811+ Collection <AASBundle > firstBundleSet = aasBundles .get (0 );
812+ if (firstBundleSet == null || firstBundleSet .isEmpty ()) {
813+ return "defaultNoShellId" ;
814+ }
815+ return firstBundleSet .stream ().findFirst ().get ().getAAS ().getIdShort ();
773816 }
774817
775818 private String getMqttSubmodelClientId () {
776819 return getMqttAASClientId () + "/submodelAggregator" ;
777820 }
821+
822+ private void addAasxFilesResourceServlet (BaSyxContext context ) {
823+ HttpServlet httpServlet = createDefaultServlet ();
824+
825+ String childContextPath = VABPathTools .append (contextConfig .getContextPath (), AASX_RES_FILE_CONTEXT_PATH );
826+ Context childContext = createChildContextForAasxResourceFiles (childContextPath , AASX_RES_FILE_DOCBASE_PATH );
827+
828+ context .addChildContext (new BaSyxChildContext (childContext , httpServlet , AASX_RES_FILE_SERVLET_MAPPING_PATTERN ));
829+ }
830+
831+ private Context createChildContextForAasxResourceFiles (String childContextPath , String childDocbasePath ) {
832+ Context childContext = new StandardContext ();
833+ childContext .setPath (childContextPath );
834+ childContext .setDocBase (childDocbasePath );
835+ childContext .addLifecycleListener (new Tomcat .FixContextListener ());
836+ return childContext ;
837+ }
838+
839+ private void createBasyxResourceDirectoryIfNotExists () {
840+ File directory = new File (AASX_RES_FILE_DOCBASE_PATH );
841+
842+ if (directory .exists ())
843+ return ;
844+
845+ directory .mkdir ();
846+ }
847+
778848}
0 commit comments