@@ -140,7 +140,7 @@ public X509Certificate[] getAcceptedIssuers() {
140
140
private SocketFactory socketFactory ;
141
141
private SSLSocketFactory sslSocketFactory ;
142
142
143
- private PacketChannel channel ;
143
+ private volatile PacketChannel channel ;
144
144
private volatile boolean connected ;
145
145
146
146
private ThreadFactory threadFactory ;
@@ -150,9 +150,8 @@ public X509Certificate[] getAcceptedIssuers() {
150
150
private long keepAliveConnectTimeout = TimeUnit .SECONDS .toMillis (3 );
151
151
152
152
private volatile ExecutorService keepAliveThreadExecutor ;
153
- private long keepAliveThreadShutdownTimeout = TimeUnit .SECONDS .toMillis (6 );
154
153
155
- private final Lock shutdownLock = new ReentrantLock ();
154
+ private final Lock connectLock = new ReentrantLock ();
156
155
157
156
/**
158
157
* Alias for BinaryLogClient("localhost", 3306, <no schema> = null, username, password).
@@ -397,69 +396,84 @@ public void setThreadFactory(ThreadFactory threadFactory) {
397
396
* @throws IOException if anything goes wrong while trying to connect
398
397
*/
399
398
public void connect () throws IOException {
400
- if (connected ) {
399
+ if (! connectLock . tryLock () ) {
401
400
throw new IllegalStateException ("BinaryLogClient is already connected" );
402
401
}
403
- GreetingPacket greetingPacket ;
402
+ boolean notifyWhenDisconnected = false ;
404
403
try {
405
404
try {
406
- Socket socket = socketFactory != null ? socketFactory .createSocket () : new Socket ();
407
- socket .connect (new InetSocketAddress (hostname , port ));
408
- channel = new PacketChannel (socket );
409
- if (channel .getInputStream ().peek () == -1 ) {
410
- throw new EOFException ();
405
+ channel = openChannel ();
406
+ GreetingPacket greetingPacket = receiveGreeting ();
407
+ authenticate (greetingPacket );
408
+ connectionId = greetingPacket .getThreadId ();
409
+ if (binlogFilename == null ) {
410
+ fetchBinlogFilenameAndPosition ();
411
411
}
412
+ if (binlogPosition < 4 ) {
413
+ if (logger .isLoggable (Level .WARNING )) {
414
+ logger .warning ("Binary log position adjusted from " + binlogPosition + " to " + 4 );
415
+ }
416
+ binlogPosition = 4 ;
417
+ }
418
+ ChecksumType checksumType = fetchBinlogChecksum ();
419
+ if (checksumType != ChecksumType .NONE ) {
420
+ confirmSupportOfChecksum (checksumType );
421
+ }
422
+ requestBinaryLogStream ();
412
423
} catch (IOException e ) {
413
- throw new IOException ( "Failed to connect to MySQL on " + hostname + ":" + port +
414
- ". Please make sure it's running." , e ) ;
424
+ disconnectChannel ();
425
+ throw e ;
415
426
}
416
- greetingPacket = receiveGreeting ();
417
- authenticate (greetingPacket );
418
- if (binlogFilename == null ) {
419
- fetchBinlogFilenameAndPosition ();
420
- }
421
- if (binlogPosition < 4 ) {
422
- if (logger .isLoggable (Level .WARNING )) {
423
- logger .warning ("Binary log position adjusted from " + binlogPosition + " to " + 4 );
427
+ connected = true ;
428
+ notifyWhenDisconnected = true ;
429
+ if (logger .isLoggable (Level .INFO )) {
430
+ String position ;
431
+ synchronized (gtidSetAccessLock ) {
432
+ position = gtidSet != null ? gtidSet .toString () : binlogFilename + "/" + binlogPosition ;
424
433
}
425
- binlogPosition = 4 ;
434
+ logger .info ("Connected to " + hostname + ":" + port + " at " + position +
435
+ " (" + (blocking ? "sid:" + serverId + ", " : "" ) + "cid:" + connectionId + ")" );
426
436
}
427
- ChecksumType checksumType = fetchBinlogChecksum ();
428
- if (checksumType != ChecksumType .NONE ) {
429
- confirmSupportOfChecksum (checksumType );
437
+ synchronized (lifecycleListeners ) {
438
+ for (LifecycleListener lifecycleListener : lifecycleListeners ) {
439
+ lifecycleListener .onConnect (this );
440
+ }
430
441
}
431
- requestBinaryLogStream ();
432
- } catch (IOException e ) {
433
- if (channel != null && channel .isOpen ()) {
434
- channel .close ();
442
+ if (keepAlive && !isKeepAliveThreadRunning ()) {
443
+ spawnKeepAliveThread ();
435
444
}
436
- throw e ;
437
- }
438
- connected = true ;
439
- connectionId = greetingPacket .getThreadId ();
440
- if (logger .isLoggable (Level .INFO )) {
441
- String position ;
445
+ ensureEventDataDeserializer (EventType .ROTATE , RotateEventDataDeserializer .class );
442
446
synchronized (gtidSetAccessLock ) {
443
- position = gtidSet != null ? gtidSet .toString () : binlogFilename + "/" + binlogPosition ;
447
+ if (gtidSet != null ) {
448
+ ensureEventDataDeserializer (EventType .GTID , GtidEventDataDeserializer .class );
449
+ }
444
450
}
445
- logger .info ("Connected to " + hostname + ":" + port + " at " + position +
446
- " (" + (blocking ? "sid:" + serverId + ", " : "" ) + "cid:" + connectionId + ")" );
447
- }
448
- synchronized (lifecycleListeners ) {
449
- for (LifecycleListener lifecycleListener : lifecycleListeners ) {
450
- lifecycleListener .onConnect (this );
451
+ listenForEventPackets ();
452
+ } finally {
453
+ connectLock .unlock ();
454
+ if (notifyWhenDisconnected ) {
455
+ synchronized (lifecycleListeners ) {
456
+ for (LifecycleListener lifecycleListener : lifecycleListeners ) {
457
+ lifecycleListener .onDisconnect (this );
458
+ }
459
+ }
451
460
}
452
461
}
453
- if (keepAlive && !isKeepAliveThreadRunning ()) {
454
- spawnKeepAliveThread ();
455
- }
456
- ensureEventDataDeserializer (EventType .ROTATE , RotateEventDataDeserializer .class );
457
- synchronized (gtidSetAccessLock ) {
458
- if (gtidSet != null ) {
459
- ensureEventDataDeserializer (EventType .GTID , GtidEventDataDeserializer .class );
462
+ }
463
+
464
+ private PacketChannel openChannel () throws IOException {
465
+ try {
466
+ Socket socket = socketFactory != null ? socketFactory .createSocket () : new Socket ();
467
+ socket .connect (new InetSocketAddress (hostname , port ));
468
+ PacketChannel channel = new PacketChannel (socket );
469
+ if (channel .getInputStream ().peek () == -1 ) {
470
+ throw new EOFException ();
460
471
}
472
+ return channel ;
473
+ } catch (IOException e ) {
474
+ throw new IOException ("Failed to connect to MySQL on " + hostname + ":" + port +
475
+ ". Please make sure it's running." , e );
461
476
}
462
- listenForEventPackets ();
463
477
}
464
478
465
479
private GreetingPacket receiveGreeting () throws IOException {
@@ -540,51 +554,46 @@ private void authenticate(GreetingPacket greetingPacket) throws IOException {
540
554
}
541
555
542
556
private void spawnKeepAliveThread () {
543
- keepAliveThreadExecutor = Executors .newSingleThreadExecutor (new ThreadFactory () {
557
+ final ExecutorService threadExecutor =
558
+ Executors .newSingleThreadExecutor (new ThreadFactory () {
544
559
545
- @ Override
546
- public Thread newThread (Runnable runnable ) {
547
- return newNamedThread (runnable , "blc-keepalive-" + hostname + ":" + port );
548
- }
549
- });
550
- keepAliveThreadExecutor .submit (new Runnable () {
560
+ @ Override
561
+ public Thread newThread (Runnable runnable ) {
562
+ return newNamedThread (runnable , "blc-keepalive-" + hostname + ":" + port );
563
+ }
564
+ });
565
+ threadExecutor .submit (new Runnable () {
551
566
@ Override
552
567
public void run () {
553
- while (true ) {
568
+ while (! threadExecutor . isShutdown () ) {
554
569
try {
555
570
Thread .sleep (keepAliveInterval );
556
571
} catch (InterruptedException e ) {
557
572
// expected in case of disconnect
558
573
}
559
- shutdownLock .lock ();
574
+ if (threadExecutor .isShutdown ()) {
575
+ return ;
576
+ }
560
577
try {
561
- if (keepAliveThreadExecutor .isShutdown ()) {
562
- return ;
578
+ channel .write (new PingCommand ());
579
+ } catch (IOException e ) {
580
+ if (logger .isLoggable (Level .INFO )) {
581
+ logger .info ("Trying to restore lost connection to " + hostname + ":" + port );
563
582
}
564
583
try {
565
- channel .write (new PingCommand ());
566
- } catch (IOException e ) {
567
- if (logger .isLoggable (Level .INFO )) {
568
- logger .info ("Trying to restore lost connection to " + hostname + ":" + port );
569
- }
570
- try {
571
- if (isConnected ()) {
572
- disconnectChannel ();
573
- }
574
- connect (keepAliveConnectTimeout );
575
- } catch (Exception ce ) {
576
- if (logger .isLoggable (Level .WARNING )) {
577
- logger .warning ("Failed to restore connection to " + hostname + ":" + port +
578
- ". Next attempt in " + keepAliveInterval + "ms" );
579
- }
584
+ terminateConnect ();
585
+ connect (keepAliveConnectTimeout );
586
+ } catch (Exception ce ) {
587
+ if (logger .isLoggable (Level .WARNING )) {
588
+ logger .warning ("Failed to restore connection to " + hostname + ":" + port +
589
+ ". Next attempt in " + keepAliveInterval + "ms" );
580
590
}
581
591
}
582
- } finally {
583
- shutdownLock .unlock ();
584
592
}
585
593
}
586
594
}
587
595
});
596
+ keepAliveThreadExecutor = threadExecutor ;
588
597
}
589
598
590
599
private Thread newNamedThread (Runnable runnable , String threadName ) {
@@ -895,7 +904,7 @@ public void registerLifecycleListener(LifecycleListener lifecycleListener) {
895
904
/**
896
905
* Unregister all lifecycle listener of specific type.
897
906
*/
898
- public synchronized void unregisterLifecycleListener (Class <? extends LifecycleListener > listenerClass ) {
907
+ public void unregisterLifecycleListener (Class <? extends LifecycleListener > listenerClass ) {
899
908
synchronized (lifecycleListeners ) {
900
909
Iterator <LifecycleListener > iterator = lifecycleListeners .iterator ();
901
910
while (iterator .hasNext ()) {
@@ -910,7 +919,7 @@ public synchronized void unregisterLifecycleListener(Class<? extends LifecycleLi
910
919
/**
911
920
* Unregister single lifecycle listener.
912
921
*/
913
- public synchronized void unregisterLifecycleListener (LifecycleListener eventListener ) {
922
+ public void unregisterLifecycleListener (LifecycleListener eventListener ) {
914
923
synchronized (lifecycleListeners ) {
915
924
lifecycleListeners .remove (eventListener );
916
925
}
@@ -922,48 +931,49 @@ public synchronized void unregisterLifecycleListener(LifecycleListener eventList
922
931
* As the result following {@link #connect()} resumes client from where it left off.
923
932
*/
924
933
public void disconnect () throws IOException {
925
- shutdownLock . lock ();
926
- try {
927
- if ( isKeepAliveThreadRunning ()) {
928
- keepAliveThreadExecutor . shutdownNow ();
929
- }
930
- disconnectChannel () ;
931
- } finally {
932
- shutdownLock . unlock () ;
934
+ terminateKeepAliveThread ();
935
+ terminateConnect ();
936
+ }
937
+
938
+ private void terminateKeepAliveThread () {
939
+ ExecutorService keepAliveThreadExecutor = this . keepAliveThreadExecutor ;
940
+ if ( keepAliveThreadExecutor == null ) {
941
+ return ;
933
942
}
934
- if (isKeepAliveThreadRunning ()) {
935
- waitForKeepAliveThreadToBeTerminated ();
943
+ keepAliveThreadExecutor .shutdownNow ();
944
+ while (!awaitTerminationInterruptibly (keepAliveThreadExecutor ,
945
+ Long .MAX_VALUE , TimeUnit .NANOSECONDS )) {
946
+ // ignore
936
947
}
937
948
}
938
949
939
- private void waitForKeepAliveThreadToBeTerminated () {
940
- boolean terminated = false ;
950
+ private static boolean awaitTerminationInterruptibly (ExecutorService executorService , long timeout , TimeUnit unit ) {
941
951
try {
942
- terminated = keepAliveThreadExecutor .awaitTermination (keepAliveThreadShutdownTimeout ,
943
- TimeUnit .MILLISECONDS );
952
+ return executorService .awaitTermination (timeout , unit );
944
953
} catch (InterruptedException e ) {
945
- if (logger .isLoggable (Level .WARNING )) {
946
- logger .log (Level .WARNING , e .getMessage ());
947
- }
954
+ return false ;
948
955
}
949
- if (!terminated ) {
950
- throw new IllegalStateException ("BinaryLogClient was unable to shut keep alive thread down in " +
951
- keepAliveThreadShutdownTimeout + "ms" );
956
+ }
957
+
958
+ private void terminateConnect () throws IOException {
959
+ do {
960
+ disconnectChannel ();
961
+ } while (!tryLockInterruptibly (connectLock , 1000 , TimeUnit .MILLISECONDS ));
962
+ connectLock .unlock ();
963
+ }
964
+
965
+ private static boolean tryLockInterruptibly (Lock lock , long time , TimeUnit unit ) {
966
+ try {
967
+ return lock .tryLock (time , unit );
968
+ } catch (InterruptedException e ) {
969
+ return false ;
952
970
}
953
971
}
954
972
955
973
private void disconnectChannel () throws IOException {
956
- try {
957
- connected = false ;
958
- if (channel != null && channel .isOpen ()) {
959
- channel .close ();
960
- }
961
- } finally {
962
- synchronized (lifecycleListeners ) {
963
- for (LifecycleListener lifecycleListener : lifecycleListeners ) {
964
- lifecycleListener .onDisconnect (this );
965
- }
966
- }
974
+ connected = false ;
975
+ if (channel != null && channel .isOpen ()) {
976
+ channel .close ();
967
977
}
968
978
}
969
979
0 commit comments