16
16
17
17
package com .rabbitmq .client ;
18
18
19
+ import com .rabbitmq .utility .Utility ;
20
+
19
21
import java .io .IOException ;
22
+ import java .util .concurrent .BlockingQueue ;
23
+ import java .util .concurrent .LinkedBlockingQueue ;
20
24
21
25
/**
22
26
* Class which manages a request queue for a simple RPC-style service.
@@ -28,10 +32,10 @@ public class RpcServer {
28
32
/** Queue to receive requests from */
29
33
private final String _queueName ;
30
34
/** Boolean controlling the exit from the mainloop. */
31
- private boolean _mainloopRunning = true ;
35
+ private volatile boolean _mainloopRunning = true ;
32
36
33
37
/** Consumer attached to our request queue */
34
- private QueueingConsumer _consumer ;
38
+ private RpcConsumer _consumer ;
35
39
36
40
/**
37
41
* Creates an RpcServer listening on a temporary exclusive
@@ -80,10 +84,10 @@ public void close()
80
84
* @throws IOException if an error is encountered
81
85
* @return the newly created and registered consumer
82
86
*/
83
- protected QueueingConsumer setupConsumer ()
87
+ protected RpcConsumer setupConsumer ()
84
88
throws IOException
85
89
{
86
- QueueingConsumer consumer = new QueueingConsumer (_channel );
90
+ RpcConsumer consumer = new DefaultRpcConsumer (_channel );
87
91
_channel .basicConsume (_queueName , consumer );
88
92
return consumer ;
89
93
}
@@ -106,7 +110,7 @@ public ShutdownSignalException mainloop()
106
110
{
107
111
try {
108
112
while (_mainloopRunning ) {
109
- QueueingConsumer . Delivery request ;
113
+ Delivery request ;
110
114
try {
111
115
request = _consumer .nextDelivery ();
112
116
} catch (InterruptedException ie ) {
@@ -136,7 +140,7 @@ public void terminateMainloop() {
136
140
/**
137
141
* Private API - Process a single request. Called from mainloop().
138
142
*/
139
- public void processRequest (QueueingConsumer . Delivery request )
143
+ public void processRequest (Delivery request )
140
144
throws IOException
141
145
{
142
146
AMQP .BasicProperties requestProperties = request .getProperties ();
@@ -157,7 +161,7 @@ public void processRequest(QueueingConsumer.Delivery request)
157
161
* Lowest-level response method. Calls
158
162
* handleCall(AMQP.BasicProperties,byte[],AMQP.BasicProperties).
159
163
*/
160
- public byte [] handleCall (QueueingConsumer . Delivery request ,
164
+ public byte [] handleCall (Delivery request ,
161
165
AMQP .BasicProperties replyProperties )
162
166
{
163
167
return handleCall (request .getProperties (),
@@ -191,7 +195,7 @@ public byte[] handleCall(byte[] requestBody,
191
195
* Lowest-level handler method. Calls
192
196
* handleCast(AMQP.BasicProperties,byte[]).
193
197
*/
194
- public void handleCast (QueueingConsumer . Delivery request )
198
+ public void handleCast (Delivery request )
195
199
{
196
200
handleCast (request .getProperties (), request .getBody ());
197
201
}
@@ -230,5 +234,143 @@ public Channel getChannel() {
230
234
public String getQueueName () {
231
235
return _queueName ;
232
236
}
237
+
238
+ /**
239
+ * Encapsulates an arbitrary message - simple "bean" holder structure.
240
+ */
241
+ public static class Delivery {
242
+ private final Envelope _envelope ;
243
+ private final AMQP .BasicProperties _properties ;
244
+ private final byte [] _body ;
245
+
246
+ public Delivery (Envelope envelope , AMQP .BasicProperties properties , byte [] body ) {
247
+ _envelope = envelope ;
248
+ _properties = properties ;
249
+ _body = body ;
250
+ }
251
+
252
+ /**
253
+ * Retrieve the message envelope.
254
+ * @return the message envelope
255
+ */
256
+ public Envelope getEnvelope () {
257
+ return _envelope ;
258
+ }
259
+
260
+ /**
261
+ * Retrieve the message properties.
262
+ * @return the message properties
263
+ */
264
+ public AMQP .BasicProperties getProperties () {
265
+ return _properties ;
266
+ }
267
+
268
+ /**
269
+ * Retrieve the message body.
270
+ * @return the message body
271
+ */
272
+ public byte [] getBody () {
273
+ return _body ;
274
+ }
275
+ }
276
+
277
+ public interface RpcConsumer extends Consumer {
278
+
279
+ Delivery nextDelivery () throws InterruptedException , ShutdownSignalException , ConsumerCancelledException ;
280
+
281
+ String getConsumerTag ();
282
+
283
+ }
284
+
285
+ private static class DefaultRpcConsumer extends DefaultConsumer implements RpcConsumer {
286
+
287
+ // Marker object used to signal the queue is in shutdown mode.
288
+ // It is only there to wake up consumers. The canonical representation
289
+ // of shutting down is the presence of _shutdown.
290
+ // Invariant: This is never on _queue unless _shutdown != null.
291
+ private static final Delivery POISON = new Delivery (null , null , null );
292
+ private final BlockingQueue <Delivery > _queue ;
293
+ // When this is non-null the queue is in shutdown mode and nextDelivery should
294
+ // throw a shutdown signal exception.
295
+ private volatile ShutdownSignalException _shutdown ;
296
+ private volatile ConsumerCancelledException _cancelled ;
297
+
298
+ public DefaultRpcConsumer (Channel ch ) {
299
+ this (ch , new LinkedBlockingQueue <>());
300
+ }
301
+
302
+ public DefaultRpcConsumer (Channel ch , BlockingQueue <Delivery > q ) {
303
+ super (ch );
304
+ this ._queue = q ;
305
+ }
306
+
307
+ @ Override
308
+ public Delivery nextDelivery () throws InterruptedException , ShutdownSignalException , ConsumerCancelledException {
309
+ return handle (_queue .take ());
310
+ }
311
+
312
+ @ Override
313
+ public void handleShutdownSignal (String consumerTag ,
314
+ ShutdownSignalException sig ) {
315
+ _shutdown = sig ;
316
+ _queue .add (POISON );
317
+ }
318
+
319
+ @ Override
320
+ public void handleCancel (String consumerTag ) throws IOException {
321
+ _cancelled = new ConsumerCancelledException ();
322
+ _queue .add (POISON );
323
+ }
324
+
325
+ @ Override
326
+ public void handleDelivery (String consumerTag ,
327
+ Envelope envelope ,
328
+ AMQP .BasicProperties properties ,
329
+ byte [] body )
330
+ throws IOException {
331
+ checkShutdown ();
332
+ this ._queue .add (new Delivery (envelope , properties , body ));
333
+ }
334
+
335
+ /**
336
+ * Check if we are in shutdown mode and if so throw an exception.
337
+ */
338
+ private void checkShutdown () {
339
+ if (_shutdown != null )
340
+ throw Utility .fixStackTrace (_shutdown );
341
+ }
342
+
343
+ /**
344
+ * If delivery is not POISON nor null, return it.
345
+ * <p/>
346
+ * If delivery, _shutdown and _cancelled are all null, return null.
347
+ * <p/>
348
+ * If delivery is POISON re-insert POISON into the queue and
349
+ * throw an exception if POISONed for no reason.
350
+ * <p/>
351
+ * Otherwise, if we are in shutdown mode or cancelled,
352
+ * throw a corresponding exception.
353
+ */
354
+ private Delivery handle (Delivery delivery ) {
355
+ if (delivery == POISON ||
356
+ delivery == null && (_shutdown != null || _cancelled != null )) {
357
+ if (delivery == POISON ) {
358
+ _queue .add (POISON );
359
+ if (_shutdown == null && _cancelled == null ) {
360
+ throw new IllegalStateException (
361
+ "POISON in queue, but null _shutdown and null _cancelled. " +
362
+ "This should never happen, please report as a BUG" );
363
+ }
364
+ }
365
+ if (null != _shutdown )
366
+ throw Utility .fixStackTrace (_shutdown );
367
+ if (null != _cancelled )
368
+ throw Utility .fixStackTrace (_cancelled );
369
+ }
370
+ return delivery ;
371
+ }
372
+ }
373
+
374
+
233
375
}
234
376
0 commit comments