blob: 2b7763d99fb610fe31945e5d84bf1df4eb26b4c9 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.restconf.utils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.io.IOUtils;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onosproject.restconf.api.RestconfException;
import org.onosproject.restconf.api.RestconfRpcOutput;
import org.onosproject.restconf.utils.exceptions.RestconfUtilsException;
import org.onosproject.yang.model.DataNode;
import org.onosproject.yang.model.DefaultResourceData;
import org.onosproject.yang.model.ResourceData;
import org.onosproject.yang.model.ResourceId;
import org.onosproject.yang.model.RpcOutput;
import org.onosproject.yang.runtime.CompositeData;
import org.onosproject.yang.runtime.CompositeStream;
import org.onosproject.yang.runtime.DefaultCompositeData;
import org.onosproject.yang.runtime.DefaultCompositeStream;
import org.onosproject.yang.runtime.DefaultRuntimeContext;
import org.onosproject.yang.runtime.RuntimeContext;
import org.onosproject.yang.runtime.YangRuntimeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
import static javax.ws.rs.core.Response.Status.EXPECTATION_FAILED;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static javax.ws.rs.core.Response.Status.NO_CONTENT;
import static javax.ws.rs.core.Response.Status.OK;
import static javax.ws.rs.core.Response.Status.REQUEST_TIMEOUT;
/**
* Utilities used by the RESTCONF app.
*/
public final class RestconfUtils {
/**
* No instantiation.
*/
private RestconfUtils() {
}
/**
* Data format required by YangRuntime Service.
*/
private static final String JSON_FORMAT = "JSON";
private static final String SLASH = "/";
private static final YangRuntimeService YANG_RUNTIME =
DefaultServiceDirectory.getService(YangRuntimeService.class);
private static final Logger log = LoggerFactory.getLogger(RestconfUtils.class);
/**
* Converts an input stream to JSON objectNode.
*
* @param inputStream the InputStream from Resource Data
* @return JSON representation of the data resource
*/
public static ObjectNode convertInputStreamToObjectNode(InputStream inputStream) {
ObjectNode rootNode;
ObjectMapper mapper = new ObjectMapper();
try {
rootNode = (ObjectNode) mapper.readTree(inputStream);
} catch (IOException e) {
throw new RestconfUtilsException("ERROR: InputStream failed to parse");
}
return rootNode;
}
/**
* Convert ObjectNode to InputStream.
*
* @param rootNode JSON representation of the data resource
* @return the InputStream from Resource Data
*/
public static InputStream convertObjectNodeToInputStream(ObjectNode rootNode) {
String json = rootNode.toString();
InputStream inputStream;
try {
inputStream = IOUtils.toInputStream(json);
} catch (Exception e) {
throw new RestconfUtilsException("ERROR: Json Node failed to parse");
}
return inputStream;
}
/**
* Convert URI to ResourceId.
*
* @param uri URI of the data resource
* @return resource identifier
*/
public static ResourceId convertUriToRid(URI uri) {
ResourceData resourceData = convertJsonToDataNode(uri, null);
return resourceData.resourceId();
}
/**
* Convert URI and ObjectNode to ResourceData.
*
* @param uri URI of the data resource
* @param rootNode JSON representation of the data resource
* @return represents type of node in data store
*/
public static ResourceData convertJsonToDataNode(URI uri,
ObjectNode rootNode) {
RuntimeContext.Builder runtimeContextBuilder = new DefaultRuntimeContext.Builder();
runtimeContextBuilder.setDataFormat(JSON_FORMAT);
RuntimeContext context = runtimeContextBuilder.build();
ResourceData resourceData = null;
InputStream jsonData = null;
try {
if (rootNode != null) {
jsonData = convertObjectNodeToInputStream(rootNode);
}
String uriString = getRawUriPath(uri);
CompositeStream compositeStream = new DefaultCompositeStream(uriString, jsonData);
// CompositeStream --- YangRuntimeService ---> CompositeData.
CompositeData compositeData = YANG_RUNTIME.decode(compositeStream, context);
resourceData = compositeData.resourceData();
} catch (Exception ex) {
log.error("convertJsonToDataNode failure: {}", ex.getMessage());
log.debug("convertJsonToDataNode failure", ex);
}
if (resourceData == null) {
throw new RestconfException("ERROR: JSON cannot be converted to DataNode",
INTERNAL_SERVER_ERROR);
}
return resourceData;
}
private static String getRawUriPath(URI uri) {
String path = uri.getRawPath();
if (path.equals("/restconf/data")) {
return null;
}
return path.replaceAll("^/restconf/data/", "").replaceAll("^/restconf/operations/", "");
}
/**
* Convert Resource Id and Data Node to Json ObjectNode.
*
* @param rid resource identifier
* @param dataNode represents type of node in data store
* @return JSON representation of the data resource
*/
public static ObjectNode convertDataNodeToJson(ResourceId rid, DataNode dataNode) {
RuntimeContext.Builder runtimeContextBuilder = DefaultRuntimeContext.builder();
runtimeContextBuilder.setDataFormat(JSON_FORMAT);
RuntimeContext context = runtimeContextBuilder.build();
DefaultResourceData.Builder resourceDataBuilder = DefaultResourceData.builder();
resourceDataBuilder.addDataNode(dataNode);
resourceDataBuilder.resourceId(rid);
ResourceData resourceData = resourceDataBuilder.build();
DefaultCompositeData.Builder compositeDataBuilder = DefaultCompositeData.builder();
compositeDataBuilder.resourceData(resourceData);
CompositeData compositeData = compositeDataBuilder.build();
ObjectNode rootNode = null;
try {
// CompositeData --- YangRuntimeService ---> CompositeStream.
CompositeStream compositeStream = YANG_RUNTIME.encode(compositeData, context);
InputStream inputStream = compositeStream.resourceData();
rootNode = convertInputStreamToObjectNode(inputStream);
} catch (Exception ex) {
log.error("convertInputStreamToObjectNode failure: {}", ex.getMessage());
log.debug("convertInputStreamToObjectNode failure", ex);
}
if (rootNode == null) {
throw new RestconfException("ERROR: InputStream can not be convert to ObjectNode",
INTERNAL_SERVER_ERROR);
}
return rootNode;
}
/**
* Removes the last path segment from the given URI. That is, returns
* the parent of the given URI.
*
* @param uri given URI
* @return parent URI
*/
public static URI rmLastPathSegment(URI uri) {
if (uri == null) {
return null;
}
UriBuilder builder = UriBuilder.fromUri(uri);
String newPath = rmLastPathSegmentStr(uri.getRawPath());
builder.replacePath(newPath);
return builder.build();
}
private static String rmLastPathSegmentStr(String rawPath) {
if (rawPath == null) {
return null;
}
int pos = rawPath.lastIndexOf(SLASH);
if (pos <= 0) {
return null;
}
return rawPath.substring(0, pos);
}
/**
* Creates a RESTCONF RPC output object from a given YANG RPC output object.
*
* @param cmdId resource ID of the RPC
* @param rpcOutput given RPC output in YANG format
* @return RPC output in RESTCONF format
*/
public static RestconfRpcOutput convertRpcOutput(ResourceId cmdId, RpcOutput rpcOutput) {
RestconfRpcOutput restconfRpcOutput = new RestconfRpcOutput();
restconfRpcOutput.status(convertResponseStatus(rpcOutput.status()));
if (rpcOutput.data() != null) {
restconfRpcOutput.output(convertDataNodeToJson(cmdId, rpcOutput.data()));
}
return restconfRpcOutput;
}
private static Response.Status convertResponseStatus(RpcOutput.Status status) {
switch (status) {
case RPC_SUCCESS:
return OK;
case RPC_FAILURE:
return EXPECTATION_FAILED;
case RPC_NODATA:
return NO_CONTENT;
case RPC_TIMEOUT:
return REQUEST_TIMEOUT;
default:
return BAD_REQUEST;
}
}
}