| /******************************************************************************* |
| * Copyright (C) 2021 the Eclipse BaSyx Authors |
| * |
| * This program and the accompanying materials are made |
| * available under the terms of the Eclipse Public License 2.0 |
| * which is available at https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| ******************************************************************************/ |
| 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.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 AASRegistryModelProvider implements IModelProvider { |
| |
| IAASRegistryService registry; |
| |
| public static final String PREFIX = "api/v1/registry"; |
| public static final String SUBMODELS = "submodels"; |
| |
| public AASRegistryModelProvider(IAASRegistryService registry) { |
| this.registry = registry; |
| } |
| |
| public AASRegistryModelProvider() { |
| 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.replaceFirst(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 getValue(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 setValue(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 |
| |
| // Decode encoded path |
| ModelUrn identifier = new ModelUrn(splitted[0]); |
| |
| if (splitted.length == 1) { |
| // Typically, VAB SET should not create new entries. Nevertheless, the registry |
| // API is defined to do it. |
| AASDescriptor desc = createAASDescriptorFromMap(newValue); |
| |
| // Ensure the passed identifier is equals the |
| String descId = desc.getIdentifier().getId(); |
| String urlId = splitted[0]; |
| if (descId.equals(urlId)) { |
| registry.register(desc); |
| } else { |
| throw new MalformedRequestException("The Identifier " + descId + " in the descriptor does not match the URL with id " + urlId); |
| } |
| } else if (splitted.length == 3) { |
| SubmodelDescriptor smDesc = createSMDescriptorFromMap(newValue); |
| registry.register(identifier, smDesc); |
| } else { |
| throw new MalformedRequestException("Unknown path " + path); |
| } |
| } else { |
| throw new MalformedRequestException("Set with empty path is not supported by registry"); |
| } |
| } |
| |
| @Override |
| public void createValue(String path, Object newEntity) throws ProviderException { |
| throw new MalformedRequestException("Create (POST) on a registry is not supported. Please, use put"); |
| } |
| |
| @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 |
| SubmodelDescriptor smDesc = getSmDescriptorFromAAS(aasId, smId); |
| if (smDesc == null) { |
| throw new ResourceNotFoundException("A Submodel with id '" + smId + "' does not exist in aas '" + splitted[0] + "'."); |
| } |
| |
| registry.delete(aasId, smDesc.getIdentifier()); |
| } else { |
| throw new MalformedRequestException("Delete with empty path is not supported by registry"); |
| } |
| } |
| |
| @Override |
| public void deleteValue(String path, Object obj) throws ProviderException { |
| throw new MalformedRequestException("DeleteValue with parameter not supported by registry"); |
| } |
| |
| @Override |
| public Object invokeOperation(String path, Object... parameter) throws ProviderException { |
| 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."); |
| } |
| |
| SubmodelDescriptor smDescriptor = aasDescriptor.getSubmodelDescriptorFromIdentifierId(smId); |
| if (smDescriptor == null) { |
| throw new ResourceNotFoundException("Specified SMId '" + smId + "' for AAS " + aasId.getId() + " does not exist."); |
| } |
| return smDescriptor; |
| } |
| |
| } |