| package org.eclipse.basyx.aas.registration.restapi; |
| |
| import java.io.UnsupportedEncodingException; |
| import java.net.URLDecoder; |
| import java.util.Collection; |
| import java.util.Map; |
| |
| import org.eclipse.basyx.aas.metamodel.map.descriptor.AASDescriptor; |
| 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.IAASRegistryService; |
| import org.eclipse.basyx.aas.registration.memory.InMemoryRegistry; |
| import org.eclipse.basyx.submodel.metamodel.api.identifier.IIdentifier; |
| import org.eclipse.basyx.submodel.metamodel.map.modeltype.ModelType; |
| import org.eclipse.basyx.vab.exception.provider.MalformedRequestException; |
| import org.eclipse.basyx.vab.exception.provider.ProviderException; |
| import org.eclipse.basyx.vab.exception.provider.ResourceAlreadyExistsException; |
| import org.eclipse.basyx.vab.exception.provider.ResourceNotFoundException; |
| import org.eclipse.basyx.vab.modelprovider.VABPathTools; |
| import org.eclipse.basyx.vab.modelprovider.api.IModelProvider; |
| |
| /** |
| * Connects an arbitrary IRegistryService implementation to the VAB |
| * |
| * @author schnicke, conradi |
| * |
| */ |
| public class DirectoryModelProvider implements IModelProvider { |
| |
| IAASRegistryService registry; |
| |
| private static final String PREFIX = "api/v1/registry"; |
| public static final String SUBMODELS = "submodels"; |
| |
| public DirectoryModelProvider(IAASRegistryService registry) { |
| this.registry = registry; |
| } |
| |
| public DirectoryModelProvider() { |
| this(new InMemoryRegistry()); |
| } |
| |
| /** |
| * Check for correctness of path and returns a stripped path (i.e. no leading |
| * prefix) |
| * |
| * @param path |
| * @return |
| * @throws MalformedRequestException if path does not start with PERFIX "api/v1/registry" |
| */ |
| private String stripPrefix(String path) throws MalformedRequestException { |
| path = VABPathTools.stripSlashes(path); |
| if (!path.startsWith(PREFIX)) { |
| throw new MalformedRequestException("Path " + path + " not recognized as registry path. Has to start with " + PREFIX); |
| } |
| path = path.replace(PREFIX, ""); |
| path = VABPathTools.stripSlashes(path); |
| return path; |
| } |
| |
| /** |
| * Splits a path and checks, that first element is not "submodels" and that the second one, if exists, is "submodels" |
| * |
| * @param path the path to be splitted |
| * @return Array of path elements |
| * @throws MalformedRequestException if path is not valid |
| */ |
| private String[] splitPath(String path) throws MalformedRequestException { |
| |
| if(path.isEmpty()) { |
| return new String[0]; |
| } |
| |
| String[] splitted = path.split("/"); |
| |
| //Assumes "submodels" is not a valid AASId |
| if(splitted[0].equals(SUBMODELS)) { |
| throw new MalformedRequestException("Path must not start with " + SUBMODELS); |
| } |
| |
| //If path contains more than one element, the second one has to be "submodels" |
| if(splitted.length > 1 && !splitted[1].equals(SUBMODELS)) { |
| throw new MalformedRequestException("Second path element must be (if present): " + SUBMODELS); |
| } |
| |
| return splitted; |
| } |
| |
| private String[] preparePath(String path) throws MalformedRequestException { |
| path = stripPrefix(path); |
| |
| String[] splitted = splitPath(path); |
| |
| try { |
| for (int i = 0; i < splitted.length; i++) { |
| splitted[i] = URLDecoder.decode(splitted[i], "UTF-8"); |
| } |
| return splitted; |
| } catch (UnsupportedEncodingException e) { |
| //Malformed request because of unsupported encoding |
| throw new MalformedRequestException("Path has to be encoded as UTF-8 string."); |
| } |
| } |
| |
| /** |
| * Checks if a given Object is a Map and checks if it has the correct modelType |
| * |
| * @param expectedModelType the modelType the Object is expected to have |
| * @param value the Object to be checked and casted |
| * @return the object casted to a Map |
| * @throws MalformedRequestException |
| */ |
| @SuppressWarnings("unchecked") |
| private Map<String, Object> checkModelType(String expectedModelType, Object value) throws MalformedRequestException { |
| //check if the given value is a Map |
| if(!(value instanceof Map)) { |
| throw new MalformedRequestException("Given newValue is not a Map"); |
| } |
| |
| Map<String, Object> map = (Map<String, Object>) value; |
| |
| //check if the given Map contains an AAS |
| String type = ModelType.createAsFacade(map).getName(); |
| |
| //have to accept Objects without modeltype information, |
| //as modeltype is not part of the public metamodel |
| if(!expectedModelType.equals(type) && type != null) { |
| throw new MalformedRequestException("Given newValue map has not the correct ModelType"); |
| } |
| |
| return map; |
| } |
| |
| /** |
| * Makes sure, that given Object is an AASDescriptor by checking its ModelType<br /> |
| * Creates a new AASDescriptor with the content of the given Map |
| * |
| * @param value the AAS Map object |
| * @return an AAS |
| * @throws MalformedRequestException |
| */ |
| private AASDescriptor createAASDescriptorFromMap(Object value) throws MalformedRequestException { |
| Map<String, Object> map = checkModelType(AASDescriptor.MODELTYPE, value); |
| AASDescriptor aasDescriptor = new AASDescriptor(map); |
| return aasDescriptor; |
| } |
| |
| /** |
| * Makes sure, that given Object is an SubmodelDescriptor by checking its ModelType<br /> |
| * Creates a new SubmodelDescriptor with the content of the given Map |
| * |
| * @param value the AAS Map object |
| * @return an AAS |
| * @throws MalformedRequestException |
| */ |
| private SubmodelDescriptor createSMDescriptorFromMap(Object value) throws MalformedRequestException { |
| Map<String, Object> map = checkModelType(SubmodelDescriptor.MODELTYPE, value); |
| SubmodelDescriptor smDescriptor = new SubmodelDescriptor(map); |
| return smDescriptor; |
| } |
| |
| @Override |
| public Object getModelPropertyValue(String path) throws ProviderException { |
| String[] splitted = preparePath(path); |
| |
| //Path is empty, request for all AASDescriptors |
| if (splitted.length == 0) { |
| return registry.lookupAll(); |
| } else { |
| |
| //Given path consists only of an AAS Id |
| if(splitted.length == 1) { |
| AASDescriptor descriptor = registry.lookupAAS(new ModelUrn(splitted[0])); |
| |
| //Throw an Exception if the requested AAS does not exist |
| if(descriptor == null) { |
| throw new ResourceNotFoundException("Specified AASid '" + splitted[0] + "' does not exist."); |
| } |
| return descriptor; |
| |
| //Given path consists of an AAS Id and "/submodels" |
| //Request for all SubmodelDescriptors of given AAS |
| } else if(splitted.length == 2) { |
| return getSmDescriptorsFromAAS(new ModelUrn(splitted[0])); |
| |
| //Given path consists of an AAS Id and "/submodels/" and a SubmodelId |
| //Request for the SubmodelDescriptor of given AAS with given id |
| } else if(splitted.length == 3) { |
| SubmodelDescriptor smDescriptor = getSmDescriptorFromAAS(new ModelUrn(splitted[0]), splitted[2]); |
| if(smDescriptor == null) { |
| throw new ResourceNotFoundException("Specified SubmodelId '" + splitted[2] + "' does not exist in AAS '" + splitted[0] + "'."); |
| } |
| return smDescriptor; |
| } |
| |
| //path has more than three elements and is therefore invalid |
| throw new MalformedRequestException("Given path '" + path + "' contains more than three path elements and is therefore invalid."); |
| } |
| } |
| |
| @Override |
| public void setModelPropertyValue(String path, Object newValue) throws ProviderException { |
| String[] splitted = preparePath(path); |
| |
| if (splitted.length > 0) { // Overwriting existing entry |
| //if path contains more or less than an aasID after the prefix |
| if(splitted.length != 1) { |
| throw new MalformedRequestException("Path '" + path + "' is invalid for updating an aas."); |
| } |
| |
| // Decode encoded path |
| ModelUrn identifier = new ModelUrn(path); |
| |
| //aas to be updated does not exist |
| if(registry.lookupAAS(identifier) == null) { |
| throw new ResourceNotFoundException("AAS '" + path + "' to be updated does not exist. Try create instead."); |
| } |
| |
| //delete old value and create the new one |
| registry.delete(identifier); |
| registry.register(createAASDescriptorFromMap(newValue)); |
| } else { |
| throw new MalformedRequestException("Set with empty path is not supported by registry"); |
| } |
| } |
| |
| @Override |
| public void createValue(String path, Object newEntity) throws ProviderException { |
| String[] splitted = preparePath(path); |
| |
| // Creating new entry |
| if (splitted.length == 0) { |
| |
| AASDescriptor aas = createAASDescriptorFromMap(newEntity); |
| |
| //aas to be created already exists |
| if(registry.lookupAAS(aas.getIdentifier()) != null) { |
| throw new ResourceAlreadyExistsException("AAS with Id '" + |
| aas.getIdentifier().getId() + "' already exists. Try update instead."); |
| } |
| |
| registry.register(aas); |
| |
| // Creating new submodel entry for existing aas |
| } else if (splitted.length == 2) { |
| ModelUrn aasId = retrieveModelURN(splitted[0]); |
| |
| SubmodelDescriptor smDescriptor = createSMDescriptorFromMap(newEntity); |
| |
| //a submodel with this Id already exists in given aas |
| //getSmDescriptorFromAAS also checks if aas exists |
| if(getSmDescriptorFromAAS(aasId, smDescriptor.getIdShort()) != null) { |
| throw new ResourceAlreadyExistsException("A Submodel with id '" + smDescriptor.getIdShort() + |
| "' already exists in aas '" + splitted[0] + "'. Try update instead."); |
| } |
| |
| registry.register(aasId, smDescriptor); |
| } else { |
| throw new MalformedRequestException("Create was called with an unsupported path: " + path); |
| } |
| } |
| |
| private ModelUrn retrieveModelURN(String str) { |
| return new ModelUrn(str); |
| } |
| |
| @Override |
| public void deleteValue(String path) throws ProviderException { |
| String[] splitted = preparePath(path); |
| |
| if (splitted.length == 1) { //delete an aas |
| |
| ModelUrn aasId = new ModelUrn(splitted[0]); |
| |
| //aas to be deleted does not exist |
| if(registry.lookupAAS(aasId) == null) { |
| throw new ResourceNotFoundException("AAS '" + splitted[0] + "' to be deleted does not exist."); |
| } |
| |
| registry.delete(aasId); |
| |
| } else if(splitted.length == 3) { //delete a submodel |
| ModelUrn aasId = new ModelUrn(splitted[0]); |
| String smId = splitted[2]; |
| // a submodel with this Id does not exist in given aas |
| // getSmDescriptorFromAAS also checks if aas exists |
| if (getSmDescriptorFromAAS(aasId, smId) == null) { |
| throw new ResourceNotFoundException("A Submodel with id '" + smId + "' does not exist in aas '" + splitted[0] + "'."); |
| } |
| |
| registry.delete(aasId, smId); |
| } else { |
| throw new MalformedRequestException("Delete with empty path is not supported by registry"); |
| } |
| } |
| |
| @Override |
| public void deleteValue(String path, Object obj) throws Exception { |
| throw new MalformedRequestException("DeleteValue with parameter not supported by registry"); |
| } |
| |
| @Override |
| public Object invokeOperation(String path, Object... parameter) throws Exception { |
| throw new MalformedRequestException("Invoke not supported by registry"); |
| } |
| |
| /** |
| * Gets all SubmodelDescriptor objects form an aas. |
| * Throws RuntimeException if aas does not exist. |
| * |
| * @param id id of the aas |
| * @return Set of contained SubmodelDescriptor objects |
| * @throws ResourceNotFoundException if the AAS does not exist |
| */ |
| private Collection<SubmodelDescriptor> getSmDescriptorsFromAAS(IIdentifier id) throws ResourceNotFoundException { |
| AASDescriptor aasDescriptor = registry.lookupAAS(id); |
| if(aasDescriptor == null) { |
| throw new ResourceNotFoundException("Specified AASid '" + id.getId() + "' does not exist."); |
| } |
| return aasDescriptor.getSubModelDescriptors(); |
| } |
| |
| /** |
| * Gets a specific SubmodelDescriptor form an aas. |
| * Throws RuntimeException if aas does not exist. |
| * |
| * @param aasId id of the aas |
| * @param smId id of the submodel |
| * @return the SubmodelDescriptor with the given id |
| * @throws ResourceNotFoundException if aasId does not exist |
| */ |
| private SubmodelDescriptor getSmDescriptorFromAAS(IIdentifier aasId, String smId) |
| throws ResourceNotFoundException { |
| AASDescriptor aasDescriptor = registry.lookupAAS(aasId); |
| if(aasDescriptor == null) { |
| throw new ResourceNotFoundException("Specified AASid '" + aasId.getId() + "' does not exist."); |
| } |
| |
| return aasDescriptor.getSubmodelDescriptorFromIdShort(smId); |
| } |
| |
| } |