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