/* * Copyright 2015-present Open Networking Laboratory * * 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.ovsdb.rfc.utils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.onosproject.ovsdb.rfc.exception.AbnormalJsonNodeException; import org.onosproject.ovsdb.rfc.exception.UnsupportedException; import org.onosproject.ovsdb.rfc.jsonrpc.Callback; import org.onosproject.ovsdb.rfc.jsonrpc.JsonRpcResponse; import org.onosproject.ovsdb.rfc.message.OperationResult; import org.onosproject.ovsdb.rfc.message.RowUpdate; import org.onosproject.ovsdb.rfc.message.TableUpdate; import org.onosproject.ovsdb.rfc.message.TableUpdates; import org.onosproject.ovsdb.rfc.message.UpdateNotification; import org.onosproject.ovsdb.rfc.notation.Column; import org.onosproject.ovsdb.rfc.notation.Row; import org.onosproject.ovsdb.rfc.notation.Uuid; import org.onosproject.ovsdb.rfc.operations.Operation; import org.onosproject.ovsdb.rfc.schema.ColumnSchema; import org.onosproject.ovsdb.rfc.schema.DatabaseSchema; import org.onosproject.ovsdb.rfc.schema.TableSchema; import org.onosproject.ovsdb.rfc.schema.type.ColumnTypeFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * JsonNode utility class. convert JsonNode into Object. */ public final class FromJsonUtil { private static final Logger log = LoggerFactory.getLogger(FromJsonUtil.class); /** * Constructs a FromJsonUtil object. Utility classes should not have a * public or default constructor, otherwise IDE will compile unsuccessfully. * This class should not be instantiated. */ private FromJsonUtil() { } /** * Verify whether the jsonNode is normal. * @param jsonNode JsonNode * @param nodeStr the node name of JsonNode */ private static void validateJsonNode(JsonNode jsonNode, String nodeStr) { if (!jsonNode.isObject() || !jsonNode.has(nodeStr)) { String message = "Abnormal DatabaseSchema JsonNode, it should contain " + nodeStr + " node but was not found"; throw new AbnormalJsonNodeException(message); } } /** * convert JsonNode into DatabaseSchema. * @param dbName database name * @param dbJson the JsonNode of get_schema result * @return DatabaseSchema * @throws AbnormalJsonNodeException this is an abnormal JsonNode exception */ public static DatabaseSchema jsonNodeToDbSchema(String dbName, JsonNode dbJson) { validateJsonNode(dbJson, "tables"); validateJsonNode(dbJson, "version"); String dbVersion = dbJson.get("version").asText(); Map<String, TableSchema> tables = new HashMap<>(); Iterator<Map.Entry<String, JsonNode>> tablesIter = dbJson.get("tables").fields(); while (tablesIter.hasNext()) { Map.Entry<String, JsonNode> table = tablesIter.next(); tables.put(table.getKey(), jsonNodeToTableSchema(table.getKey(), table.getValue())); } return new DatabaseSchema(dbName, dbVersion, tables); } /** * convert JsonNode into TableSchema. * @param tableName table name * @param tableJson table JsonNode * @return TableSchema * @throws AbnormalJsonNodeException this is an abnormal JsonNode exception */ private static TableSchema jsonNodeToTableSchema(String tableName, JsonNode tableJson) { validateJsonNode(tableJson, "columns"); Map<String, ColumnSchema> columns = new HashMap<>(); Iterator<Map.Entry<String, JsonNode>> columnsIter = tableJson.get("columns").fields(); while (columnsIter.hasNext()) { Map.Entry<String, JsonNode> column = columnsIter.next(); columns.put(column.getKey(), jsonNodeToColumnSchema(column.getKey(), column.getValue())); } return new TableSchema(tableName, columns); } /** * convert JsonNode into ColumnSchema. * @param name column name * @param columnJson column JsonNode * @return ColumnSchema * @throws AbnormalJsonNodeException this is an abnormal JsonNode exception */ private static ColumnSchema jsonNodeToColumnSchema(String name, JsonNode columnJson) { validateJsonNode(columnJson, "type"); return new ColumnSchema(name, ColumnTypeFactory.getColumnTypeFromJson(columnJson .get("type"))); } /** * convert JsonNode into the returnType of methods in OvsdbRPC class. * @param resultJsonNode the result JsonNode * @param methodName the method name of methods in OvsdbRPC class * @param objectMapper ObjectMapper entity * @return Object * @throws UnsupportedException this is an unsupported exception */ private static Object convertResultType(JsonNode resultJsonNode, String methodName, ObjectMapper objectMapper) { switch (methodName) { case "getSchema": case "monitor": return resultJsonNode; case "echo": case "listDbs": return objectMapper.convertValue(resultJsonNode, objectMapper.getTypeFactory() .constructParametricType(List.class, String.class)); case "transact": return objectMapper.convertValue(resultJsonNode, objectMapper.getTypeFactory() .constructParametricType(List.class, JsonNode.class)); default: throw new UnsupportedException("does not support this rpc method" + methodName); } } /** * convert JsonNode into the returnType of methods in OvsdbRPC class. * @param jsonNode the result JsonNode * @param methodName the method name of methods in OvsdbRPC class * @return Object */ public static Object jsonResultParser(JsonNode jsonNode, String methodName) { ObjectMapper objectMapper = ObjectMapperUtil.getObjectMapper(); JsonNode error = jsonNode.get("error"); if (error != null && !error.isNull()) { log.error("jsonRpcResponse error : {}", error.toString()); } JsonNode resultJsonNode = jsonNode.get("result"); Object result = convertResultType(resultJsonNode, methodName, objectMapper); return result; } /** * When monitor the ovsdb tables, if a table update, ovs send update * notification, then call callback function. * @param jsonNode the result JsonNode * @param callback the callback function * @throws UnsupportedException this is an unsupported exception */ public static void jsonCallbackRequestParser(JsonNode jsonNode, Callback callback) { ObjectMapper objectMapper = ObjectMapperUtil.getObjectMapper(); JsonNode params = jsonNode.get("params"); Object param = null; String methodName = jsonNode.get("method").asText(); switch (methodName) { case "update": param = objectMapper.convertValue(params, UpdateNotification.class); callback.update((UpdateNotification) param); break; default: throw new UnsupportedException("does not support this callback method: " + methodName); } } /** * Ovs send echo request to keep the heart, need we return echo result. * @param jsonNode the result JsonNode * @return JsonRpcResponse String */ public static String getEchoRequestStr(JsonNode jsonNode) { ObjectMapper objectMapper = ObjectMapperUtil.getObjectMapper(); String str = null; if (jsonNode.get("method").asText().equals("echo")) { JsonRpcResponse response = new JsonRpcResponse(jsonNode.get("id").asText()); try { str = objectMapper.writeValueAsString(response); } catch (JsonProcessingException e) { log.error("JsonProcessingException while converting JsonNode into string: ", e); } } return str; } /** * Convert the List of Operation result into List of OperationResult . * @param input the List of JsonNode * @param operations the List of Operation * @return the List of OperationResult */ public static List<OperationResult> jsonNodeToOperationResult(List<JsonNode> input, List<Operation> operations) { ObjectMapper objectMapper = ObjectMapperUtil.getObjectMapper(false); List<OperationResult> operationResults = new ArrayList<OperationResult>(); for (int i = 0; i < input.size(); i++) { JsonNode jsonNode = input.get(i); Operation operation = operations.get(i); if (jsonNode != null && jsonNode.size() > 0) { if (i >= operations.size() || !operation.getOp().equals("select")) { OperationResult or = objectMapper.convertValue(jsonNode, OperationResult.class); operationResults.add(or); } else { List<Row> rows = createRows(operation.getTableSchema(), jsonNode); OperationResult or = new OperationResult(rows); operationResults.add(or); } } } return operationResults; } /** * Convert Operation JsonNode into Rows. * @param tableSchema TableSchema entity * @param rowsNode JsonNode * @return ArrayList<Row> the List of Row */ private static ArrayList<Row> createRows(TableSchema tableSchema, JsonNode rowsNode) { validateJsonNode(rowsNode, "rows"); ArrayList<Row> rows = Lists.newArrayList(); for (JsonNode rowNode : rowsNode.get("rows")) { rows.add(createRow(tableSchema, null, rowNode)); //FIXME null will throw exception } return rows; } /** * convert the params of Update Notification into TableUpdates. * @param updatesJson the params of Update Notification * @param dbSchema DatabaseSchema entity * @return TableUpdates */ public static TableUpdates jsonNodeToTableUpdates(JsonNode updatesJson, DatabaseSchema dbSchema) { Map<String, TableUpdate> tableUpdateMap = Maps.newHashMap(); Iterator<Map.Entry<String, JsonNode>> tableUpdatesItr = updatesJson.fields(); while (tableUpdatesItr.hasNext()) { Map.Entry<String, JsonNode> entry = tableUpdatesItr.next(); TableSchema tableSchema = dbSchema.getTableSchema(entry.getKey()); TableUpdate tableUpdate = jsonNodeToTableUpdate(tableSchema, entry.getValue()); tableUpdateMap.put(entry.getKey(), tableUpdate); } return TableUpdates.tableUpdates(tableUpdateMap); } /** * convert the params of Update Notification into TableUpdate. * @param tableSchema TableSchema entity * @param updateJson the table-update in params of Update Notification * @return TableUpdate */ public static TableUpdate jsonNodeToTableUpdate(TableSchema tableSchema, JsonNode updateJson) { Map<Uuid, RowUpdate> rows = Maps.newHashMap(); Iterator<Map.Entry<String, JsonNode>> tableUpdateItr = updateJson.fields(); while (tableUpdateItr.hasNext()) { Map.Entry<String, JsonNode> oldNewRow = tableUpdateItr.next(); String uuidStr = oldNewRow.getKey(); Uuid uuid = Uuid.uuid(uuidStr); JsonNode newR = oldNewRow.getValue().get("new"); JsonNode oldR = oldNewRow.getValue().get("old"); Row newRow = newR != null ? createRow(tableSchema, uuid, newR) : null; Row oldRow = oldR != null ? createRow(tableSchema, uuid, oldR) : null; RowUpdate rowUpdate = new RowUpdate(uuid, oldRow, newRow); rows.put(uuid, rowUpdate); } return TableUpdate.tableUpdate(rows); } /** * Convert Operation JsonNode into Row. * @param tableSchema TableSchema entity * @param rowNode JsonNode * @return Row */ private static Row createRow(TableSchema tableSchema, Uuid uuid, JsonNode rowNode) { if (tableSchema == null) { return null; } Map<String, Column> columns = Maps.newHashMap(); Iterator<Map.Entry<String, JsonNode>> rowIter = rowNode.fields(); while (rowIter.hasNext()) { Map.Entry<String, JsonNode> next = rowIter.next(); ColumnSchema columnSchema = tableSchema.getColumnSchema(next.getKey()); if (columnSchema != null) { String columnName = columnSchema.name(); Object obj = TransValueUtil.getValueFromJson(next.getValue(), columnSchema.type()); columns.put(columnName, new Column(columnName, obj)); } } return new Row(tableSchema.name(), uuid, columns); } }