按照前面一篇文章的框架,sal层的Node这个类应该是和插件层的类有联系,但在这个类中,我没有看到引入了插件层的内容。我的猜想,它应该符合观察者的模式,它属于观察者,而插件层是主题,当插件层的内容发生变化时,它应该也有所变化,但这里并没有那个联系,反而我在插件层中的控制器中看到了Controller类引用了Node这个类,未完待续,之后慢慢理解再补充。
package org.opendaylight.controller.sal.core;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.opendaylight.controller.sal.utils.HexEncode;
import org.opendaylight.controller.sal.utils.INodeFactory;
import org.opendaylight.controller.sal.utils.ServiceHelper;
/**
* Describe a generic network element in multiple SDNs technologies. A
* Node is identified by the pair (NodeType, NodeID), the nodetype are
* needed in order to further specify the nodeID
*
*/
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class Node implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Enum-like static class created with the purpose of identifing
* multiple type of nodes in the SDN network. The type is
* necessary to figure out to later on correctly use the
* nodeID. Using a static class instead of an Enum so we can add
* dynamically new types without changing anything in the
* surround.
*/
public static final class NodeIDType {
private static final ConcurrentHashMap<String, Class<? extends Object>> compatibleType =
new ConcurrentHashMap<String, Class<? extends Object>>();
/**
* Identifier for an OpenFlow node
*/
public static String OPENFLOW = "OF";
/**
* Identifier for a PCEP node
*/
public static String PCEP = "PE";
/**
* Identifier for a ONEPK node
*/
public static String ONEPK = "PK";
/**
* Identifier for a node in a non-SDN network
*/
public static String PRODUCTION = "PR";
// Pre-populated types, just here for convenience and ease of
// unit-testing, but certainly those could live also outside.
static {
compatibleType.put(OPENFLOW, Long.class);
compatibleType.put(PCEP, UUID.class);
compatibleType.put(ONEPK, String.class);
compatibleType.put(PRODUCTION, String.class);
}
/**
* Return the type of the class expected for the
* NodeID, it's used for validity check in the constructor
*
* @param nodeType the type of the node we want to check
* compatibility for
*
* @return The Class which is supposed to instantiate the ID
* for the NodeID
*/
public static Class<?> getClassType(String nodeType) {
return compatibleType.get(nodeType);
}
/**
* Returns all the registered nodeIDTypes currently available
*
* @return The current registered NodeIDTypes
*/
public static Set<String> values() {
return compatibleType.keySet();
}
/**
* Register a new ID for which Node can be created
*
* @param type, the new type being registered
* @param compatibleID, the type of class to be accepted as ID
*
* @return true if registered, false otherwise
*/
public static boolean registerIDType(String type,
Class<? extends Object> compatibleID) {
if (compatibleType.get(type) != null) {
return false;
} else {
compatibleType.put(type, compatibleID);
return true;
}
}
/**
* UNRegister a new ID for which Node can be created
*
* @param type, the type being UN-registered
*
*/
public static void unRegisterIDType(String type) {
compatibleType.remove(type);
}
}
// This is the identity of the Node a (Type,ID) pair!, the full
// essence of this class.
private Object nodeID;
private String nodeType;
// Shadow value for unmarshalling
private String nodeIDString;
/**
* Private constructor used for JAXB mapping
*/
private Node() {
this.nodeID = null;
this.nodeType = null;
this.nodeIDString = null;
}
/**
* Constructor for the Node objects, it validate the input so if
* the ID passed is not of the type expected accordingly to the
* type an exception is raised.
*
* @param nodeType Type of the node we are building
* @param id ID used by the SDN technology to identify the node
*
*/
public Node(String nodeType, Object id) throws ConstructionException {
if (NodeIDType.getClassType(nodeType) != null &&
NodeIDType.getClassType(nodeType).isInstance(id)) {
this.nodeType = nodeType;
this.nodeID = id;
} else {
throw new ConstructionException("Type of incoming object:"
+ id.getClass() + " not compatible with expected type:"
+ NodeIDType.getClassType(nodeType));
}
}
/**
* Copy Constructor for the Node objects.
*
* @param src type of nodes to copy from
*
*/
public Node(Node src) throws ConstructionException {
if (src != null) {
this.nodeType = src.getType();
// Here we can reference the object because that is
// supposed to be an immutable identifier as well like a
// UUID/Integer and so on, hence no need to create a copy
// of it
this.nodeID = src.getID();
} else {
throw
new ConstructionException("Null incoming object to copy from");
}
}
/**
* getter for node type
*
*
* @return The node Type for this Node object
*/
@XmlElement(name = "type")
public String getType() {
return this.nodeType;
}
/**
* fill the current object from the string parameters passed, will
* be only used by JAXB
*
* @param typeStr string representing the type of the Node
* @param IDStr String representation of the ID
*/
private void fillmeFromString(String typeStr, String IDStr) {
if (typeStr == null) {
return;
}
if (IDStr == null) {
return;
}
this.nodeType = typeStr;
if (typeStr.equals(NodeIDType.OPENFLOW)) {
this.nodeID = Long.valueOf(HexEncode.stringToLong(IDStr));
} else if (typeStr.equals(NodeIDType.ONEPK)) {
this.nodeID = IDStr;
} else if (typeStr.equals(NodeIDType.PCEP)) {
this.nodeID = UUID.fromString(IDStr);
} else if (typeStr.equals(NodeIDType.PRODUCTION)) {
this.nodeID = IDStr;
} else {
//Use plugin's method to get appropriate conversion from IDStr to nodeID
INodeFactory f = (INodeFactory) ServiceHelper
.getGlobalInstance(INodeFactory.class, new Node(), "(protocolName="+typeStr+")");
if(f!=null){
Node n = f.fromString(typeStr, IDStr);
this.nodeID = n.nodeID;
}
}
}
/**
* Private setter for nodeType to be called by JAXB not by anyone
* else, Node is immutable
*
* @param type of node to be set
*/
private void setType(String type) {
this.nodeType = type;
if (this.nodeIDString != null) {
this.fillmeFromString(type, this.nodeIDString);
}
}
/**
* getter for node ID
*
*
* @return The node ID for this Node object
*/
public Object getID() {
return this.nodeID;
}
/**
* Getter for the node ID in string format
*
* @return The nodeID in string format
*/
@XmlElement(name = "id")
public String getNodeIDString() {
if (this.nodeType.equals(NodeIDType.OPENFLOW)) {
return HexEncode.longToHexString((Long) this.nodeID);
} else {
return this.nodeID.toString();
}
}
/**
* private setter to be used by JAXB
*
* @param nodeIDString String representation for NodeID
*/
private void setNodeIDString(String nodeIDString) {
this.nodeIDString = nodeIDString;
if (this.nodeType != null) {
this.fillmeFromString(this.nodeType, nodeIDString);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((nodeID == null) ? 0 : nodeID.hashCode());
result = prime * result
+ ((nodeType == null) ? 0 : nodeType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Node other = (Node) obj;
if (nodeID == null) {
if (other.nodeID != null)
return false;
} else if (!nodeID.equals(other.nodeID))
return false;
if (nodeType == null) {
if (other.nodeType != null)
return false;
} else if (!nodeType.equals(other.nodeType))
return false;
return true;
}
@Override
public String toString() {
if (this.nodeType.equals(NodeIDType.OPENFLOW)) {
return this.nodeType + "|"
+ HexEncode.longToHexString((Long) this.nodeID);
} else {
return this.nodeType + "|" + this.nodeID.toString();
}
}
/**
* Static method to get back a Node from a string
*
* @param str string formatted in toString mode that can be
* converted back to a Node format.
*
* @return a Node if succed or null if no
*/
public static Node fromString(String str) {
if (str == null) {
return null;
}
String parts[] = str.split("\\|");
if (parts.length != 2) {
// Try to guess from a String formatted as a long because
// for long time openflow has been prime citizen so lets
// keep this legacy for now
String numStr = str.toUpperCase();
Long ofNodeID = null;
if (numStr.startsWith("0X")) {
// Try as an hex number
try {
BigInteger b = new BigInteger(
numStr.replaceFirst("0X", ""), 16);
ofNodeID = Long.valueOf(b.longValue());
} catch (Exception ex) {
ofNodeID = null;
}
} else {
// Try as a decimal number
try {
BigInteger b = new BigInteger(numStr);
ofNodeID = Long.valueOf(b.longValue());
} catch (Exception ex) {
ofNodeID = null;
}
}
// Startegy #3 parse as HexLong
if (ofNodeID == null) {
try {
ofNodeID = Long.valueOf(HexEncode.stringToLong(numStr));
} catch (Exception ex) {
ofNodeID = null;
}
}
// We ran out of ideas ... return null
if (ofNodeID == null) {
return null;
}
// Lets return the cooked up NodeID
try {
return new Node(NodeIDType.OPENFLOW, ofNodeID);
} catch (ConstructionException ex) {
return null;
}
}
String typeStr = parts[0];
String IDStr = parts[1];
return fromString(typeStr, IDStr);
}
/**
* Static method to get back a Node from a pair of strings, the
* first one being the Type representation, the second one being
* the ID string representation, expected to be heavily used in
* northbound API.
*
* @param type, the type of the node we are parsing
* @param id, the string representation of the node id
*
* @return a Node if succed or null if no
*/
public static Node fromString(String typeStr, String IDStr) {
if (typeStr == null) {
return null;
}
if (IDStr == null) {
return null;
}
if (typeStr.equals(NodeIDType.OPENFLOW)) {
try {
Long ID = Long.valueOf(HexEncode.stringToLong(IDStr));
return new Node(typeStr, ID);
} catch (Exception ex) {
return null;
}
} else if (typeStr.equals(NodeIDType.ONEPK)) {
try {
return new Node(typeStr, IDStr);
} catch (Exception ex) {
return null;
}
} else if (typeStr.equals(NodeIDType.PCEP)) {
try {
UUID ID = UUID.fromString(IDStr);
return new Node(typeStr, ID);
} catch (Exception ex) {
return null;
}
} else if (typeStr.equals(NodeIDType.PRODUCTION)) {
try {
return new Node(typeStr, IDStr);
} catch (Exception ex) {
return null;
}
} else {
//Use INodeFactory to create a Node of registered Node type.
//The protocol plugin being used depends on typeStr.
INodeFactory f = (INodeFactory) ServiceHelper
.getGlobalInstance(INodeFactory.class, new Node(), "(protocolName="+typeStr+")");
if(f==null)
return null;
return f.fromString(typeStr, IDStr);
}
}
}