blob: 52ec917e26ddfa0a3c5a8806a26c4609f62b45fb [file] [log] [blame]
package org.eclipse.basyx.components.aas;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Set;
import java.util.stream.Collectors;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.catalina.servlets.DefaultServlet;
import org.eclipse.basyx.aas.aggregator.AASAggregator;
import org.eclipse.basyx.aas.aggregator.api.IAASAggregator;
import org.eclipse.basyx.aas.aggregator.restapi.AASAggregatorProvider;
import org.eclipse.basyx.aas.metamodel.map.descriptor.AASDescriptor;
import org.eclipse.basyx.aas.registration.api.IAASRegistryService;
import org.eclipse.basyx.aas.registration.proxy.AASRegistryProxy;
import org.eclipse.basyx.components.IComponent;
import org.eclipse.basyx.components.aas.aasx.AASXPackageManager;
import org.eclipse.basyx.components.aas.aasx.SubmodelFileEndpointLoader;
import org.eclipse.basyx.components.aas.configuration.AASServerBackend;
import org.eclipse.basyx.components.aas.configuration.BaSyxAASServerConfiguration;
import org.eclipse.basyx.components.aas.mongodb.MongoDBAASAggregator;
import org.eclipse.basyx.components.aas.servlet.AASAggregatorServlet;
import org.eclipse.basyx.components.configuration.BaSyxConfiguration;
import org.eclipse.basyx.components.configuration.BaSyxContextConfiguration;
import org.eclipse.basyx.components.configuration.BaSyxMongoDBConfiguration;
import org.eclipse.basyx.components.xml.XMLAASBundleFactory;
import org.eclipse.basyx.submodel.metamodel.api.ISubModel;
import org.eclipse.basyx.support.bundle.AASBundle;
import org.eclipse.basyx.support.bundle.AASBundleDescriptorFactory;
import org.eclipse.basyx.support.bundle.AASBundleIntegrator;
import org.eclipse.basyx.vab.protocol.http.server.AASHTTPServer;
import org.eclipse.basyx.vab.protocol.http.server.BaSyxContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
/**
* Component providing an empty AAS server that is able to receive AAS/SMs from
* remote. It uses the Aggregator API, i.e. AAS should be pushed to
* ${URL}/shells
*
* @author schnicke, espen
*
*/
public class AASServerComponent implements IComponent {
private static Logger logger = LoggerFactory.getLogger(AASServerComponent.class);
// The server with the servlet that will be created
private AASHTTPServer server;
private IAASRegistryService registry;
// Configurations
private BaSyxContextConfiguration contextConfig;
private BaSyxAASServerConfiguration aasConfig;
private BaSyxMongoDBConfiguration mongoDBConfig;
// Initial AASBundle
protected Collection<AASBundle> aasBundles;
/**
* Constructs an empty AAS server using the passed context
*/
public AASServerComponent(BaSyxContextConfiguration contextConfig) {
this.contextConfig = contextConfig;
this.aasConfig = new BaSyxAASServerConfiguration();
}
/**
* Constructs an empty AAS server using the passed configuration
*/
public AASServerComponent(BaSyxContextConfiguration contextConfig, BaSyxAASServerConfiguration aasConfig) {
this.contextConfig = contextConfig;
this.aasConfig = aasConfig;
}
/**
* Constructs an empty AAS server using the passed configuration
*/
public AASServerComponent(BaSyxContextConfiguration contextConfig, BaSyxAASServerConfiguration aasConfig,
BaSyxMongoDBConfiguration mongoDBConfig) {
this.contextConfig = contextConfig;
this.aasConfig = aasConfig;
this.aasConfig.setAASBackend(AASServerBackend.MONGODB);
this.mongoDBConfig = mongoDBConfig;
}
public void setRegistry(IAASRegistryService registry) {
this.registry = registry;
}
/**
* Starts the AASX component at http://${hostName}:${port}/${path}
*/
@Override
public void startComponent() {
logger.info("Create the server...");
// Load the aggregator servlet
createRegistryFromUrl();
AASAggregatorServlet aggregatorServlet = loadAggregatorServlet();
// Init HTTP context and add an XMLAASServlet according to the configuration
BaSyxContext context = contextConfig.createBaSyxContext();
context.addServletMapping("/*", aggregatorServlet);
// An initial AAS has been loaded from the drive?
if (aasBundles != null) {
// 1. Also provide the files
context.addServletMapping("/aasx/*", new DefaultServlet());
// 2. Fix the file paths according to the servlet configuration
modifyFilePaths(contextConfig.getHostname(), contextConfig.getPort(), contextConfig.getContextPath());
// 3. Register the initial AAS
registerAAS();
}
logger.info("Start the server");
server = new AASHTTPServer(context);
server.start();
}
/**
* Retrieves the URL on which the component is providing its HTTP server
*
* @return
*/
public String getURL() {
return contextConfig.getUrl();
}
@Override
public void stopComponent() {
server.shutdown();
}
private void loadBundleFromXML(String xmlPath) throws IOException, ParserConfigurationException, SAXException {
logger.info("Loading aas from xml \"" + xmlPath + "\"");
String xmlContent = BaSyxConfiguration.getResourceString(xmlPath);
this.aasBundles = new XMLAASBundleFactory(xmlContent).create();
}
private void loadBundleFromAASX(String aasxPath)
throws IOException, ParserConfigurationException, SAXException, URISyntaxException {
logger.info("Loading aas from aasx \"" + aasxPath + "\"");
// Instantiate the aasx package manager
AASXPackageManager packageManager = new AASXPackageManager(aasxPath);
// Unpack the files referenced by the aas
packageManager.unzipRelatedFiles(aasxPath);
// Retrieve the aas from the package
this.aasBundles = packageManager.retrieveAASBundles();
}
private AASAggregatorServlet loadAggregatorServlet() {
// Load the initial AAS bundles from given source
loadAASFromSource(aasConfig.getAASSource());
// Load the aggregator
IAASAggregator aggregator = loadAASAggregator();
// Integrate the loaded bundles into the aggregator
if (aasBundles != null) {
AASBundleIntegrator.integrate(aggregator, aasBundles);
}
// Return the servlet for the resulting aggregator
return new AASAggregatorServlet(aggregator);
}
private void loadAASFromSource(String aasSource) {
if (aasSource.isEmpty()) {
return;
}
try {
if (aasSource.endsWith(".aasx")) {
loadBundleFromAASX(aasSource);
} else if (aasSource.endsWith(".xml")) {
loadBundleFromXML(aasSource);
}
} catch (IOException | ParserConfigurationException | SAXException | URISyntaxException e) {
logger.error("Could not load initial AAS from source " + aasSource);
e.printStackTrace();
}
}
/**
* Only creates the registry, if it hasn't been set explicitly before
*/
private void createRegistryFromUrl() {
if (this.registry != null) {
// Do not overwrite an explicitly set registry
return;
}
// Load registry url from config
String registryUrl = this.aasConfig.getRegistry();
if (registryUrl != null && !registryUrl.isEmpty()) {
registry = new AASRegistryProxy(registryUrl);
logger.info("Registry loaded at \"" + registryUrl + "\"");
}
}
private void registerAAS() {
if (registry != null) {
Set<AASDescriptor> descriptors = retrieveDescriptors(contextConfig.getUrl());
descriptors.stream().forEach(registry::register);
} else {
logger.info("No registry specified, skipped registration");
}
}
/**
* Returns the set of AAS descriptors for the AAS contained in the AASX
*
* @param hostBasePath
* the path to the server; helper method for e.g. virtualization
* environments
* @return
*/
private Set<AASDescriptor> retrieveDescriptors(String hostBasePath) {
// Base path + aggregator accessor
final String fullBasePath = hostBasePath + "/" + AASAggregatorProvider.PREFIX;
return aasBundles.stream().map(b -> AASBundleDescriptorFactory.createAASDescriptor(b, fullBasePath))
.collect(Collectors.toSet());
}
/**
* Fixes the File submodel element value paths according to the given endpoint configuration
*/
private void modifyFilePaths(String hostName, int port, String rootPath) {
for (AASBundle bundle : aasBundles) {
Set<ISubModel> submodels = bundle.getSubmodels();
for (ISubModel sm : submodels) {
SubmodelFileEndpointLoader.setRelativeFileEndpoints(sm, hostName, port, rootPath);
}
}
}
/**
* Loads a aas aggregator servlet with a backend according to the configuration
*
* @return
*/
private IAASAggregator loadAASAggregator() {
// Get aggregator according to backend config
AASServerBackend backendType = aasConfig.getAASBackend();
IAASAggregator aggregator = null;
if ( backendType == AASServerBackend.INMEMORY ) {
logger.info("Using InMemory backend");
aggregator = new AASAggregator(registry);
} else if ( backendType == AASServerBackend.MONGODB ) {
logger.info("Using MongoDB backend");
aggregator = loadMongoDBAggregator();
}
return aggregator;
}
private IAASAggregator loadMongoDBAggregator() {
BaSyxMongoDBConfiguration config;
if (this.mongoDBConfig == null) {
config = new BaSyxMongoDBConfiguration();
config.loadFromDefaultSource();
} else {
config = this.mongoDBConfig;
}
MongoDBAASAggregator aggregator = new MongoDBAASAggregator(config);
aggregator.setRegistry(registry);
return aggregator;
}
}