blob: c46af4e7a369524cc956d4b3fccf17f27ad5a854 [file] [log] [blame]
package org.eclipse.basyx.components.servlets;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.basyx.aas.backend.modelprovider.http.BasysHTTPServelet;
import org.eclipse.basyx.components.directory.AASDirectoryEntry;
import org.eclipse.basyx.components.directory.exception.AASDirectoryProviderException;
/**
* Static configuration file based directory provider
*
* This directory provider provides a static directory. It therefore only supports get() operations.
* Modification of the directory via PUT/POST/PATCH/DELETE operations is not supported.
*
* @author kuhn
*
*/
public class StaticCFGDirectoryServlet extends BasysHTTPServelet {
/**
* Version information to identify the version of serialized instances
*/
private static final long serialVersionUID = 1L;
/**
* Configuration properties (raw input from file)
*/
protected Properties properties = null;
/**
* Asset administration shells by ID
*/
protected Map<String, AASDirectoryEntry> aasByID = new HashMap<>();
/**
* Asset administration shells by tag
*/
protected Map<String, Collection<AASDirectoryEntry>> aasByTag = new HashMap<>();
/**
* Uplink server
*/
protected String uplink = null;
/**
* Downlink servers
*/
protected Map<String, String> downlinks = new HashMap<>();
/**
* Constructor
*/
public StaticCFGDirectoryServlet() {
// Invoke base constructor
super();
}
/**
* Load a property
*/
protected String extractProperty(Properties prop, String key) {
// Check if properties contain value
if (!prop.containsKey(key)) return null;
// Extract and remove value
String value = (String) prop.get(key);
prop.remove(key);
// Return value
return value;
}
/**
* Extract property keys with prefix and suffix. Prefix and suffix is removed from key.
*/
protected Collection<String> getProperties(Properties prop, String prefix, String suffix) {
// Store result
HashSet<String> result = new HashSet<>();
// Iterate keys
for (String key: prop.stringPropertyNames()) {
if (key.startsWith(prefix) && key.endsWith(suffix)) result.add(key.substring(prefix.length(), key.length()-suffix.length()));
}
// Return result
return result;
}
/**
* Extract downlink servers
*/
protected Map<String, String> extractDownlinks(Properties prop) {
// Return value
Map<String, String> result = new HashMap<>();
// Downlink server names
Collection<String> downlinkServerNames = getProperties(prop, "cfg.downlink.", ".pattern");
// Remove downlink pattern and server URL
for (String name: downlinkServerNames) {
// Get downlink pattern and server URL
result.put(prop.getProperty("cfg.downlink."+name+".pattern"), prop.getProperty("cfg.downlink."+name+".directory"));
// Remove pattern and directory properties
prop.remove("cfg.downlink."+name+".pattern");
prop.remove("cfg.downlink."+name+".directory");
}
// Return downlink server mappings
return result;
}
/**
* Extract Asset Administration Shell definitions
*/
protected Map<String, AASDirectoryEntry> extractAAS(Properties prop) {
// Return value
Map<String, AASDirectoryEntry> result = new HashMap<>();
// Get AAS IDs
Collection<String> aasIDs = getProperties(prop, "", ".id");
// Create AAS directory entries from properties
for (String aasID : aasIDs) {
// Create AAS directory entry
AASDirectoryEntry entry = new AASDirectoryEntry(prop.getProperty(aasID+".id"), prop.getProperty(aasID+".aas"), prop.getProperty(aasID+".type"), prop.getProperty(aasID+".tags"));
// Add AAS directory entry
result.put(prop.getProperty(aasID+".id"), entry);
}
// Return ID to AAS mappings
return result;
}
/**
* Map AAS tags to AAS
*/
protected Map<String, Collection<AASDirectoryEntry>> mapAASToTags(Map<String, AASDirectoryEntry> aasByID) {
// Return value
Map<String, Collection<AASDirectoryEntry>> result = new HashMap<>();
// Iterate AAS directory entries
for (AASDirectoryEntry dirEntry: aasByID.values()) {
// Process tags
for (String tag: dirEntry.getAASTags()) {
// Create tag if necessary
if (!result.containsKey(tag)) {result.put(tag, new HashSet<AASDirectoryEntry>());}
// Add AAS to tag
result.get(tag).add(dirEntry);
}
}
// Return HashTag to AAS mappings
return result;
}
/**
* Load properties from file
*/
protected void loadProperties(String cfgFilePath) {
// Read property file
try {
// Open property file
InputStream input = getServletContext().getResourceAsStream(cfgFilePath);
// Instantiate property structure
properties = new Properties();
properties.load(input);
System.out.println("properties:"+properties);
System.out.println("properties (keys):"+properties.keySet());
System.out.println("properties (cfg.downlink.is.pattern):"+properties.get("cfg.downlink.is.pattern"));
// Process properties
// - Uplink server
uplink = extractProperty(properties, "cfg.uplink");
// - Downlink servers
downlinks = extractDownlinks(properties);
// - AAS by ID
aasByID = extractAAS(properties);
// - AAS by tag
aasByTag = mapAASToTags(aasByID);
System.out.println("Downlink:"+downlinks);
System.out.println("properties:"+properties);
System.out.println("aasbyID:"+aasByID);
} catch (IOException e) {
// Output exception
e.printStackTrace();
}
}
/**
* Initialize servlet
*
* @throws ServletException
*/
public void init() throws ServletException {
// Call base implementation
super.init();
// Read configuration values
String configFilePath = (String) getInitParameter("config");
// - Read property file
loadProperties(configFilePath);
}
/**
* Send a response
*/
protected void sendResponse(String value, PrintWriter outputStream) {
// Null pointer check
if (value == null) return;
// Output result
outputStream.write(value);
outputStream.flush();
}
/**
* Get AAS content from AASDirectoryEntry
*/
protected String getAASContent(AASDirectoryEntry directoryEntry) {
// Process directory entry
switch (directoryEntry.getAASContentType()) {
// Local content type
case AASDirectoryEntry.AAS_CONTENTTYPE_LOCAL:
return directoryEntry.getAASContent();
// Remote content type
case AASDirectoryEntry.AAS_CONTENTTYPE_REMOTE:
throw new AASDirectoryProviderException("Unsupported AAS content type");
// Proxy content type - content is ID of AAS that contains the information
case AASDirectoryEntry.AAS_CONTENTTYPE_PROXY:
return getAASContentByID(directoryEntry.getAASContent());
// Unknown content type
default:
throw new AASDirectoryProviderException("Unknown AAS content type");
}
}
/**
* Get requested tags as collection
*/
protected Collection<String> getTagsAsCollection(String tags) {
// Collection stores AAS tags
Collection<String> alltags = new HashSet<>();
// Catch null pointer exceptions
try {
// Get AAS tags
String[] splitTags = tags.split(",");
// Only add non-empty tags
for (String tag: splitTags) if (tag.trim().length() > 0) alltags.add(tag.trim());
} catch (NullPointerException e) {}
// Return all tags
return alltags;
}
/**
* Get a specific AAS content with ID
*/
protected String getAASContentByID(String aasID) {
// Extract requested AAS ID
AASDirectoryEntry aas = aasByID.get(aasID);
// Null pointer check
if (aas == null) return null;
// Return result
return getAASContent(aas);
}
/**
* Implement "Get" operation
*
* Process HTTP get request - get sub model property value
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Process request depending on the path and on parameter
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
String path = URLDecoder.decode(uri.substring(contextPath.length()+1).substring(req.getServletPath().length()), "UTF-8"); // plus 1 for "/"
// Extract action parameter
Collection<String> alltags = getTagsAsCollection(req.getParameter("tags"));
// Setup HTML response header
resp.setContentType("application/json");
resp.setCharacterEncoding("UTF-8");
// Process get request
// - Get all (local) AAS
if (path.equals("api/v1/registry")) {
// Extract AAS directory entries
Collection<AASDirectoryEntry> entries = null;
// Check if tags are to be processed
if (alltags.isEmpty()) {
// Get all tags
entries = aasByID.values();
} else {
// HashSet that holds all elements
Set<AASDirectoryEntry> taggedEntries = new HashSet<>();
// Get tagged elements that have all requested tags
// - Get first requested tag
taggedEntries.addAll(aasByTag.get(alltags.iterator().next()));
// - Remove all directory entries that do not have all tags
for (String tag: alltags) taggedEntries.retainAll(aasByTag.get(tag));
// - Place remaining elements into entries collection
entries = taggedEntries;
}
// Build response string
StringBuilder response = new StringBuilder();
for (AASDirectoryEntry entry: entries) response.append(getAASContent(entry));
// Write result
sendResponse(response.toString(), resp.getWriter());
// End processing
return;
}
// Get a specific AAS
if (path.startsWith("api/v1/registry/")) {
System.out.println("Getting:"+path);
// Get requested AAS with ID
String aas = getAASContentByID(path.substring(new String("api/v1/registry/").length()));
// Write result
sendResponse(aas, resp.getWriter());
// End processing
return;
}
}
/**
* Implement "Put" operation
*/
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Indicate an unsupported operation
throw new AASDirectoryProviderException("Unsupported operation");
}
/**
* <pre>
* Handle HTTP POST operation. Creates a new Property, Operation, Event, Submodel or AAS or invokes an operation.
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Indicate an unsupported operation
throw new AASDirectoryProviderException("Unsupported operation");
}
/**
* Handle a HTTP PATCH operation. Updates a map or collection respective to action string.
*
*/
@Override
protected void doPatch(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Indicate an unsupported operation
throw new AASDirectoryProviderException("Unsupported operation");
}
/**
* Implement "Delete" operation. Deletes any resource under the given path.
*/
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Indicate an unsupported operation
throw new AASDirectoryProviderException("Unsupported operation");
}
}