2020import com .github .shyiko .mysql .binlog .event .EventHeader ;
2121import com .github .shyiko .mysql .binlog .event .EventHeaderV4 ;
2222import com .github .shyiko .mysql .binlog .event .EventType ;
23+ import com .github .shyiko .mysql .binlog .event .GtidEventData ;
2324import com .github .shyiko .mysql .binlog .event .RotateEventData ;
2425import com .github .shyiko .mysql .binlog .event .deserialization .ChecksumType ;
2526import com .github .shyiko .mysql .binlog .event .deserialization .EventDataDeserializationException ;
2627import com .github .shyiko .mysql .binlog .event .deserialization .EventDataDeserializer ;
2728import com .github .shyiko .mysql .binlog .event .deserialization .EventDeserializer ;
29+ import com .github .shyiko .mysql .binlog .event .deserialization .GtidEventDataDeserializer ;
2830import com .github .shyiko .mysql .binlog .event .deserialization .RotateEventDataDeserializer ;
2931import com .github .shyiko .mysql .binlog .io .ByteArrayInputStream ;
3032import com .github .shyiko .mysql .binlog .jmx .BinaryLogClientMXBean ;
3537import com .github .shyiko .mysql .binlog .network .protocol .PacketChannel ;
3638import com .github .shyiko .mysql .binlog .network .protocol .ResultSetRowPacket ;
3739import com .github .shyiko .mysql .binlog .network .protocol .command .AuthenticateCommand ;
40+ import com .github .shyiko .mysql .binlog .network .protocol .command .Command ;
3841import com .github .shyiko .mysql .binlog .network .protocol .command .DumpBinaryLogCommand ;
42+ import com .github .shyiko .mysql .binlog .network .protocol .command .DumpBinaryLogGtidCommand ;
3943import com .github .shyiko .mysql .binlog .network .protocol .command .PingCommand ;
4044import com .github .shyiko .mysql .binlog .network .protocol .command .QueryCommand ;
4145
@@ -80,6 +84,9 @@ public class BinaryLogClient implements BinaryLogClientMXBean {
8084 private volatile String binlogFilename ;
8185 private volatile long binlogPosition = 4 ;
8286
87+ private GtidSet gtidSet ;
88+ private final Object gtidSetAccessLock = new Object ();
89+
8390 private EventDeserializer eventDeserializer = new EventDeserializer ();
8491
8592 private final List <EventListener > eventListeners = new LinkedList <EventListener >();
@@ -200,6 +207,34 @@ public void setBinlogPosition(long binlogPosition) {
200207 this .binlogPosition = binlogPosition ;
201208 }
202209
210+ /**
211+ * @return GTID set. Note that this value changes with each received GTID event (provided client is in GTID mode).
212+ * @see #setGtidSet(String)
213+ */
214+ public String getGtidSet () {
215+ synchronized (gtidSetAccessLock ) {
216+ return gtidSet != null ? gtidSet .toString () : null ;
217+ }
218+ }
219+
220+ /**
221+ * @param gtidSet GTID set (can be an empty string).
222+ * <p>NOTE #1: Any value but null will switch BinaryLogClient into a GTID mode (in which case GTID set will be
223+ * updated with each incoming GTID event) as well as set binlogFilename to "" (empty string) (meaning
224+ * BinaryLogClient will request events "outside of the set" <u>starting from the oldest known binlog</u>).
225+ * <p>NOTE #2: {@link #setBinlogFilename(String)} and {@link #setBinlogPosition(long)} can be used to specify the
226+ * exact position from which MySQL server should start streaming events (taking into account GTID set).
227+ * @see #getGtidSet()
228+ */
229+ public void setGtidSet (String gtidSet ) {
230+ if (gtidSet != null && this .binlogFilename == null ) {
231+ this .binlogFilename = "" ;
232+ }
233+ synchronized (gtidSetAccessLock ) {
234+ this .gtidSet = gtidSet != null ? new GtidSet (gtidSet ) : null ;
235+ }
236+ }
237+
203238 /**
204239 * @return true if "keep alive" thread should be automatically started (default), false otherwise.
205240 * @see #setKeepAlive(boolean)
@@ -309,7 +344,7 @@ public void connect() throws IOException {
309344 if (checksumType != ChecksumType .NONE ) {
310345 confirmSupportOfChecksum (checksumType );
311346 }
312- channel . write ( new DumpBinaryLogCommand ( serverId , binlogFilename , binlogPosition ) );
347+ requestBinaryLogStream ( );
313348 } catch (IOException e ) {
314349 if (channel != null && channel .isOpen ()) {
315350 channel .close ();
@@ -328,14 +363,42 @@ public void connect() throws IOException {
328363 if (keepAlive && !isKeepAliveThreadRunning ()) {
329364 spawnKeepAliveThread ();
330365 }
331- EventDataDeserializer eventDataDeserializer = eventDeserializer .getEventDataDeserializer (EventType .ROTATE );
332- if (eventDataDeserializer .getClass () != RotateEventDataDeserializer .class &&
366+ ensureEventDataDeserializer (EventType .ROTATE , RotateEventDataDeserializer .class );
367+ synchronized (gtidSetAccessLock ) {
368+ if (gtidSet != null ) {
369+ ensureEventDataDeserializer (EventType .GTID , GtidEventDataDeserializer .class );
370+ }
371+ }
372+ listenForEventPackets ();
373+ }
374+
375+ private void requestBinaryLogStream () throws IOException {
376+ Command dumpBinaryLogCommand ;
377+ synchronized (gtidSetAccessLock ) {
378+ if (gtidSet != null ) {
379+ dumpBinaryLogCommand = new DumpBinaryLogGtidCommand (serverId , binlogFilename , binlogPosition , gtidSet );
380+ } else {
381+ dumpBinaryLogCommand = new DumpBinaryLogCommand (serverId , binlogFilename , binlogPosition );
382+ }
383+ }
384+ channel .write (dumpBinaryLogCommand );
385+ }
386+
387+ private void ensureEventDataDeserializer (EventType eventType ,
388+ Class <? extends EventDataDeserializer > eventDataDeserializerClass ) {
389+ EventDataDeserializer eventDataDeserializer = eventDeserializer .getEventDataDeserializer (eventType );
390+ if (eventDataDeserializer .getClass () != eventDataDeserializerClass &&
333391 eventDataDeserializer .getClass () != EventDeserializer .EventDataWrapper .Deserializer .class ) {
334- eventDeserializer .setEventDataDeserializer (EventType .ROTATE ,
335- new EventDeserializer .EventDataWrapper .Deserializer (new RotateEventDataDeserializer (),
392+ EventDataDeserializer internalEventDataDeserializer ;
393+ try {
394+ internalEventDataDeserializer = eventDataDeserializerClass .newInstance ();
395+ } catch (Exception e ) {
396+ throw new RuntimeException (e );
397+ }
398+ eventDeserializer .setEventDataDeserializer (eventType ,
399+ new EventDeserializer .EventDataWrapper .Deserializer (internalEventDataDeserializer ,
336400 eventDataDeserializer ));
337401 }
338- listenForEventPackets ();
339402 }
340403
341404 private void authenticate (String salt , int collation ) throws IOException {
@@ -526,6 +589,7 @@ private void listenForEventPackets() throws IOException {
526589 if (isConnected ()) {
527590 notifyEventListeners (event );
528591 updateClientBinlogFilenameAndPosition (event );
592+ updateGtidSet (event );
529593 }
530594 }
531595 } catch (Exception e ) {
@@ -565,6 +629,24 @@ private void updateClientBinlogFilenameAndPosition(Event event) {
565629 }
566630 }
567631
632+ private void updateGtidSet (Event event ) {
633+ EventHeader eventHeader = event .getHeader ();
634+ if (eventHeader .getEventType () == EventType .GTID ) {
635+ synchronized (gtidSetAccessLock ) {
636+ if (gtidSet != null ) {
637+ EventData eventData = event .getData ();
638+ GtidEventData gtidEventData ;
639+ if (eventData instanceof EventDeserializer .EventDataWrapper ) {
640+ gtidEventData = (GtidEventData ) ((EventDeserializer .EventDataWrapper ) eventData ).getInternal ();
641+ } else {
642+ gtidEventData = (GtidEventData ) eventData ;
643+ }
644+ gtidSet .add (gtidEventData .getGtid ());
645+ }
646+ }
647+ }
648+ }
649+
568650 private ResultSetRowPacket [] readResultSet () throws IOException {
569651 List <ResultSetRowPacket > resultSet = new LinkedList <ResultSetRowPacket >();
570652 while ((channel .read ())[0 ] != (byte ) 0xFE /* eof */ ) { /* skip */ }
0 commit comments