27
27
import org .apache .seatunnel .connectors .seatunnel .activemq .exception .ActivemqConnectorErrorCode ;
28
28
import org .apache .seatunnel .connectors .seatunnel .activemq .exception .ActivemqConnectorException ;
29
29
import org .apache .seatunnel .connectors .seatunnel .activemq .split .ActivemqSplit ;
30
- import org .apache .seatunnel .format .json .JsonDeserializationSchema ;
31
30
32
31
import lombok .extern .slf4j .Slf4j ;
33
32
34
33
import javax .jms .JMSException ;
35
34
import javax .jms .Message ;
36
35
import javax .jms .MessageConsumer ;
37
- import javax .jms .MessageListener ;
38
36
import javax .jms .TextMessage ;
39
37
40
38
import java .io .IOException ;
45
43
import java .util .Set ;
46
44
import java .util .SortedMap ;
47
45
import java .util .TreeMap ;
46
+ import java .util .concurrent .CompletableFuture ;
47
+ import java .util .concurrent .ExecutorService ;
48
+ import java .util .concurrent .Executors ;
49
+ import java .util .concurrent .LinkedBlockingQueue ;
50
+ import java .util .concurrent .TimeUnit ;
48
51
49
52
import static org .apache .seatunnel .connectors .seatunnel .activemq .config .ActivemqOptions .QUEUE_NAME ;
50
53
import static org .apache .seatunnel .connectors .seatunnel .activemq .config .ActivemqSourceOptions .USE_CORRELATION_ID ;
51
54
import static org .apache .seatunnel .connectors .seatunnel .activemq .exception .ActivemqConnectorErrorCode .HANDLE_SHUTDOWN_SIGNAL_FAILED ;
52
- import static org .apache .seatunnel .connectors .seatunnel .activemq .exception .ActivemqConnectorErrorCode .MESSAGE_ACK_FAILED ;
53
55
54
56
@ Slf4j
55
57
public class ActivemqSourceReader <T > implements SourceReader <T , ActivemqSplit > {
58
+ private static final long POLL_TIMEOUT_MILLIS = 1000L ;
59
+
56
60
protected final Context context ;
57
61
protected final MessageConsumer consumer ;
58
62
protected transient Set <String > correlationIdsProcessedButNotAcknowledged ;
@@ -62,6 +66,8 @@ public class ActivemqSourceReader<T> implements SourceReader<T, ActivemqSplit> {
62
66
private final DeserializationSchema <SeaTunnelRow > deserializationSchema ;
63
67
private ActivemqClient activemqClient ;
64
68
private final ReadonlyConfig config ;
69
+ private final ExecutorService executorService ;
70
+ private final LinkedBlockingQueue <Message > messageQueue ;
65
71
66
72
public ActivemqSourceReader (
67
73
DeserializationSchema <SeaTunnelRow > deserializationSchema ,
@@ -74,12 +80,26 @@ public ActivemqSourceReader(
74
80
this .config = config ;
75
81
this .activemqClient = new ActivemqClient (config );
76
82
this .consumer = activemqClient .getConsumer ();
83
+ this .executorService =
84
+ Executors .newCachedThreadPool (r -> new Thread (r , "ActiveMQ Source Data Consumer" ));
85
+ this .messageQueue = new LinkedBlockingQueue <>();
77
86
}
78
87
79
88
@ Override
80
89
public void open () throws Exception {
81
90
this .correlationIdsProcessedButNotAcknowledged = new HashSet <>();
82
91
this .massageIdsProcessedForCurrentSnapshot = new ArrayList <>();
92
+ // start consumer listening and put messages in a queue
93
+ consumer .setMessageListener (
94
+ message -> {
95
+ try {
96
+ messageQueue .put (message );
97
+ } catch (InterruptedException e ) {
98
+ Thread .currentThread ().interrupt ();
99
+ throw new ActivemqConnectorException (
100
+ ActivemqConnectorErrorCode .HANDLE_SHUTDOWN_SIGNAL_FAILED , e );
101
+ }
102
+ });
83
103
}
84
104
85
105
@ Override
@@ -97,16 +117,26 @@ public void close() throws IOException {
97
117
if (activemqClient != null ) {
98
118
activemqClient .close ();
99
119
}
120
+ if (executorService != null ) {
121
+ executorService .shutdownNow ();
122
+ }
100
123
}
101
124
102
125
@ Override
103
126
public void pollNext (Collector output ) throws Exception {
104
- consumer .setMessageListener (
105
- new MessageListener () {
106
- @ Override
107
- public void onMessage (Message message ) {
127
+ while (true ) {
128
+ Message message = messageQueue .poll (POLL_TIMEOUT_MILLIS , TimeUnit .MILLISECONDS );
129
+ if (message == null ) {
130
+ if (Boundedness .BOUNDED .equals (context .getBoundedness ())) {
131
+ break ;
132
+ }
133
+ continue ;
134
+ }
135
+ CompletableFuture <Void > completableFuture = new CompletableFuture <>();
136
+ executorService .submit (
137
+ () -> {
108
138
try {
109
- if (message != null && message instanceof TextMessage ) {
139
+ if (message instanceof TextMessage ) {
110
140
TextMessage textMessage = (TextMessage ) message ;
111
141
String correlationId = textMessage .getJMSCorrelationID ();
112
142
byte [] body = textMessage .getText ().getBytes ();
@@ -117,29 +147,21 @@ public void onMessage(Message message) {
117
147
}
118
148
massageIdsProcessedForCurrentSnapshot .add (
119
149
message .getJMSMessageID ());
120
- try {
121
- if (deserializationSchema
122
- instanceof JsonDeserializationSchema ) {
123
- ((JsonDeserializationSchema ) deserializationSchema )
124
- .collect (body , output );
125
- } else {
126
- deserializationSchema .deserialize (body , output );
127
- }
128
- } catch (IOException e ) {
129
- log .error ("Failed to deserialize message" , e );
130
- }
150
+ deserializationSchema .deserialize (body , output );
151
+
131
152
// verify that message processing is complete
132
153
textMessage .acknowledge ();
133
154
}
134
155
}
135
- } catch (JMSException e ) {
136
- throw new ActivemqConnectorException (MESSAGE_ACK_FAILED , e );
137
156
} catch (Exception e ) {
157
+ completableFuture .join ();
138
158
throw new ActivemqConnectorException (HANDLE_SHUTDOWN_SIGNAL_FAILED , e );
139
159
}
140
- }
141
- });
142
- // source support streaming mode, this is for test
160
+ completableFuture .complete (null );
161
+ });
162
+ completableFuture .join ();
163
+ }
164
+
143
165
if (Boundedness .BOUNDED .equals (context .getBoundedness ())) {
144
166
// signal to the source that we have reached the end of the data.
145
167
context .signalNoMoreElement ();
0 commit comments