Merge "Move adsal into its own subdirectory."
[controller.git] / opendaylight / adsal / sal / api / src / main / java / org / opendaylight / controller / sal / core / Node.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 /**
11  * @file   Node.java
12  *
13  * @brief  Describe a generic network element in multiple SDNs technologies
14  *
15  * Describe a generic network element in multiple SDNs technologies. A
16  * Node is identified by the pair (NodeType, NodeID), the nodetype are
17  * needed in order to further specify the nodeID
18  */
19 package org.opendaylight.controller.sal.core;
20
21 import java.io.Serializable;
22 import java.math.BigInteger;
23 import java.util.Set;
24 import java.util.UUID;
25 import java.util.concurrent.ConcurrentHashMap;
26
27 import javax.xml.bind.annotation.XmlAccessType;
28 import javax.xml.bind.annotation.XmlAccessorType;
29 import javax.xml.bind.annotation.XmlElement;
30 import javax.xml.bind.annotation.XmlRootElement;
31
32 import org.opendaylight.controller.sal.utils.HexEncode;
33 import org.opendaylight.controller.sal.utils.INodeFactory;
34 import org.opendaylight.controller.sal.utils.ServiceHelper;
35
36 /**
37  * Describe a generic network element in multiple SDNs technologies. A
38  * Node is identified by the pair (NodeType, NodeID), the nodetype are
39  * needed in order to further specify the nodeID
40  *
41  */
42 @XmlAccessorType(XmlAccessType.NONE)
43 @XmlRootElement
44 public class Node implements Serializable {
45     private static final long serialVersionUID = 1L;
46
47     /**
48      * Enum-like static class created with the purpose of identifing
49      * multiple type of nodes in the SDN network. The type is
50      * necessary to figure out to later on correctly use the
51      * nodeID. Using a static class instead of an Enum so we can add
52      * dynamically new types without changing anything in the
53      * surround.
54      */
55     public static final class NodeIDType {
56         private static final ConcurrentHashMap<String, Class<? extends Object>> compatibleType =
57             new ConcurrentHashMap<String, Class<? extends Object>>();
58         /**
59          * Identifier for an OpenFlow node
60          */
61         public static String OPENFLOW = "OF";
62         /**
63          * Identifier for a PCEP node
64          */
65         public static String PCEP = "PE";
66         /**
67          * Identifier for a ONEPK node
68          */
69         public static String ONEPK = "PK";
70         /**
71          * Identifier for a node in a non-SDN network
72          */
73         public static String PRODUCTION = "PR";
74
75         // Pre-populated types, just here for convenience and ease of
76         // unit-testing, but certainly those could live also outside.
77         static {
78             compatibleType.put(OPENFLOW, Long.class);
79             compatibleType.put(PCEP, UUID.class);
80             compatibleType.put(ONEPK, String.class);
81             compatibleType.put(PRODUCTION, String.class);
82         }
83
84         /**
85          * Return the type of the class expected for the
86          * NodeID, it's used for validity check in the constructor
87          *
88          * @param nodeType the type of the node we want to check
89          * compatibility for
90          *
91          * @return The Class which is supposed to instantiate the ID
92          * for the NodeID
93          */
94         public static Class<?> getClassType(String nodeType) {
95             return compatibleType.get(nodeType);
96         }
97
98         /**
99          * Returns all the registered nodeIDTypes currently available
100          *
101          * @return The current registered NodeIDTypes
102          */
103         public static Set<String> values() {
104             return compatibleType.keySet();
105         }
106
107         /**
108          * Register a new ID for which Node can be created
109          *
110          * @param type, the new type being registered
111          * @param compatibleID, the type of class to be accepted as ID
112          *
113          * @return true if registered, false otherwise
114          */
115         public static boolean registerIDType(String type,
116                                              Class<? extends Object> compatibleID) {
117             if (compatibleType.get(type) != null) {
118                 return false;
119             }  else {
120                 compatibleType.put(type, compatibleID);
121                 return true;
122             }
123         }
124
125         /**
126          * UNRegister a new ID for which Node can be created
127          *
128          * @param type, the type being UN-registered
129          *
130          */
131         public static void unRegisterIDType(String type) {
132             compatibleType.remove(type);
133         }
134     }
135
136     // This is the identity of the Node a (Type,ID) pair!, the full
137     // essence of this class.
138     private Object nodeID;
139     private String nodeType;
140
141     // Shadow value for unmarshalling
142     private String nodeIDString;
143
144     /**
145      * Private constructor used for JAXB mapping
146      */
147     private Node() {
148         this.nodeID = null;
149         this.nodeType = null;
150         this.nodeIDString = null;
151     }
152
153     /**
154      * Constructor for the Node objects, it validate the input so if
155      * the ID passed is not of the type expected accordingly to the
156      * type an exception is raised.
157      *
158      * @param nodeType Type of the node we are building
159      * @param id ID used by the SDN technology to identify the node
160      *
161      */
162     public Node(String nodeType, Object id) throws ConstructionException {
163         if (NodeIDType.getClassType(nodeType) != null &&
164             NodeIDType.getClassType(nodeType).isInstance(id)) {
165             this.nodeType = nodeType;
166             this.nodeID = id;
167         } else {
168             throw new ConstructionException("Type of incoming object:"
169                                             + id.getClass() + " not compatible with expected type:"
170                                             + NodeIDType.getClassType(nodeType));
171         }
172     }
173
174     /**
175      * Copy Constructor for the Node objects.
176      *
177      * @param src type of nodes to copy from
178      *
179      */
180     public Node(Node src) throws ConstructionException {
181         if (src != null) {
182             this.nodeType = src.getType();
183             // Here we can reference the object because that is
184             // supposed to be an immutable identifier as well like a
185             // UUID/Integer and so on, hence no need to create a copy
186             // of it
187             this.nodeID = src.getID();
188         } else {
189             throw
190                 new ConstructionException("Null incoming object to copy from");
191         }
192     }
193
194     /**
195      * getter for node type
196      *
197      *
198      * @return The node Type for this Node object
199      */
200     @XmlElement(name = "type")
201     public String getType() {
202         return this.nodeType;
203     }
204
205     /**
206      * fill the current object from the string parameters passed, will
207      * be only used by JAXB
208      *
209      * @param typeStr string representing the type of the Node
210      * @param IDStr String representation of the ID
211      */
212     private void fillmeFromString(String typeStr, String IDStr) {
213         if (typeStr == null) {
214             return;
215         }
216
217         if (IDStr == null) {
218             return;
219         }
220
221         this.nodeType = typeStr;
222         if (typeStr.equals(NodeIDType.OPENFLOW)) {
223             this.nodeID = Long.valueOf(HexEncode.stringToLong(IDStr));
224         } else if (typeStr.equals(NodeIDType.ONEPK)) {
225             this.nodeID = IDStr;
226         } else if (typeStr.equals(NodeIDType.PCEP)) {
227             this.nodeID = UUID.fromString(IDStr);
228         } else if (typeStr.equals(NodeIDType.PRODUCTION)) {
229             this.nodeID = IDStr;
230         } else {
231             //Use plugin's method to get appropriate conversion from IDStr to nodeID
232             INodeFactory f = (INodeFactory) ServiceHelper
233                     .getGlobalInstance(INodeFactory.class, new Node(), "(protocolName="+typeStr+")");
234             if(f!=null){
235                 Node n = f.fromString(typeStr, IDStr);
236                 this.nodeID = n.nodeID;
237             }
238         }
239     }
240
241     /**
242      * Private setter for nodeType to be called by JAXB not by anyone
243      * else, Node is immutable
244      *
245      * @param type of node to be set
246      */
247     @SuppressWarnings("unused")
248     private void setType(String type) {
249         this.nodeType = type;
250         if (this.nodeIDString != null) {
251             this.fillmeFromString(type, this.nodeIDString);
252         }
253     }
254
255     /**
256      * getter for node ID
257      *
258      *
259      * @return The node ID for this Node object
260      */
261     public Object getID() {
262         return this.nodeID;
263     }
264
265     /**
266      * Getter for the node ID in string format
267      *
268      * @return The nodeID in string format
269      */
270     @XmlElement(name = "id")
271     public String getNodeIDString() {
272         if (this.nodeType.equals(NodeIDType.OPENFLOW)) {
273             return HexEncode.longToHexString((Long) this.nodeID);
274         } else {
275             return this.nodeID.toString();
276         }
277     }
278
279     /**
280      * private setter to be used by JAXB
281      *
282      * @param nodeIDString String representation for NodeID
283      */
284     @SuppressWarnings("unused")
285     private void setNodeIDString(String nodeIDString) {
286         this.nodeIDString = nodeIDString;
287         if (this.nodeType != null) {
288             this.fillmeFromString(this.nodeType, nodeIDString);
289         }
290     }
291
292     @Override
293     public int hashCode() {
294         final int prime = 31;
295         int result = 1;
296         result = prime * result + ((nodeID == null) ? 0 : nodeID.hashCode());
297         result = prime * result
298                 + ((nodeType == null) ? 0 : nodeType.hashCode());
299         return result;
300     }
301
302     @Override
303     public boolean equals(Object obj) {
304         if (this == obj)
305             return true;
306         if (obj == null)
307             return false;
308         if (getClass() != obj.getClass())
309             return false;
310         Node other = (Node) obj;
311         if (nodeID == null) {
312             if (other.nodeID != null)
313                 return false;
314         } else if (!nodeID.equals(other.nodeID))
315             return false;
316         if (nodeType == null) {
317             if (other.nodeType != null)
318                 return false;
319         } else if (!nodeType.equals(other.nodeType))
320             return false;
321         return true;
322     }
323
324     @Override
325     public String toString() {
326         if (this.nodeType.equals(NodeIDType.OPENFLOW)) {
327             return this.nodeType + "|"
328                 + HexEncode.longToHexString((Long) this.nodeID);
329         } else {
330             return this.nodeType + "|" + this.nodeID.toString();
331         }
332     }
333
334     /**
335      * Static method to get back a Node from a string
336      *
337      * @param str string formatted in toString mode that can be
338      * converted back to a Node format.
339      *
340      * @return a Node if succed or null if no
341      */
342     public static Node fromString(String str) {
343         if (str == null) {
344             return null;
345         }
346
347         String parts[] = str.split("\\|");
348         if (parts.length != 2) {
349             // Try to guess from a String formatted as a long because
350             // for long time openflow has been prime citizen so lets
351             // keep this legacy for now
352             String numStr = str.toUpperCase();
353
354             Long ofNodeID = null;
355             if (numStr.startsWith("0X")) {
356                 // Try as an hex number
357                 try {
358                     BigInteger b = new BigInteger(
359                         numStr.replaceFirst("0X", ""), 16);
360                     ofNodeID = Long.valueOf(b.longValue());
361                 } catch (Exception ex) {
362                     ofNodeID = null;
363                 }
364             } else {
365                 // Try as a decimal number
366                 try {
367                     BigInteger b = new BigInteger(numStr);
368                     ofNodeID = Long.valueOf(b.longValue());
369                 } catch (Exception ex) {
370                     ofNodeID = null;
371                 }
372             }
373
374             // Startegy #3 parse as HexLong
375             if (ofNodeID == null) {
376                 try {
377                     ofNodeID = Long.valueOf(HexEncode.stringToLong(numStr));
378                 } catch (Exception ex) {
379                     ofNodeID = null;
380                 }
381             }
382
383             // We ran out of ideas ... return null
384             if (ofNodeID == null) {
385                 return null;
386             }
387
388             // Lets return the cooked up NodeID
389             try {
390                 return new Node(NodeIDType.OPENFLOW, ofNodeID);
391             } catch (ConstructionException ex) {
392                 return null;
393             }
394         }
395
396         String typeStr = parts[0];
397         String IDStr = parts[1];
398
399         return fromString(typeStr, IDStr);
400     }
401
402     /**
403      * Static method to get back a Node from a pair of strings, the
404      * first one being the Type representation, the second one being
405      * the ID string representation, expected to be heavily used in
406      * northbound API.
407      *
408      * @param type, the type of the node we are parsing
409      * @param id, the string representation of the node id
410      *
411      * @return a Node if succed or null if no
412      */
413     public static Node fromString(String typeStr, String IDStr) {
414         if (typeStr == null) {
415             return null;
416         }
417
418         if (IDStr == null) {
419             return null;
420         }
421
422         if (typeStr.equals(NodeIDType.OPENFLOW)) {
423             try {
424                 Long ID = Long.valueOf(HexEncode.stringToLong(IDStr));
425                 return new Node(typeStr, ID);
426             } catch (Exception ex) {
427                 return null;
428             }
429         } else if (typeStr.equals(NodeIDType.ONEPK)) {
430             try {
431                 return new Node(typeStr, IDStr);
432             } catch (Exception ex) {
433                 return null;
434             }
435         } else if (typeStr.equals(NodeIDType.PCEP)) {
436             try {
437                 UUID ID = UUID.fromString(IDStr);
438                 return new Node(typeStr, ID);
439             } catch (Exception ex) {
440                 return null;
441             }
442         } else if (typeStr.equals(NodeIDType.PRODUCTION)) {
443             try {
444                 return new Node(typeStr, IDStr);
445             } catch (Exception ex) {
446                 return null;
447             }
448         } else {
449             //Use INodeFactory to create a Node of registered Node type.
450             //The protocol plugin being used depends on typeStr.
451             INodeFactory f = (INodeFactory) ServiceHelper
452                     .getGlobalInstance(INodeFactory.class, new Node(), "(protocolName="+typeStr+")");
453             if(f==null)
454                 return null;
455             return f.fromString(typeStr, IDStr);
456         }
457     }
458 }