Merge "Fixing OF Multipart messages 1) So we have a MultipartRequestDesc message...
[openflowjava.git] / openflow-protocol-impl / src / main / java / org / opendaylight / openflowjava / protocol / impl / connection / ConnectionAdapterImpl.java
1 /* Copyright (C)2013 Pantheon Technologies, s.r.o. All rights reserved. */
2
3 package org.opendaylight.openflowjava.protocol.impl.connection;
4
5 import io.netty.channel.Channel;
6 import io.netty.channel.ChannelFuture;
7 import io.netty.util.concurrent.GenericFutureListener;
8
9 import java.util.Collection;
10 import java.util.Collections;
11 import java.util.List;
12 import java.util.concurrent.Future;
13 import java.util.concurrent.TimeUnit;
14
15 import org.opendaylight.controller.sal.common.util.RpcErrors;
16 import org.opendaylight.controller.sal.common.util.Rpcs;
17 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionReadyListener;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierInput;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierOutput;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoInput;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoOutput;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoReplyInput;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.EchoRequestMessage;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.ErrorMessage;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.ExperimenterInput;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.ExperimenterMessage;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowModInput;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemovedMessage;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetAsyncInput;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetAsyncOutput;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetConfigInput;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetConfigOutput;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetQueueConfigInput;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetQueueConfigOutput;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GroupModInput;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MeterModInput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReplyMessage;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartRequestMessage;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OpenflowProtocolListener;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PacketInMessage;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PacketOutInput;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortModInput;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortStatusMessage;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestInput;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.SendMultipartRequestMessageInput;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.SetAsyncInput;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.SetConfigInput;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.TableModInput;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.DisconnectEvent;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SwitchIdleEvent;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.system.rev130927.SystemNotificationsListener;
58 import org.opendaylight.yangtools.yang.binding.DataContainer;
59 import org.opendaylight.yangtools.yang.binding.DataObject;
60 import org.opendaylight.yangtools.yang.binding.Notification;
61 import org.opendaylight.yangtools.yang.common.RpcError;
62 import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity;
63 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
64 import org.opendaylight.yangtools.yang.common.RpcResult;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67
68 import com.google.common.cache.Cache;
69 import com.google.common.cache.CacheBuilder;
70 import com.google.common.cache.RemovalListener;
71 import com.google.common.cache.RemovalNotification;
72 import com.google.common.collect.Lists;
73 import com.google.common.util.concurrent.SettableFuture;
74
75 /**
76  * Handles messages (notifications + rpcs) and connections
77  * @author mirehak
78  * @author michal.polkorab
79  */
80 public class ConnectionAdapterImpl implements ConnectionFacade {
81
82     /** after this time, rpc future response objects will be thrown away (in minutes) */
83     public static final int RPC_RESPONSE_EXPIRATION = 1;
84
85     protected static final Logger LOG = LoggerFactory
86             .getLogger(ConnectionAdapterImpl.class);
87
88     private static final String APPLICATION_TAG = "OPENFLOW_LIBRARY";
89     private static final String TAG = "OPENFLOW";
90     private Channel channel;
91     private OpenflowProtocolListener messageListener;
92     /** expiring cache for future rpcResponses */
93     protected Cache<RpcResponseKey, SettableFuture<?>> responseCache;
94     private SystemNotificationsListener systemListener;
95     private boolean disconnectOccured = false;
96
97     protected ConnectionReadyListener connectionReadyListener;
98
99     /**
100      * default ctor
101      */
102     public ConnectionAdapterImpl() {
103         responseCache = CacheBuilder.newBuilder()
104                 .concurrencyLevel(1)
105                 .expireAfterWrite(RPC_RESPONSE_EXPIRATION, TimeUnit.MINUTES)
106                 .removalListener(new ResponseRemovalListener()).build();
107         LOG.debug("ConnectionAdapter created");
108     }
109
110     /**
111      * @param channel the channel to be set - used for communication
112      */
113     public void setChannel(Channel channel) {
114         this.channel = channel;
115     }
116
117     @Override
118     public Future<RpcResult<BarrierOutput>> barrier(BarrierInput input) {
119         return sendToSwitchExpectRpcResultFuture(
120                 input, BarrierOutput.class, "barrier-input sending failed");
121     }
122
123     @Override
124     public Future<RpcResult<EchoOutput>> echo(EchoInput input) {
125         return sendToSwitchExpectRpcResultFuture(
126                 input, EchoOutput.class, "echo-input sending failed");
127     }
128
129     @Override
130     public Future<RpcResult<Void>> echoReply(EchoReplyInput input) {
131         return sendToSwitchFuture(input, "echo-reply sending failed");
132     }
133
134     @Override
135     public Future<RpcResult<Void>> experimenter(ExperimenterInput input) {
136         return sendToSwitchFuture(input, "experimenter sending failed");
137     }
138
139     @Override
140     public Future<RpcResult<Void>> flowMod(FlowModInput input) {
141         return sendToSwitchFuture(input, "flow-mod sending failed");
142     }
143
144     @Override
145     public Future<RpcResult<GetConfigOutput>> getConfig(GetConfigInput input) {
146         return sendToSwitchExpectRpcResultFuture(
147                 input, GetConfigOutput.class, "get-config-input sending failed");
148     }
149
150     @Override
151     public Future<RpcResult<GetFeaturesOutput>> getFeatures(
152             GetFeaturesInput input) {
153         return sendToSwitchExpectRpcResultFuture(
154                 input, GetFeaturesOutput.class, "get-features-input sending failed");
155     }
156
157     @Override
158     public Future<RpcResult<GetQueueConfigOutput>> getQueueConfig(
159             GetQueueConfigInput input) {
160         return sendToSwitchExpectRpcResultFuture(
161                 input, GetQueueConfigOutput.class, "get-queue-config-input sending failed");
162     }
163
164     @Override
165     public Future<RpcResult<Void>> groupMod(GroupModInput input) {
166         return sendToSwitchFuture(input, "group-mod-input sending failed");
167     }
168
169     @Override
170     public Future<RpcResult<Void>> hello(HelloInput input) {
171         return sendToSwitchFuture(input, "hello-input sending failed");
172     }
173
174     @Override
175     public Future<RpcResult<Void>> meterMod(MeterModInput input) {
176         return sendToSwitchFuture(input, "meter-mod-input sending failed");
177     }
178
179     @Override
180     public Future<RpcResult<Void>> packetOut(PacketOutInput input) {
181         return sendToSwitchFuture(input, "packet-out-input sending failed");
182     }
183
184     @Override
185     public Future<RpcResult<Void>> sendMultipartRequestMessage(SendMultipartRequestMessageInput input) {
186         return sendToSwitchFuture(input, "multi-part-request sending failed");
187     }
188
189     @Override
190     public Future<RpcResult<Void>> portMod(PortModInput input) {
191         return sendToSwitchFuture(input, "port-mod-input sending failed");
192     }
193
194     @Override
195     public Future<RpcResult<RoleRequestOutput>> roleRequest(
196             RoleRequestInput input) {
197         return sendToSwitchExpectRpcResultFuture(
198                 input, RoleRequestOutput.class, "role-request-config-input sending failed");
199     }
200
201     @Override
202     public Future<RpcResult<Void>> setConfig(SetConfigInput input) {
203         return sendToSwitchFuture(input, "set-config-input sending failed");
204     }
205
206     @Override
207     public Future<RpcResult<Void>> tableMod(TableModInput input) {
208         return sendToSwitchFuture(input, "table-mod-input sending failed");
209     }
210
211     @Override
212     public Future<RpcResult<GetAsyncOutput>> getAsync(GetAsyncInput input) {
213         return sendToSwitchExpectRpcResultFuture(
214                 input, GetAsyncOutput.class, "get-async-input sending failed");
215     }
216
217     @Override
218     public Future<RpcResult<Void>> setAsync(SetAsyncInput input) {
219         return sendToSwitchFuture(input, "set-async-input sending failed");
220     }
221
222     @Override
223     public Future<Boolean> disconnect() {
224         ChannelFuture disconnectResult = channel.disconnect();
225         responseCache.invalidateAll();
226         disconnectOccured = true;
227
228         String failureInfo = "switch disconnecting failed";
229         ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
230         String message = "Check the switch connection";
231         return handleTransportChannelFuture(disconnectResult, failureInfo, errorSeverity, message);
232     }
233
234     @Override
235     public boolean isAlive() {
236         return channel.isOpen();
237     }
238
239     @Override
240     public void setMessageListener(OpenflowProtocolListener messageListener) {
241         this.messageListener = messageListener;
242     }
243
244     @Override
245     public void consume(DataObject message) {
246         LOG.debug("ConsumeIntern msg");
247         if (disconnectOccured ) {
248             return;
249         }
250         if (message instanceof Notification) {
251             // System events
252             if (message instanceof DisconnectEvent) {
253                 systemListener.onDisconnectEvent((DisconnectEvent) message);
254                 responseCache.invalidateAll();
255                 disconnectOccured = true;
256             } else if (message instanceof SwitchIdleEvent) {
257                 systemListener.onSwitchIdleEvent((SwitchIdleEvent) message);
258             }
259             // OpenFlow messages
260               else if (message instanceof EchoRequestMessage) {
261                 messageListener.onEchoRequestMessage((EchoRequestMessage) message);
262             } else if (message instanceof ErrorMessage) {
263                 messageListener.onErrorMessage((ErrorMessage) message);
264             } else if (message instanceof ExperimenterMessage) {
265                 messageListener.onExperimenterMessage((ExperimenterMessage) message);
266             } else if (message instanceof FlowRemovedMessage) {
267                 messageListener.onFlowRemovedMessage((FlowRemovedMessage) message);
268             } else if (message instanceof HelloMessage) {
269                 LOG.info("Hello received / branch");
270                 messageListener.onHelloMessage((HelloMessage) message);
271             } else if (message instanceof MultipartReplyMessage) {
272                 messageListener.onMultipartReplyMessage((MultipartReplyMessage) message);
273             } else if (message instanceof MultipartRequestMessage) {
274                 messageListener.onMultipartRequestMessage((MultipartRequestMessage) message);
275             } else if (message instanceof PacketInMessage) {
276                 messageListener.onPacketInMessage((PacketInMessage) message);
277             } else if (message instanceof PortStatusMessage) {
278                 messageListener.onPortStatusMessage((PortStatusMessage) message);
279             } else {
280                 LOG.warn("message listening not supported for type: "+message.getClass());
281             }
282         } else {
283             if (message instanceof OfHeader) {
284                 LOG.debug("OFheader msg received");
285                 RpcResponseKey key = createRpcResponseKey((OfHeader) message);
286                 final SettableFuture<RpcResult<?>> rpcFuture = findRpcResponse(key);
287                 if (rpcFuture != null) {
288                     LOG.debug("corresponding rpcFuture found");
289                     List<RpcError> errors = Collections.emptyList();
290                     LOG.debug("before setting rpcFuture");
291                     rpcFuture.set(Rpcs.getRpcResult(true, message, errors));
292                     LOG.debug("after setting rpcFuture");
293                     responseCache.invalidate(key);
294                 } else {
295                     LOG.warn("received unexpected rpc response: "+key);
296                 }
297             } else {
298                 LOG.warn("message listening not supported for type: "+message.getClass());
299             }
300         }
301     }
302
303     /**
304      * sends given message to switch, sending result will be reported via return value
305      * @param input message to send
306      * @param failureInfo describes, what type of message caused failure by sending
307      * @return future object, <ul>
308      *  <li>if send successful, {@link RpcResult} without errors and successful
309      *  status will be returned, </li>
310      *  <li>else {@link RpcResult} will contain errors and failed status</li>
311      *  </ul>
312      */
313     private SettableFuture<RpcResult<Void>> sendToSwitchFuture(
314             DataObject input, final String failureInfo) {
315         LOG.debug("going to flush");
316         ChannelFuture resultFuture = channel.writeAndFlush(input);
317         LOG.debug("flushed");
318
319         ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
320         String errorMessage = "check switch connection";
321         return handleRpcChannelFuture(resultFuture, failureInfo, errorSeverity, errorMessage);
322     }
323
324     /**
325      * sends given message to switch, sending result or switch response will be reported via return value
326      * @param input message to send
327      * @param responseClazz type of response
328      * @param failureInfo describes, what type of message caused failure by sending
329      * @return future object, <ul>
330      *  <li>if send fails, {@link RpcResult} will contain errors and failed status </li>
331      *  <li>else {@link RpcResult} will be stored in responseCache and wait for particular timeout
332      *  ({@link ConnectionAdapterImpl#RPC_RESPONSE_EXPIRATION}),
333      *  <ul><li>either switch will manage to answer
334      *  and then corresponding response message will be set into returned future</li>
335      *  <li>or response in cache will expire and returned future will be cancelled</li></ul>
336      *  </li>
337      *  </ul>
338      */
339     private <IN extends OfHeader, OUT extends OfHeader> SettableFuture<RpcResult<OUT>> sendToSwitchExpectRpcResultFuture(
340             IN input, Class<OUT> responseClazz, final String failureInfo) {
341         LOG.debug("going to flush");
342         SettableFuture<RpcResult<OUT>> rpcResult = SettableFuture.create();
343         RpcResponseKey key = new RpcResponseKey(input.getXid(), responseClazz);
344         responseCache.put(key, rpcResult);
345         ChannelFuture resultFuture = channel.writeAndFlush(input);
346         LOG.debug("flushed");
347
348         ErrorSeverity errorSeverity = ErrorSeverity.ERROR;
349         String errorMessage = "check switch connection";
350
351         return handleRpcChannelFutureWithResponse(resultFuture, failureInfo, errorSeverity,
352                 errorMessage, input, responseClazz, rpcResult, key);
353     }
354
355     /**
356      * @param resultFuture
357      * @param failureInfo
358      * @return
359      */
360     private SettableFuture<RpcResult<Void>> handleRpcChannelFuture(
361             ChannelFuture resultFuture, final String failureInfo,
362             final ErrorSeverity errorSeverity, final String errorMessage) {
363
364         final SettableFuture<RpcResult<Void>> rpcResult = SettableFuture.create();
365         LOG.debug("handlerpcchannelfuture");
366         resultFuture.addListener(new GenericFutureListener<io.netty.util.concurrent.Future<? super Void>>() {
367
368             @Override
369             public void operationComplete(
370                     io.netty.util.concurrent.Future<? super Void> future)
371                     throws Exception {
372                 LOG.debug("operation complete");
373                 Collection<RpcError> errors = Collections.emptyList();
374
375                 if (future.cause() != null) {
376                     LOG.debug("future.cause != null");
377                     RpcError rpcError = buildRpcError(failureInfo,
378                             errorSeverity, errorMessage, future.cause());
379                     errors = Lists.newArrayList(rpcError);
380                 }
381
382                 rpcResult.set(Rpcs.getRpcResult(
383                         future.isSuccess(),
384                         (Void) null,
385                         errors)
386                 );
387             }
388         });
389         return rpcResult;
390     }
391
392     /**
393      * @param resultFuture
394      * @param failureInfo
395      * @param errorSeverity
396      * @param errorMessage
397      * @param input
398      * @param responseClazz
399      * @param key of rpcResponse
400      * @return
401      */
402     private <IN extends OfHeader, OUT extends OfHeader> SettableFuture<RpcResult<OUT>> handleRpcChannelFutureWithResponse(
403             ChannelFuture resultFuture, final String failureInfo,
404             final ErrorSeverity errorSeverity, final String errorMessage,
405             final IN input, Class<OUT> responseClazz, final SettableFuture<RpcResult<OUT>> rpcResult, final RpcResponseKey key) {
406         LOG.debug("handleRpcchanfuture with response");
407
408         resultFuture.addListener(new GenericFutureListener<io.netty.util.concurrent.Future<? super Void>>() {
409
410             @Override
411             public void operationComplete(
412                     io.netty.util.concurrent.Future<? super Void> future)
413                     throws Exception {
414
415                 LOG.debug("operation complete");
416                 Collection<RpcError> errors = Collections.emptyList();
417                 if (future.cause() != null) {
418                     LOG.debug("ChannelFuture.cause != null");
419                     RpcError rpcError = buildRpcError(failureInfo,
420                             errorSeverity, errorMessage, future.cause());
421                     errors = Lists.newArrayList(rpcError);
422                     rpcResult.set(Rpcs.getRpcResult(
423                             future.isSuccess(),
424                             (OUT) null,
425                             errors)
426                             );
427                     responseCache.invalidate(key);
428                 } else {
429                     LOG.debug("ChannelFuture.cause == null");
430                     if (responseCache.getIfPresent(key) == null) {
431                         LOG.debug("responcache: key wasn't present");
432                     }
433                 }
434             }
435         });
436         return rpcResult;
437     }
438
439     /**
440      * @param resultFuture
441      * @param failureInfo
442      * @param errorSeverity
443      * @param message
444      * @return
445      */
446     private static SettableFuture<Boolean> handleTransportChannelFuture(
447             ChannelFuture resultFuture, final String failureInfo,
448             final ErrorSeverity errorSeverity, final String message) {
449
450         final SettableFuture<Boolean> transportResult = SettableFuture.create();
451
452         resultFuture.addListener(new GenericFutureListener<io.netty.util.concurrent.Future<? super Void>>() {
453
454             @Override
455             public void operationComplete(
456                     io.netty.util.concurrent.Future<? super Void> future)
457                     throws Exception {
458                 transportResult.set(future.isSuccess());
459                 if (!future.isSuccess()) {
460                     transportResult.setException(future.cause());
461                 }
462             }
463         });
464         return transportResult;
465     }
466
467     /**
468      * @param cause
469      * @return
470      */
471     protected RpcError buildRpcError(String info, ErrorSeverity severity, String message,
472             Throwable cause) {
473         RpcError error = RpcErrors.getRpcError(APPLICATION_TAG, TAG, info, severity, message,
474                 ErrorType.RPC, cause);
475         return error;
476     }
477
478     /**
479      * @param cause
480      * @return
481      */
482     protected RpcError buildTransportError(String info, ErrorSeverity severity, String message,
483             Throwable cause) {
484         RpcError error = RpcErrors.getRpcError(APPLICATION_TAG, TAG, info, severity, message,
485                 ErrorType.TRANSPORT, cause);
486         return error;
487     }
488
489     /**
490      * @param message
491      * @return
492      */
493     private static RpcResponseKey createRpcResponseKey(OfHeader message) {
494         return new RpcResponseKey(message.getXid(), message.getClass());
495     }
496
497     /**
498      * @return
499      */
500     @SuppressWarnings("unchecked")
501     private SettableFuture<RpcResult<?>> findRpcResponse(RpcResponseKey key) {
502         return (SettableFuture<RpcResult<?>>) responseCache.getIfPresent(key);
503     }
504
505     @Override
506     public void setSystemListener(SystemNotificationsListener systemListener) {
507         this.systemListener = systemListener;
508     }
509
510     @Override
511     public void checkListeners() {
512         StringBuffer buffer =  new StringBuffer();
513         if (systemListener == null) {
514             buffer.append("SystemListener ");
515         }
516         if (messageListener == null) {
517             buffer.append("MessageListener ");
518         }
519         if (connectionReadyListener == null) {
520             buffer.append("ConnectionReadyListener ");
521         }
522
523         if (buffer.length() > 0) {
524             throw new IllegalStateException("Missing listeners: " + buffer.toString());
525         }
526     }
527
528     static class ResponseRemovalListener implements RemovalListener<RpcResponseKey, SettableFuture<?>> {
529         @Override
530         public void onRemoval(
531                 RemovalNotification<RpcResponseKey, SettableFuture<?>> notification) {
532             SettableFuture<?> future = notification.getValue();
533             if (!future.isDone()) {
534                 LOG.warn("rpc response discarded: " + notification.getKey());
535                 future.cancel(true);
536             }
537         }
538     }
539
540     /**
541      * Class is used ONLY for exiting msgQueue processing thread
542      * @author michal.polkorab
543      */
544     static class ExitingDataObject implements DataObject {
545         @Override
546         public Class<? extends DataContainer> getImplementedInterface() {
547             return null;
548         }
549     }
550
551     @Override
552     public void fireConnectionReadyNotification() {
553         new Thread(new Runnable() {
554             @Override
555             public void run() {
556                 connectionReadyListener.onConnectionReady();
557             }
558         }).start();
559     }
560
561
562     @Override
563     public void setConnectionReadyListener(
564             ConnectionReadyListener connectionReadyListener) {
565         this.connectionReadyListener = connectionReadyListener;
566     }
567
568 }