Revisit Exception handling in BitBufferHelper and Packet
[controller.git] / opendaylight / protocol_plugins / openflow / src / main / java / org / opendaylight / controller / protocol_plugin / openflow / internal / FlowProgrammerService.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.protocol_plugin.openflow.internal;
10
11 import java.nio.ByteBuffer;
12 import java.util.ArrayList;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ConcurrentMap;
19
20 import org.eclipse.osgi.framework.console.CommandInterpreter;
21 import org.eclipse.osgi.framework.console.CommandProvider;
22 import org.opendaylight.controller.protocol_plugin.openflow.IFlowProgrammerNotifier;
23 import org.opendaylight.controller.protocol_plugin.openflow.IInventoryShimExternalListener;
24 import org.opendaylight.controller.protocol_plugin.openflow.core.IController;
25 import org.opendaylight.controller.protocol_plugin.openflow.core.IMessageListener;
26 import org.opendaylight.controller.protocol_plugin.openflow.core.ISwitch;
27 import org.opendaylight.controller.protocol_plugin.openflow.vendorextension.v6extension.V6Error;
28 import org.openflow.protocol.OFError;
29 import org.openflow.protocol.OFFlowMod;
30 import org.openflow.protocol.OFFlowRemoved;
31 import org.openflow.protocol.OFMessage;
32 import org.openflow.protocol.OFPort;
33 import org.openflow.protocol.OFType;
34 import org.openflow.protocol.action.OFAction;
35
36 import org.opendaylight.controller.sal.core.ContainerFlow;
37 import org.opendaylight.controller.sal.core.IContainerListener;
38 import org.opendaylight.controller.sal.core.Node;
39 import org.opendaylight.controller.sal.core.Node.NodeIDType;
40 import org.opendaylight.controller.sal.core.NodeConnector;
41 import org.opendaylight.controller.sal.core.Property;
42 import org.opendaylight.controller.sal.core.UpdateType;
43 import org.opendaylight.controller.sal.flowprogrammer.Flow;
44 import org.opendaylight.controller.sal.flowprogrammer.IPluginInFlowProgrammerService;
45 import org.opendaylight.controller.sal.match.Match;
46 import org.opendaylight.controller.sal.match.MatchType;
47 import org.opendaylight.controller.sal.utils.GlobalConstants;
48 import org.opendaylight.controller.sal.utils.HexEncode;
49 import org.opendaylight.controller.sal.utils.NodeCreator;
50 import org.opendaylight.controller.sal.utils.StatusCode;
51 import org.opendaylight.controller.sal.utils.Status;
52 import org.osgi.framework.BundleContext;
53 import org.osgi.framework.FrameworkUtil;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
58  * Represents the openflow plugin component in charge of programming the flows
59  * the flow programming and relay them to functional modules above SAL.
60  */
61 public class FlowProgrammerService implements IPluginInFlowProgrammerService,
62         IMessageListener, IContainerListener, IInventoryShimExternalListener,
63         CommandProvider {
64     private static final Logger log = LoggerFactory
65             .getLogger(FlowProgrammerService.class);
66     private IController controller;
67     private ConcurrentMap<String, IFlowProgrammerNotifier> flowProgrammerNotifiers;
68     private Map<String, Set<NodeConnector>> containerToNc;
69     private ConcurrentMap<Long, Map<Integer, Long>> xid2rid;
70     private int barrierMessagePriorCount = getBarrierMessagePriorCount();
71
72     public FlowProgrammerService() {
73         controller = null;
74         flowProgrammerNotifiers = new ConcurrentHashMap<String, IFlowProgrammerNotifier>();
75         containerToNc = new HashMap<String, Set<NodeConnector>>();
76         xid2rid = new ConcurrentHashMap<Long, Map<Integer, Long>>();
77     }
78
79     public void setController(IController core) {
80         this.controller = core;
81     }
82
83     public void unsetController(IController core) {
84         if (this.controller == core) {
85             this.controller = null;
86         }
87     }
88
89     public void setFlowProgrammerNotifier(Map<String, ?> props,
90             IFlowProgrammerNotifier s) {
91         if (props == null || props.get("containerName") == null) {
92             log.error("Didn't receive the service correct properties");
93             return;
94         }
95         String containerName = (String) props.get("containerName");
96         this.flowProgrammerNotifiers.put(containerName, s);
97     }
98
99     public void unsetFlowProgrammerNotifier(Map<String, ?> props,
100             IFlowProgrammerNotifier s) {
101         if (props == null || props.get("containerName") == null) {
102             log.error("Didn't receive the service correct properties");
103             return;
104         }
105         String containerName = (String) props.get("containerName");
106         if (this.flowProgrammerNotifiers != null
107                 && this.flowProgrammerNotifiers.containsKey(containerName)
108                 && this.flowProgrammerNotifiers.get(containerName) == s) {
109             this.flowProgrammerNotifiers.remove(containerName);
110         }
111     }
112
113     /**
114      * Function called by the dependency manager when all the required
115      * dependencies are satisfied
116      * 
117      */
118     void init() {
119         this.controller.addMessageListener(OFType.FLOW_REMOVED, this);
120         registerWithOSGIConsole();
121     }
122
123     /**
124      * Function called by the dependency manager when at least one dependency
125      * become unsatisfied or when the component is shutting down because for
126      * example bundle is being stopped.
127      * 
128      */
129     void destroy() {
130     }
131
132     /**
133      * Function called by dependency manager after "init ()" is called and after
134      * the services provided by the class are registered in the service registry
135      * 
136      */
137     void start() {
138     }
139
140     /**
141      * Function called by the dependency manager before the services exported by
142      * the component are unregistered, this will be followed by a "destroy ()"
143      * calls
144      * 
145      */
146     void stop() {
147     }
148
149     @Override
150     public Status addFlow(Node node, Flow flow) {
151         return addFlowInternal(node, flow, 0);
152     }
153
154     @Override
155     public Status modifyFlow(Node node, Flow oldFlow, Flow newFlow) {
156         return modifyFlowInternal(node, oldFlow, newFlow, 0);
157     }
158
159     @Override
160     public Status removeFlow(Node node, Flow flow) {
161         return removeFlowInternal(node, flow, 0);
162     }
163
164     @Override
165     public Status addFlowAsync(Node node, Flow flow, long rid) {
166         return addFlowInternal(node, flow, rid);
167     }
168
169     @Override
170     public Status modifyFlowAsync(Node node, Flow oldFlow, Flow newFlow,
171             long rid) {
172         return modifyFlowInternal(node, oldFlow, newFlow, rid);
173     }
174
175     @Override
176     public Status removeFlowAsync(Node node, Flow flow, long rid) {
177         return removeFlowInternal(node, flow, rid);
178     }
179
180     private Status addFlowInternal(Node node, Flow flow, long rid) {
181         String action = "add";
182         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
183             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
184                     action, "Invalid node type"));
185         }
186
187         if (controller != null) {
188             ISwitch sw = controller.getSwitch((Long) node.getID());
189             if (sw != null) {
190                 FlowConverter x = new FlowConverter(flow);
191                 OFMessage msg = x.getOFFlowMod(OFFlowMod.OFPFC_ADD, null);
192
193                 Object result;
194                 if (rid == 0) {
195                     /*
196                      * Synchronous message send. Each message is followed by a
197                      * Barrier message.
198                      */
199                     result = sw.syncSend(msg);
200                 } else {
201                     /*
202                      * Message will be sent asynchronously. A Barrier message
203                      * will be inserted automatically to synchronize the
204                      * progression.
205                      */
206                     result = asyncMsgSend(node, sw, msg, rid);  
207                 }
208                 if (result instanceof Boolean) {
209                     return ((Boolean) result == Boolean.TRUE) ? new Status(
210                             StatusCode.SUCCESS, null) : new Status(
211                             StatusCode.TIMEOUT, errorString(null, action,
212                                     "Request Timed Out"));
213                 } else if (result instanceof OFError) {
214                     OFError res = (OFError) result;
215                     if (res.getErrorType() == V6Error.NICIRA_VENDOR_ERRORTYPE) {
216                         V6Error er = new V6Error(res);
217                         byte[] b = res.getError();
218                         ByteBuffer bb = ByteBuffer.allocate(b.length);
219                         bb.put(b);
220                         bb.rewind();
221                         er.readFrom(bb);
222                         return new Status(StatusCode.INTERNALERROR,
223                                 errorString("program", action,
224                                         "Vendor Extension Internal Error"));
225                     }
226                     return new Status(StatusCode.INTERNALERROR, errorString(
227                             "program", action, Utils.getOFErrorString(res)));
228                 } else {
229                     return new Status(StatusCode.INTERNALERROR, errorString(
230                             "send", action, "Internal Error"));
231                 }
232             } else {
233                 return new Status(StatusCode.GONE, errorString("send", action,
234                         "Switch is not available"));
235             }
236         }
237         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
238                 "Internal plugin error"));
239     }
240
241     private Status modifyFlowInternal(Node node, Flow oldFlow, Flow newFlow, long rid) {
242         String action = "modify";
243         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
244             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
245                     action, "Invalid node type"));
246         }
247         if (controller != null) {
248             ISwitch sw = controller.getSwitch((Long) node.getID());
249             if (sw != null) {
250                 OFMessage msg1 = null, msg2 = null;
251
252                 // If priority and match portion are the same, send a
253                 // modification message
254                 if (oldFlow.getPriority() != newFlow.getPriority()
255                         || !oldFlow.getMatch().equals(newFlow.getMatch())) {
256                     msg1 = new FlowConverter(oldFlow).getOFFlowMod(
257                             OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
258                     msg2 = new FlowConverter(newFlow).getOFFlowMod(
259                             OFFlowMod.OFPFC_ADD, null);
260                 } else {
261                     msg1 = new FlowConverter(newFlow).getOFFlowMod(
262                             OFFlowMod.OFPFC_MODIFY_STRICT, null);
263                 }
264                 /*
265                  * Synchronous message send
266                  */
267                 action = (msg2 == null) ? "modify" : "delete";
268                 Object result;
269                 if (rid == 0) {
270                     /*
271                      * Synchronous message send. Each message is followed by a
272                      * Barrier message.
273                      */
274                     result = sw.syncSend(msg1);
275                 } else {
276                     /*
277                      * Message will be sent asynchronously. A Barrier message
278                      * will be inserted automatically to synchronize the
279                      * progression.
280                      */
281                     result = asyncMsgSend(node, sw, msg1, rid);
282                 }
283                 if (result instanceof Boolean) {
284                     if ((Boolean) result == Boolean.FALSE) {
285                         return new Status(StatusCode.TIMEOUT, errorString(null,
286                                 action, "Request Timed Out"));
287                     } else if (msg2 == null) {
288                         return new Status(StatusCode.SUCCESS, null);
289                     }
290                 } else if (result instanceof OFError) {
291                     return new Status(StatusCode.INTERNALERROR, errorString(
292                             "program", action,
293                             Utils.getOFErrorString((OFError) result)));
294                 } else {
295                     return new Status(StatusCode.INTERNALERROR, errorString(
296                             "send", action, "Internal Error"));
297                 }
298
299                 if (msg2 != null) {
300                     action = "add";
301                     if (rid == 0) {
302                         /*
303                          * Synchronous message send. Each message is followed by a
304                          * Barrier message.
305                          */
306                         result = sw.syncSend(msg2);
307                     } else {
308                         /*
309                          * Message will be sent asynchronously. A Barrier message
310                          * will be inserted automatically to synchronize the
311                          * progression.
312                          */
313                         result = asyncMsgSend(node, sw, msg2, rid);
314                     }
315                     if (result instanceof Boolean) {
316                         return ((Boolean) result == Boolean.TRUE) ? new Status(
317                                 StatusCode.SUCCESS, null) : new Status(
318                                 StatusCode.TIMEOUT, errorString(null, action,
319                                         "Request Timed Out"));
320                     } else if (result instanceof OFError) {
321                         return new Status(StatusCode.INTERNALERROR,
322                                 errorString("program", action, Utils
323                                         .getOFErrorString((OFError) result)));
324                     } else {
325                         return new Status(StatusCode.INTERNALERROR,
326                                 errorString("send", action, "Internal Error"));
327                     }
328                 }
329             } else {
330                 return new Status(StatusCode.GONE, errorString("send", action,
331                         "Switch is not available"));
332             }
333         }
334         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
335                 "Internal plugin error"));
336     }
337
338     private Status removeFlowInternal(Node node, Flow flow, long rid) {
339         String action = "remove";
340         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
341             return new Status(StatusCode.NOTACCEPTABLE, errorString("send",
342                     action, "Invalid node type"));
343         }
344         if (controller != null) {
345             ISwitch sw = controller.getSwitch((Long) node.getID());
346             if (sw != null) {
347                 OFMessage msg = new FlowConverter(flow).getOFFlowMod(
348                         OFFlowMod.OFPFC_DELETE_STRICT, OFPort.OFPP_NONE);
349                 Object result;
350                 if (rid == 0) {
351                     /*
352                      * Synchronous message send. Each message is followed by a
353                      * Barrier message.
354                      */
355                     result = sw.syncSend(msg);
356                 } else {
357                     /*
358                      * Message will be sent asynchronously. A Barrier message
359                      * will be inserted automatically to synchronize the
360                      * progression.
361                      */
362                     result = asyncMsgSend(node, sw, msg, rid);
363                 }
364                 if (result instanceof Boolean) {
365                     return ((Boolean) result == Boolean.TRUE) ? new Status(
366                             StatusCode.SUCCESS, null) : new Status(
367                             StatusCode.TIMEOUT, errorString(null, action,
368                                     "Request Timed Out"));
369                 } else if (result instanceof OFError) {
370                     return new Status(StatusCode.INTERNALERROR, errorString(
371                             "program", action,
372                             Utils.getOFErrorString((OFError) result)));
373                 } else {
374                     return new Status(StatusCode.INTERNALERROR, errorString(
375                             "send", action, "Internal Error"));
376                 }
377             } else {
378                 return new Status(StatusCode.GONE, errorString("send", action,
379                         "Switch is not available"));
380             }
381         }
382         return new Status(StatusCode.INTERNALERROR, errorString("send", action,
383                 "Internal plugin error"));
384     }
385
386     @Override
387     public Status removeAllFlows(Node node) {
388         return new Status(StatusCode.SUCCESS, null);
389     }
390
391     private String errorString(String phase, String action, String cause) {
392         return "Failed to "
393                 + ((phase != null) ? phase + " the " + action
394                         + " flow message: " : action + " the flow: ") + cause;
395     }
396
397     @Override
398     public void receive(ISwitch sw, OFMessage msg) {
399         if (msg instanceof OFFlowRemoved) {
400             handleFlowRemovedMessage(sw, (OFFlowRemoved) msg);
401         }
402     }
403
404     private void handleFlowRemovedMessage(ISwitch sw, OFFlowRemoved msg) {
405         Node node = NodeCreator.createOFNode(sw.getId());
406         Flow flow = new FlowConverter(msg.getMatch(),
407                 new ArrayList<OFAction>(0)).getFlow(node);
408         flow.setPriority(msg.getPriority());
409         flow.setIdleTimeout(msg.getIdleTimeout());
410         flow.setId(msg.getCookie());
411
412         Match match = flow.getMatch();
413         NodeConnector inPort = match.isPresent(MatchType.IN_PORT) ? (NodeConnector) match
414                 .getField(MatchType.IN_PORT).getValue() : null;
415
416         for (Map.Entry<String, IFlowProgrammerNotifier> containerNotifier : flowProgrammerNotifiers
417                 .entrySet()) {
418             String container = containerNotifier.getKey();
419             IFlowProgrammerNotifier notifier = containerNotifier.getValue();
420             /*
421              * Switch only provide us with the match information. For now let's
422              * try to identify the container membership only from the input port
423              * match field. In any case, upper layer consumers can derive
424              * whether the notification was not for them. More sophisticated
425              * filtering can be added later on.
426              */
427             if (inPort == null
428                     || container.equals(GlobalConstants.DEFAULT.toString())
429                     || this.containerToNc.get(container).contains(inPort)) {
430                 notifier.flowRemoved(node, flow);
431             }
432         }
433     }
434
435     @Override
436     public void tagUpdated(String containerName, Node n, short oldTag,
437             short newTag, UpdateType t) {
438
439     }
440
441     @Override
442     public void containerFlowUpdated(String containerName,
443             ContainerFlow previousFlow, ContainerFlow currentFlow, UpdateType t) {
444     }
445
446     @Override
447     public void nodeConnectorUpdated(String containerName, NodeConnector p,
448             UpdateType type) {
449         Set<NodeConnector> target = null;
450
451         switch (type) {
452         case ADDED:
453             if (!containerToNc.containsKey(containerName)) {
454                 containerToNc.put(containerName, new HashSet<NodeConnector>());
455             }
456             containerToNc.get(containerName).add(p);
457             break;
458         case CHANGED:
459             break;
460         case REMOVED:
461             target = containerToNc.get(containerName);
462             if (target != null) {
463                 target.remove(p);
464             }
465             break;
466         default:
467         }
468     }
469
470     @Override
471     public void containerModeUpdated(UpdateType t) {
472
473     }
474
475     @Override
476     public Status sendBarrierMessage(Node node) {
477         if (!node.getType().equals(NodeIDType.OPENFLOW)) {
478             return new Status(StatusCode.NOTACCEPTABLE,
479                     "The node does not support Barrier message.");
480         }
481
482         if (controller != null) {
483             long swid = (Long) node.getID();
484             ISwitch sw = controller.getSwitch(swid);
485             if (sw != null) {
486                 sw.sendBarrierMessage();
487                 clearXid2Rid(swid);
488                 return (new Status(StatusCode.SUCCESS, null));
489             } else {
490                 return new Status(StatusCode.GONE,
491                         "The node does not have a valid Switch reference.");
492             }
493         }
494         return new Status(StatusCode.INTERNALERROR,
495                 "Failed to send Barrier message.");
496     }
497     
498     /**
499      * This method sends the message asynchronously until the number of messages
500      * sent reaches a threshold. Then a Barrier message is sent automatically
501      * for sync purpose. An unique Request ID associated with the message is
502      * passed down by the caller. The Request ID will be returned to the caller
503      * when an error message is received from the switch.
504      * 
505      * @param node
506      *            The node
507      * @param msg
508      *            The switch
509      * @param msg
510      *            The OF message to be sent
511      * @param rid
512      *            The Request Id
513      * @return result
514      */
515     private Object asyncMsgSend(Node node, ISwitch sw, OFMessage msg, long rid) {
516         Object result = Boolean.TRUE;
517         long swid = (Long) node.getID();
518         int xid;
519
520         xid = sw.asyncSend(msg);
521         addXid2Rid(swid, xid, rid);
522         
523         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
524         if (swxid2rid == null) {
525             return result;
526         }
527         
528         int size = swxid2rid.size();
529         if (size % barrierMessagePriorCount == 0) {
530             result = sendBarrierMessage(node);
531         }
532         
533         return result;
534     }
535     
536     /**
537      * A number of async messages are sent followed by a synchronous Barrier
538      * message. This method returns the maximum async messages that can be sent
539      * before the Barrier message.
540      * 
541      * @return The max count of async messages sent prior to Barrier message
542      */
543     private int getBarrierMessagePriorCount() {
544         String count = System.getProperty("of.barrierMessagePriorCount");
545         int rv = 100;
546
547         if (count != null) {
548             try {
549                 rv = Integer.parseInt(count);
550             } catch (Exception e) {
551             }
552         }
553
554         return rv;
555     }
556     
557     /**
558      * This method returns the message Request ID previously assigned by the
559      * caller for a given OF message xid
560      * 
561      * @param swid
562      *            The switch id
563      * @param xid
564      *            The OF message xid
565      * @return The Request ID
566      */
567     public long getMessageRid(long swid, int xid) {
568         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
569         long rid = 0;
570         
571         if (swxid2rid != null) {
572             rid = swxid2rid.get(xid);
573         }
574         return rid;
575     }
576
577     /**
578      * This method returns a copy of outstanding xid to rid mappings.for a given
579      * switch
580      * 
581      * @param swid
582      *            The switch id
583      * @return a copy of xid2rid mappings
584      */
585     public Map<Integer, Long> getSwXid2Rid(long swid) {
586         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
587         
588         if (swxid2rid != null) {
589             return new HashMap<Integer, Long>(swxid2rid);
590         } else {
591             return new HashMap<Integer, Long>();
592         }
593     }
594
595     /**
596      * Adds xid to rid mapping to the local DB
597      * 
598      * @param swid
599      *            The switch id
600      * @param xid
601      *            The OF message xid
602      * @param rid
603      *            The message Request ID
604      */
605     private void addXid2Rid(long swid, int xid, long rid) {
606         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
607         if (swxid2rid != null) {
608             swxid2rid.put(xid, rid);
609         }
610     }
611
612     /**
613      * When an Error message is received, this method will be invoked to remove
614      * the offending xid from the local DB.
615      * 
616      * @param swid
617      *            The switch id
618      * @param xid
619      *            The OF message xid
620      */
621     private void removeXid2Rid(long swid, int xid) {
622         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
623         if (swxid2rid != null) {
624             swxid2rid.remove(xid);
625         }
626     }
627
628     /**
629      * When a Barrier reply is received, this method will be invoked to clear
630      * the local DB
631      * 
632      * @param swid
633      *            The switch id
634      */
635     private void clearXid2Rid(long swid) {
636         Map<Integer, Long> swxid2rid = this.xid2rid.get(swid);
637         if (swxid2rid != null) {
638             swxid2rid.clear();
639         }
640     }
641
642     @Override
643     public void updateNode(Node node, UpdateType type, Set<Property> props) {
644         long swid = (Long)node.getID();
645         
646         switch (type) {
647         case ADDED:
648             Map<Integer, Long> swxid2rid = new HashMap<Integer, Long>();
649             this.xid2rid.put(swid, swxid2rid);
650             break;
651         case CHANGED:
652             break;
653         case REMOVED:
654             this.xid2rid.remove(swid);
655             break;
656         default:
657         }
658     }
659
660     @Override
661     public void updateNodeConnector(NodeConnector nodeConnector,
662             UpdateType type, Set<Property> props) {
663     }
664
665     private void registerWithOSGIConsole() {
666         BundleContext bundleContext = FrameworkUtil.getBundle(this.getClass())
667                 .getBundleContext();
668         bundleContext.registerService(CommandProvider.class.getName(), this,
669                 null);
670     }
671
672     @Override
673     public String getHelp() {
674         StringBuffer help = new StringBuffer();
675         help.append("-- Flow Programmer Service --\n");
676         help.append("\t px2r <node id>          - Print outstanding xid2rid mappings for a given node id\n");
677         help.append("\t px2rc                   - Print max num of async msgs prior to the Barrier\n");
678         return help.toString();
679     }
680
681     public void _px2r(CommandInterpreter ci) {
682         String st = ci.nextArgument();
683         if (st == null) {
684             ci.println("Please enter a valid node id");
685             return;
686         }
687         
688         long sid;
689         try {
690             sid = HexEncode.stringToLong(st);
691         } catch (NumberFormatException e) {
692             ci.println("Please enter a valid node id");
693             return;
694         }
695         
696         Map<Integer, Long> swxid2rid = this.xid2rid.get(sid);
697         if (swxid2rid == null) {
698             ci.println("The node id entered does not exist");
699             return;
700         }
701
702         ci.println("xid             rid");
703         
704         Set<Integer> xidSet = swxid2rid.keySet();
705         if (xidSet == null) {
706             return;
707         }
708
709         for (Integer xid : xidSet) {
710             ci.println(xid + "       " + swxid2rid.get(xid));
711         }
712     }
713
714     public void _px2rc(CommandInterpreter ci) {
715         ci.println("Max num of async messages sent prior to the Barrier message is "
716                 + barrierMessagePriorCount);
717     }
718 }