From 204d8517566d7f4964e5c5e4060e075543e46b52 Mon Sep 17 00:00:00 2001 From: Sharon Aicler Date: Thu, 19 Mar 2015 10:51:59 -0700 Subject: [PATCH] Fix bug bug 2651 - XSQL does not pickup augmentation data Change-Id: I350de6ccf5efd8e427c5823a0f03c91e0df33f19 Signed-off-by: Sharon Aicler --- .../src/main/resources/04-xsql.xml | 4 +- .../main/java/org/odl/xsql/JDBCDriver.java | 11 +- .../md/sal/dom/xsql/TablesResultSet.java | 11 +- .../md/sal/dom/xsql/XSQLAdapter.java | 28 +-- .../md/sal/dom/xsql/XSQLBluePrint.java | 35 +++- .../md/sal/dom/xsql/XSQLBluePrintNode.java | 67 ++++-- .../md/sal/dom/xsql/XSQLODLUtils.java | 65 ++++-- .../md/sal/dom/xsql/jdbc/JDBCResultSet.java | 198 +++++++++++++----- .../md/sal/dom/xsql/jdbc/JDBCServer.java | 3 +- .../org/opendaylight/xsql/XSQLProvider.java | 36 ++-- .../org/ns/xsql/rev140626/XSQLModule.java | 23 +- .../src/main/resources/04-xsql.xml | 4 +- .../sal-dom-xsql/src/main/yang/XSQL.yang | 6 +- .../org/opendaylight/xsql/test/XSQLTest.java | 29 ++- .../src/test/resources/BluePrintCache.dat | Bin 60431 -> 88704 bytes 15 files changed, 384 insertions(+), 136 deletions(-) diff --git a/opendaylight/md-sal/sal-dom-xsql-config/src/main/resources/04-xsql.xml b/opendaylight/md-sal/sal-dom-xsql-config/src/main/resources/04-xsql.xml index d7d547d19e..1b9a37df66 100644 --- a/opendaylight/md-sal/sal-dom-xsql-config/src/main/resources/04-xsql.xml +++ b/opendaylight/md-sal/sal-dom-xsql-config/src/main/resources/04-xsql.xml @@ -8,8 +8,8 @@ XSQL - binding:binding-data-broker - binding-data-broker + binding:binding-async-data-broker + binding-data-broker dom:dom-async-data-broker diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/odl/xsql/JDBCDriver.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/odl/xsql/JDBCDriver.java index cc92b48a15..2cb2e7bfb5 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/odl/xsql/JDBCDriver.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/odl/xsql/JDBCDriver.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.odl.xsql; import java.sql.Connection; @@ -10,7 +17,9 @@ import java.util.Properties; import java.util.logging.Logger; import org.opendaylight.controller.md.sal.dom.xsql.jdbc.JDBCConnection; - +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ public class JDBCDriver implements Driver { public static JDBCDriver drv = new JDBCDriver(); diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/TablesResultSet.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/TablesResultSet.java index 2f28052758..938d25ec50 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/TablesResultSet.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/TablesResultSet.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.md.sal.dom.xsql; import java.io.InputStream; @@ -21,7 +28,9 @@ import java.sql.Time; import java.sql.Timestamp; import java.util.Calendar; import java.util.Map; - +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ public class TablesResultSet implements ResultSet { private String tables[] = null; diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLAdapter.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLAdapter.java index a5658ccc9e..05f65225ea 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLAdapter.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLAdapter.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.md.sal.dom.xsql; import java.io.File; @@ -24,7 +31,9 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; - +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ public class XSQLAdapter extends Thread implements SchemaContextListener { private static final int SLEEP = 10000; @@ -51,6 +60,7 @@ public class XSQLAdapter extends Thread implements SchemaContextListener { private String pinningFile; private ServerSocket serverSocket = null; private DOMDataBroker domDataBroker = null; + private static final String REFERENCE_FIELD_NAME = "reference"; private XSQLAdapter() { XSQLAdapter.log("Starting Adapter"); @@ -152,28 +162,18 @@ public class XSQLAdapter extends Thread implements SchemaContextListener { List result = new LinkedList(); YangInstanceIdentifier instanceIdentifier = YangInstanceIdentifier .builder() - .node(XSQLODLUtils.getPath(table.getODLNode()).get(0)) + .node(XSQLODLUtils.getPath(table.getFirstFromSchemaNodes()).get(0)) .toInstance(); DOMDataReadTransaction t = this.domDataBroker .newReadOnlyTransaction(); Object node = t.read(type, instanceIdentifier).get(); - node = XSQLODLUtils.get(node, "reference"); + node = XSQLODLUtils.get(node, REFERENCE_FIELD_NAME); if (node == null) { return result; } - - Map children = XSQLODLUtils.getChildren(node); - for (Object c : children.values()) { - result.add(c); - /* I don't remember why i did this... possibly to prevent different siblings queried together - Map sons = XSQLODLUtils.getChildren(c); - for (Object child : sons.values()) { - result.add(child); - }*/ - } - + result.add(node); return result; } catch (Exception err) { XSQLAdapter.log(err); diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrint.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrint.java index a9c0f69fc6..76152966d0 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrint.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrint.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.md.sal.dom.xsql; import java.io.DataInputStream; @@ -23,7 +30,9 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; - +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ public class XSQLBluePrint implements DatabaseMetaData, Serializable { private static final long serialVersionUID = 1L; @@ -203,15 +212,23 @@ public class XSQLBluePrint implements DatabaseMetaData, Serializable { return result; } - public void addToBluePrintCache(XSQLBluePrintNode blNode) { - this.tableNameToBluePrint.put(blNode.getBluePrintNodeName(), blNode); - Map map = this.odlNameToBluePrint.get(blNode - .getODLTableName()); - if (map == null) { - map = new HashMap(); - this.odlNameToBluePrint.put(blNode.getODLTableName(), map); + public XSQLBluePrintNode addToBluePrintCache(XSQLBluePrintNode blNode,XSQLBluePrintNode parent) { + XSQLBluePrintNode existingNode = this.tableNameToBluePrint.get(blNode.getBluePrintNodeName()); + if(existingNode!=null){ + existingNode.mergeAugmentation(blNode); + return existingNode; + }else{ + this.tableNameToBluePrint.put(blNode.getBluePrintNodeName(), blNode); + Map map = this.odlNameToBluePrint.get(blNode.getODLTableName()); + if (map == null) { + map = new HashMap(); + this.odlNameToBluePrint.put(blNode.getODLTableName(), map); + } + map.put(blNode.getBluePrintNodeName(), blNode); + if(parent!=null) + parent.addChild(blNode); + return blNode; } - map.put(blNode.getBluePrintNodeName(), blNode); } public Class getGenericType(ParameterizedType type) { diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrintNode.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrintNode.java index 4a56545238..d3cd91a6bd 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrintNode.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLBluePrintNode.java @@ -1,3 +1,10 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.md.sal.dom.xsql; import java.io.Serializable; @@ -8,6 +15,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ public class XSQLBluePrintNode implements Serializable { private static final long serialVersionUID = 1L; @@ -24,12 +34,25 @@ public class XSQLBluePrintNode implements Serializable { private Set columns = new HashSet(); private Map origNameToColumn = new HashMap(); - private transient Object odlNode = null; + private transient Object[] odlSchemaNodes = null; private boolean module = false; private String bluePrintTableName = null; private String odlTableName = null; private String origName = null; + public void mergeAugmentation(XSQLBluePrintNode aug) { + this.relations.addAll(aug.relations); + this.inheritingNodes.addAll(aug.inheritingNodes); + this.children.addAll(aug.children); + this.columns.addAll(aug.columns); + this.origNameToColumn.putAll(aug.origNameToColumn); + if (aug.odlSchemaNodes != null) { + for (Object sn : aug.odlSchemaNodes) { + addToSchemaNodes(sn); + } + } + } + public XSQLBluePrintNode(String name, String _origName, int _level) { this.level = _level; this.odlTableName = name; @@ -46,12 +69,32 @@ public class XSQLBluePrintNode implements Serializable { public XSQLBluePrintNode(Object _odlNode, int _level, XSQLBluePrintNode _parent) { - this.odlNode = _odlNode; + addToSchemaNodes(_odlNode); this.level = _level; this.module = XSQLODLUtils.isModule(_odlNode); this.parent = _parent; this.bluePrintTableName = XSQLODLUtils.getBluePrintName(_odlNode); - this.odlTableName = XSQLODLUtils.getODLNodeName(this.odlNode); + this.odlTableName = XSQLODLUtils + .getODLNodeName(getFirstFromSchemaNodes()); + } + + private void addToSchemaNodes(Object schemaObject) { + if (this.odlSchemaNodes == null) + this.odlSchemaNodes = new Object[1]; + else { + Object[] temp = new Object[this.odlSchemaNodes.length + 1]; + System.arraycopy(this.odlSchemaNodes, 0, temp, 0, + this.odlSchemaNodes.length); + this.odlSchemaNodes = temp; + } + this.odlSchemaNodes[this.odlSchemaNodes.length - 1] = schemaObject; + } + + public Object getFirstFromSchemaNodes() { + if (this.odlSchemaNodes == null) { + return null; + } + return this.odlSchemaNodes[0]; } public String getOrigName() { @@ -72,16 +115,13 @@ public class XSQLBluePrintNode implements Serializable { public String getODLTableName() { if (this.odlTableName == null) { - this.odlTableName = XSQLODLUtils.getODLNodeName(this.odlNode); + this.odlTableName = XSQLODLUtils + .getODLNodeName(getFirstFromSchemaNodes()); } return this.odlTableName; } - public Object getODLNode() { - return this.odlNode; - } - - public void AddChild(XSQLBluePrintNode ch) { + public void addChild(XSQLBluePrintNode ch) { this.children.add(ch); } @@ -218,7 +258,7 @@ public class XSQLBluePrintNode implements Serializable { if (myInterfaceName != null) { return myInterfaceName; } - if (odlNode != null) { + if (this.odlSchemaNodes != null) { return getBluePrintNodeName(); } if (odlTableName != null) { @@ -238,15 +278,14 @@ public class XSQLBluePrintNode implements Serializable { @Override public boolean equals(Object obj) { XSQLBluePrintNode other = (XSQLBluePrintNode) obj; - if (odlNode != null) { + if (this.odlSchemaNodes != null) { return getBluePrintNodeName().equals(other.getBluePrintNodeName()); } else if (this.odlTableName == null && other.odlTableName != null) { return false; } if (this.odlTableName != null && other.odlTableName == null) { return false; - } - else { + } else { return this.odlTableName.equals(other.odlTableName); } } @@ -255,7 +294,7 @@ public class XSQLBluePrintNode implements Serializable { public int hashCode() { if (myInterfaceString != null) { return myInterfaceString.hashCode(); - } else if (odlNode != null) { + } else if (this.odlSchemaNodes != null) { return bluePrintTableName.hashCode(); } return 0; diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLODLUtils.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLODLUtils.java index 17b8ae5f29..16a33b380b 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLODLUtils.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/XSQLODLUtils.java @@ -1,15 +1,27 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.md.sal.dom.xsql; import java.lang.reflect.Field; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; @@ -20,7 +32,9 @@ import org.opendaylight.yangtools.yang.model.util.Uint16; import org.opendaylight.yangtools.yang.model.util.Uint32; import org.opendaylight.yangtools.yang.model.util.Uint64; import org.opendaylight.yangtools.yang.model.util.Uint8; - +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ public class XSQLODLUtils { private static Map, Class> types = @@ -113,7 +127,7 @@ public class XSQLODLUtils { public static boolean createOpenDaylightCache(XSQLBluePrint bluePrint,Object module) { XSQLBluePrintNode node = new XSQLBluePrintNode(module, 0,null); - bluePrint.addToBluePrintCache(node); + bluePrint.addToBluePrintCache(node,null); collectODL(bluePrint, node, ((Module) module).getChildNodes(), 1); return true; } @@ -124,20 +138,30 @@ public class XSQLODLUtils { return; } for (DataSchemaNode n : nodes) { - if (n instanceof DataNodeContainer /*|| n instanceof LeafListSchemaNode*/ - || n instanceof ListSchemaNode) { + if (n instanceof DataNodeContainer) { XSQLBluePrintNode bn = new XSQLBluePrintNode(n, level,parent); - bluePrint.addToBluePrintCache(bn); - parent.AddChild(bn); - if (n instanceof DataNodeContainer) { + bn = bluePrint.addToBluePrintCache(bn,parent); + if (n instanceof ListSchemaNode) { level++; - collectODL(bluePrint, bn, - ((DataNodeContainer) n).getChildNodes(), level); + collectODL(bluePrint, bn,((ListSchemaNode) n).getChildNodes(), level); + Set s = ((ListSchemaNode)n).getAvailableAugmentations(); + if(s!=null){ + for(AugmentationSchema as:s){ + collectODL(bluePrint, bn,as.getChildNodes(), level); + } + } level--; - } else if (n instanceof ListSchemaNode) { + }else{ level++; - collectODL(bluePrint, bn, - ((ListSchemaNode) n).getChildNodes(), level); + collectODL(bluePrint, bn,((DataNodeContainer) n).getChildNodes(), level); + if(n instanceof ContainerSchemaNode){ + Set s = ((ContainerSchemaNode)n).getAvailableAugmentations(); + if(s!=null){ + for(AugmentationSchema as:s){ + collectODL(bluePrint, bn,as.getChildNodes(), level); + } + } + } level--; } } else { @@ -189,7 +213,7 @@ public class XSQLODLUtils { Field f = findField(c, name); return f.get(o); } catch (Exception err) { - XSQLAdapter.log(err); + //XSQLAdapter.log(err); } return null; } @@ -207,6 +231,21 @@ public class XSQLODLUtils { return (Map) get(o, "children"); } + public static Collection getChildrenCollection(Object o) { + Object value = get(o, "children"); + if(value==null) + return Collections.emptyList(); + if(value instanceof Map) + return ((Map)value).values(); + else + if(value instanceof Collection){ + return (Collection)value; + }else{ + XSQLAdapter.log("Unknown Child Value Type="+value.getClass().getName()); + return new ArrayList(); + } + } + public static Object getValue(Object o) { return get(o, "value"); } diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCResultSet.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCResultSet.java index 6689908204..ea16e72dc9 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCResultSet.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCResultSet.java @@ -1,10 +1,16 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.controller.md.sal.dom.xsql.jdbc; import java.io.InputStream; import java.io.Reader; import java.io.Serializable; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import java.math.BigDecimal; import java.net.URL; import java.sql.Array; @@ -24,6 +30,7 @@ import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; @@ -32,14 +39,18 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.opendaylight.controller.md.sal.dom.xsql.XSQLAdapter; import org.opendaylight.controller.md.sal.dom.xsql.XSQLBluePrint; import org.opendaylight.controller.md.sal.dom.xsql.XSQLBluePrintNode; import org.opendaylight.controller.md.sal.dom.xsql.XSQLColumn; import org.opendaylight.controller.md.sal.dom.xsql.XSQLCriteria; import org.opendaylight.controller.md.sal.dom.xsql.XSQLODLUtils; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; -public class JDBCResultSet implements Serializable, ResultSet, - ResultSetMetaData { +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ +public class JDBCResultSet implements Serializable, ResultSet, ResultSetMetaData { private static final long serialVersionUID = -7450200738431047057L; private static final ClassLoader CLASS_LOADER = JDBCResultSet.class.getClassLoader(); private static final Class[] PROXY_INTERFACES = new Class[] { ResultSet.class }; @@ -57,27 +68,28 @@ public class JDBCResultSet implements Serializable, ResultSet, private Map>> criteria = new ConcurrentHashMap>>(); private Exception err = null; private List EMPTY_RESULT = new LinkedList(); - private transient Map subQueries = new HashMap(); + private transient Map subQueries = new HashMap(); public ResultSet getProxy() { - return (ResultSet) Proxy.newProxyInstance(CLASS_LOADER, PROXY_INTERFACES, new JDBCProxy(this)); + return this; + //return (ResultSet) Proxy.newProxyInstance(CLASS_LOADER, PROXY_INTERFACES, new JDBCProxy(this)); } public void setSQL(String _sql) { this.sql = _sql; } - public JDBCResultSet addSubQuery(String _sql,String logicalName) { + public JDBCResultSet addSubQuery(String _sql, String logicalName) { if (subQueries == null) { - subQueries = new HashMap(); + subQueries = new HashMap(); } JDBCResultSet rs = new JDBCResultSet(_sql); - this.subQueries.put(logicalName,rs); + this.subQueries.put(logicalName, rs); return rs; } - public Map getSubQueries() { - if (this.subQueries==null) { + public Map getSubQueries() { + if (this.subQueries == null) { this.subQueries = new HashMap<>(); } return this.subQueries; @@ -112,7 +124,8 @@ public class JDBCResultSet implements Serializable, ResultSet, } } - public int isObjectFitCriteria(Map objValues, String tableName) { + public int isObjectFitCriteria(Map objValues, + String tableName) { Map> tblCriteria = criteria .get(tableName); if (tblCriteria == null) { @@ -289,19 +302,41 @@ public class JDBCResultSet implements Serializable, ResultSet, } public static class Record { + // The map container the Attribute 2 the attribute value public Map data = new HashMap<>(); + // The Element Object (Possibly some kind of NormalizedNode public Object element = null; + // Does this record fit the criteria + // In case of a list property, we first collect the list and only then + // we + // we decide which list item should be included or not. + public boolean fitCriteria = true; public Map getRecord() { return this.data; } } - private Map collectColumnValues(Object node, XSQLBluePrintNode bpn) { - Map subChildren = XSQLODLUtils.getChildren(node); - Map result = new HashMap<>(); - for (Object stc : subChildren.values()) { - if (stc.getClass().getName().endsWith("ImmutableAugmentationNode")) { + public static class RecordsContainer { + public List records = new LinkedList(); + public List fitRecords = new LinkedList(); + public Object currentObject = null; + } + + private void collectColumnValues(RecordsContainer rContainer, + XSQLBluePrintNode bpn) { + Collection subChildren = XSQLODLUtils + .getChildrenCollection(rContainer.currentObject); + Record r = new Record(); + r.element = rContainer.currentObject; + for (Object stc : subChildren) { + if (stc.getClass().getName() + .endsWith("ImmutableUnkeyedListEntryNode")) { + r.fitCriteria = false; + rContainer.currentObject = stc; + collectColumnValues(rContainer, bpn); + } else if (stc.getClass().getName() + .endsWith("ImmutableAugmentationNode")) { Map values = XSQLODLUtils.getChildren(stc); for (Object key : values.keySet()) { Object val = values.get(key); @@ -309,7 +344,7 @@ public class JDBCResultSet implements Serializable, ResultSet, Object value = XSQLODLUtils.getValue(val); String k = XSQLODLUtils.getNodeName(val); if (value != null) { - result.put(bpn.getBluePrintNodeName() + "." + k, + r.data.put(bpn.getBluePrintNodeName() + "." + k, value.toString()); } } @@ -318,16 +353,17 @@ public class JDBCResultSet implements Serializable, ResultSet, String k = XSQLODLUtils.getNodeName(stc); Object value = XSQLODLUtils.getValue(stc); if (value != null) { - result.put(bpn.getBluePrintNodeName() + "." + k, + r.data.put(bpn.getBluePrintNodeName() + "." + k, value.toString()); } } } - return result; + if (r.fitCriteria) { + rContainer.records.add(r); + } } - private void addToData(Record rec, XSQLBluePrintNode bpn, - XSQLBluePrint bluePrint, Map fullRecord) { + private void addToData(Record rec, XSQLBluePrintNode bpn,XSQLBluePrint bluePrint, Map fullRecord) { XSQLBluePrintNode eNodes[] = bluePrint .getBluePrintNodeByODLTableName(XSQLODLUtils .getNodeIdentiofier(rec.element)); @@ -386,6 +422,11 @@ public class JDBCResultSet implements Serializable, ResultSet, String odlNodeName = XSQLODLUtils.getNodeIdentiofier(child); if (odlNodeName == null) { + if (child instanceof DataContainerNode) { + List augChidlren = getChildren(child, tableName, + bluePrint); + result.addAll(augChidlren); + } continue; } @@ -407,7 +448,10 @@ public class JDBCResultSet implements Serializable, ResultSet, continue; } - if (child.getClass().getName().endsWith("ImmutableContainerNode")) { + if (child.getClass().getName().endsWith("ImmutableUnkeyedListNode")) { + result.add(child); + } else if (child.getClass().getName() + .endsWith("ImmutableContainerNode")) { result.add(child); } else if (child.getClass().getName() .endsWith("ImmutableAugmentationNode")) { @@ -420,52 +464,76 @@ public class JDBCResultSet implements Serializable, ResultSet, } } else if (child.getClass().getName().endsWith("ImmutableMapNode")) { result.addAll(XSQLODLUtils.getMChildren(child)); + } else { + XSQLAdapter.log("Missed Node Data OF Type=" + + child.getClass().getName()); } } return result; } - public List addRecords(Object element, XSQLBluePrintNode node,boolean root, String tableName, XSQLBluePrint bluePrint) { + public List addRecords(Object element, XSQLBluePrintNode node, + boolean root, String tableName, XSQLBluePrint bluePrint) { List result = new LinkedList(); - //In case this is a sibling to the requested table, the elenment type - //won't be in the path of the leaf node - if(node==null){ - return result; - } String nodeID = XSQLODLUtils.getNodeIdentiofier(element); if (node.getODLTableName().equals(nodeID)) { - XSQLBluePrintNode bluePrintNode = bluePrint.getBluePrintNodeByODLTableName(nodeID)[0]; - Record rec = new Record(); - rec.element = element; - XSQLBluePrintNode bpn = this.tablesInQueryMap.get(bluePrintNode.getBluePrintNodeName()); - if (this.criteria.containsKey(bluePrintNode.getBluePrintNodeName()) || bpn != null) { - Map allKeyValues = collectColumnValues(element, bpn); - if (!(isObjectFitCriteria(allKeyValues, - bpn.getBluePrintNodeName()) == 1)) { - return EMPTY_RESULT; + XSQLBluePrintNode bluePrintNode = bluePrint + .getBluePrintNodeByODLTableName(nodeID)[0]; + RecordsContainer rContainer = new RecordsContainer(); + rContainer.currentObject = element; + XSQLBluePrintNode bpn = this.tablesInQueryMap.get(bluePrintNode + .getBluePrintNodeName()); + if (this.criteria.containsKey(bluePrintNode.getBluePrintNodeName()) + || bpn != null) { + collectColumnValues(rContainer, bpn); + for (Record r : rContainer.records) { + if (!(isObjectFitCriteria(r.data, + bpn.getBluePrintNodeName()) == 1)) { + r.fitCriteria = false; + } + if (r.fitCriteria) { + Record rec = new Record(); + rec.element = r.element; + addToData(rec, bpn, bluePrint, r.data); + rContainer.fitRecords.add(rec); + } } - addToData(rec, bpn, bluePrint, allKeyValues); + if (rContainer.fitRecords.isEmpty()) + return EMPTY_RESULT; } - if (root) { - addRecord(rec.data); + if (rContainer.records.isEmpty()) { + Record rec = new Record(); + rec.element = rContainer.currentObject; + if (root) { + addRecord(rec.data); + } else { + result.add(rec); + } } else { - result.add(rec); + for (Record rec : rContainer.fitRecords) { + if (root) { + addRecord(rec.data); + } else { + result.add(rec); + } + } } return result; } XSQLBluePrintNode parent = node.getParent(); - List subRecords = addRecords(element, parent, false, tableName,bluePrint); + List subRecords = addRecords(element, parent, false, tableName, + bluePrint); for (Record subRec : subRecords) { List subO = getChildren(subRec.element, tableName, bluePrint); if (subO != null) { for (Object subData : subO) { - Record rec = new Record(); - rec.element = subData; - rec.data.putAll(subRec.data); + RecordsContainer rContainer = new RecordsContainer(); + rContainer.currentObject = subData; - String recID = XSQLODLUtils.getNodeIdentiofier(rec.element); + String recID = XSQLODLUtils + .getNodeIdentiofier(rContainer.currentObject); XSQLBluePrintNode eNodes[] = bluePrint .getBluePrintNodeByODLTableName(recID); XSQLBluePrintNode bpn = null; @@ -476,18 +544,24 @@ public class JDBCResultSet implements Serializable, ResultSet, break; } } - boolean isObjectInCriteria = true; if (bpn != null) { - Map allKeyValues = collectColumnValues(rec.element, bpn); - if ((isObjectFitCriteria(allKeyValues, - bpn.getBluePrintNodeName()) == 1)) { - addToData(rec, bpn, bluePrint, allKeyValues); - } else { - isObjectInCriteria = false; + collectColumnValues(rContainer, bpn); + for (Record r : rContainer.records) { + if ((isObjectFitCriteria(r.data, + bpn.getBluePrintNodeName()) == 1)) { + Record rec = new Record(); + rec.data.putAll(subRec.data); + rec.element = r.element; + addToData(rec, bpn, bluePrint, r.data); + } else { + r.fitCriteria = false; + } } } - - if (isObjectInCriteria) { + if (rContainer.records.isEmpty()) { + Record rec = new Record(); + rec.data.putAll(subRec.data); + rec.element = rContainer.currentObject; if (root) { if (!rec.data.isEmpty()) { addRecord(rec.data); @@ -495,11 +569,23 @@ public class JDBCResultSet implements Serializable, ResultSet, } else { result.add(rec); } + } else { + for (Record r : rContainer.records) { + r.data.putAll(subRec.data); + if (r.fitCriteria) { + if (root) { + if (!r.data.isEmpty()) { + addRecord(r.data); + } + } else { + result.add(r); + } + } + } } } } } - return result; } diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCServer.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCServer.java index 7b2733ccf7..31941e496b 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCServer.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/controller/md/sal/dom/xsql/jdbc/JDBCServer.java @@ -46,8 +46,7 @@ public class JDBCServer extends Thread { } } - public static void execute(JDBCResultSet rs, XSQLAdapter adapter) - throws SQLException { + public static void execute(JDBCResultSet rs, XSQLAdapter adapter)throws SQLException { if(rs.getSQL().toLowerCase().trim().equals("select 1")){ rs.setFinished(true); return; diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/xsql/XSQLProvider.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/xsql/XSQLProvider.java index cde01573f2..29a1945a6e 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/xsql/XSQLProvider.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/xsql/XSQLProvider.java @@ -1,37 +1,47 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.xsql; -import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction; -import org.opendaylight.controller.sal.binding.api.data.DataProviderService; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.dom.xsql.XSQLAdapter; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.xsql.rev140626.XSQL; import org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.xsql.rev140626.XSQLBuilder; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** - * Created by root on 6/26/14. - */ + * @author Sharon Aicler(saichler@gmail.com) + **/ public class XSQLProvider implements AutoCloseable { public static final InstanceIdentifier ID = InstanceIdentifier.builder(XSQL.class).build(); - private static final Logger LOG = LoggerFactory.getLogger(XSQLProvider.class); + //public static final InstanceIdentifier ID2 = InstanceIdentifier.builder(SalTest.class).build(); public void close() { } - public XSQL buildXSQL(DataProviderService dps) { + public XSQL buildXSQL(DataBroker dps) { + XSQLAdapter.log("Building XSL..."); XSQLBuilder builder = new XSQLBuilder(); builder.setPort("34343"); XSQL xsql = builder.build(); try { if (dps != null) { - final DataModificationTransaction t = dps.beginTransaction(); - t.removeOperationalData(ID); - t.putOperationalData(ID,xsql); - t.commit().get(); + XSQLAdapter.log("Starting TRansaction..."); + WriteTransaction t = dps.newReadWriteTransaction(); + t.delete(LogicalDatastoreType.OPERATIONAL, ID); + t.put(LogicalDatastoreType.OPERATIONAL,ID,xsql); + XSQLAdapter.log("Submitting..."); + t.submit(); } } catch (Exception e) { - LOG.warn("Failed to update XSQL port status, ", e); + XSQLAdapter.log(e); } return xsql; } diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java index c8a5a85ae6..a669345e14 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/java/org/opendaylight/yang/gen/v1/http/netconfcentral/org/ns/xsql/rev140626/XSQLModule.java @@ -1,9 +1,19 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ package org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.xsql.rev140626; import org.opendaylight.controller.md.sal.dom.xsql.XSQLAdapter; import org.opendaylight.xsql.XSQLProvider; - +/** + * @author Sharon Aicler(saichler@gmail.com) + **/ public class XSQLModule extends org.opendaylight.yang.gen.v1.http.netconfcentral.org.ns.xsql.rev140626.AbstractXSQLModule { + private static final long SLEEP_TIME_BEFORE_CREATING_TRANSACTION = 10000; public XSQLModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } @@ -22,9 +32,14 @@ public class XSQLModule extends org.opendaylight.yang.gen.v1.http.netconfcentral XSQLAdapter xsqlAdapter = XSQLAdapter.getInstance(); getSchemaServiceDependency().registerSchemaContextListener(xsqlAdapter); xsqlAdapter.setDataBroker(getAsyncDataBrokerDependency()); - XSQLProvider p = new XSQLProvider(); - //p.buildXSQL(getDataBrokerDependency()); + final XSQLProvider p = new XSQLProvider(); + Runnable runthis = new Runnable() { + @Override + public void run() { + try{Thread.sleep(SLEEP_TIME_BEFORE_CREATING_TRANSACTION);}catch(Exception err){} + p.buildXSQL(getDataBrokerDependency()); + } + }; return p; } - } diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/resources/04-xsql.xml b/opendaylight/md-sal/sal-dom-xsql/src/main/resources/04-xsql.xml index d7d547d19e..1b9a37df66 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/resources/04-xsql.xml +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/resources/04-xsql.xml @@ -8,8 +8,8 @@ XSQL - binding:binding-data-broker - binding-data-broker + binding:binding-async-data-broker + binding-data-broker dom:dom-async-data-broker diff --git a/opendaylight/md-sal/sal-dom-xsql/src/main/yang/XSQL.yang b/opendaylight/md-sal/sal-dom-xsql/src/main/yang/XSQL.yang index 0437e10e34..f0f52694a6 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/main/yang/XSQL.yang +++ b/opendaylight/md-sal/sal-dom-xsql/src/main/yang/XSQL.yang @@ -37,14 +37,14 @@ module XSQL{ case XSQL { when "/config:modules/config:module/config:type = 'XSQL'"; - container data-broker { + container data-broker { uses config:service-ref { refine type { mandatory false; - config:required-identity mdsal:binding-data-broker; + config:required-identity mdsal:binding-async-data-broker; } } - } + } container async-data-broker { uses config:service-ref { diff --git a/opendaylight/md-sal/sal-dom-xsql/src/test/java/org/opendaylight/xsql/test/XSQLTest.java b/opendaylight/md-sal/sal-dom-xsql/src/test/java/org/opendaylight/xsql/test/XSQLTest.java index 8a6b184f82..e3f5fbb810 100644 --- a/opendaylight/md-sal/sal-dom-xsql/src/test/java/org/opendaylight/xsql/test/XSQLTest.java +++ b/opendaylight/md-sal/sal-dom-xsql/src/test/java/org/opendaylight/xsql/test/XSQLTest.java @@ -2,18 +2,29 @@ package org.opendaylight.xsql.test; import java.io.InputStream; import java.sql.SQLException; +import java.util.Collections; +import java.util.Set; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.md.sal.dom.xsql.XSQLAdapter; import org.opendaylight.controller.md.sal.dom.xsql.XSQLBluePrint; import org.opendaylight.controller.md.sal.dom.xsql.jdbc.JDBCResultSet; import org.opendaylight.controller.md.sal.dom.xsql.jdbc.JDBCServer; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; public class XSQLTest { - - XSQLBluePrint bluePrint = null; + private static final String DATASTORE_TEST_YANG = "/sal-persisted-dom-test.yang"; + private XSQLBluePrint bluePrint = null; + //private static SchemaContext schemaContext = null; + @BeforeClass + public static void loadSchemaContext(){ + //schemaContext = createTestContext(); + } @Before public void before() { @@ -167,4 +178,18 @@ public class XSQLTest { System.out.print("*** XSQL Tests -"); System.out.println(str); } + + public static final InputStream getDatastoreTestInputStream() { + return getInputStream(DATASTORE_TEST_YANG); + } + + private static InputStream getInputStream(final String resourceName) { + return XSQLTest.class.getResourceAsStream(DATASTORE_TEST_YANG); + } + + public static SchemaContext createTestContext() { + YangParserImpl parser = new YangParserImpl(); + Set modules = parser.parseYangModelsFromStreams(Collections.singletonList(getDatastoreTestInputStream())); + return parser.resolveSchemaContext(modules); + } } diff --git a/opendaylight/md-sal/sal-dom-xsql/src/test/resources/BluePrintCache.dat b/opendaylight/md-sal/sal-dom-xsql/src/test/resources/BluePrintCache.dat index b6b34acfcd2b9e983e71320e0dd6f11a03c5d8c5..152f7878df6ab928a53c5a5e0a7c1f638b208faa 100644 GIT binary patch literal 88704 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 -- 2.36.6