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