1 /***
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Exoffice Technologies. For written permission,
18 * please contact info@exolab.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Exoffice Technologies. Exolab is a registered
23 * trademark of Exoffice Technologies.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 2001-2003 (C) Exoffice Technologies Inc. All Rights Reserved.
42 *
43 * $Id: TopicConsumerEndpoint.java,v 1.40 2003/10/21 14:41:23 tanderson Exp $
44 *
45 * Date Author Changes
46 * 3/1/2001 jima Created
47 */
48 package org.exolab.jms.messagemgr;
49
50 import java.sql.Connection;
51 import java.util.HashMap;
52 import java.util.Iterator;
53
54 import javax.jms.InvalidSelectorException;
55 import javax.jms.JMSException;
56 import javax.jms.MessageListener;
57 import javax.jms.Session;
58 import javax.transaction.TransactionManager;
59
60 import org.apache.commons.logging.Log;
61 import org.apache.commons.logging.LogFactory;
62
63 import org.exolab.jms.JMSErrorCodes;
64 import org.exolab.jms.client.JmsDestination;
65 import org.exolab.jms.client.JmsTopic;
66 import org.exolab.jms.message.MessageHandle;
67 import org.exolab.jms.message.MessageImpl;
68 import org.exolab.jms.persistence.PersistenceException;
69 import org.exolab.jms.scheduler.Scheduler;
70 import org.exolab.jms.server.ClientDisconnectionException;
71 import org.exolab.jms.server.JmsServerSession;
72
73
74 /***
75 * The topic destination cache is specific to a consumer that
76 * subscribes to a topic destination.
77 *
78 * @version $Revision: 1.40 $ $Date: 2003/10/21 14:41:23 $
79 * @author <a href="mailto:jima@exoffice.com">Jim Alateras</a>
80 */
81 public class TopicConsumerEndpoint
82 extends ConsumerEndpoint
83 implements DestinationEventListener {
84
85 /***
86 * Maintains a list of TopicDestinationCache that this endpoint subscribes
87 * too. A wildcard subscription may point to more than one.
88 */
89 protected HashMap _caches = new HashMap();
90
91 /***
92 * Cache the destination that this endpoint subscribes too
93 */
94 protected JmsTopic _topic = null;
95
96 /***
97 * The maximum number of messages that a dispatch can deliver at any one
98 * time
99 */
100 private final int MAX_MESSAGES = 200;
101
102 /***
103 * The logger
104 */
105 private static final Log _log =
106 LogFactory.getLog(TopicConsumerEndpoint.class);
107
108
109 /***
110 * Construct a new <code>TopicConsumerEndpoint</code>.<p/>
111 * The destination and selector determine where it will be sourcing
112 * its messages from, and scheduler is used to asynchronously deliver
113 * messages to the consumer.
114 *
115 * @param session - the owning session
116 * @param clientId - uniquely identifies the remote client within session
117 * @param topic - destination that this object was created for
118 * @param selector - the selector attached to the consumer, if any.
119 * @param scheduler - used to schedule async message delivery.
120 * @throws InvalidSelectorException - if the selector is invalid
121 * @throws JMSException - for other general exceptions
122 */
123 TopicConsumerEndpoint(JmsServerSession session, long clientId,
124 JmsTopic topic, String selector, Scheduler scheduler)
125 throws InvalidSelectorException, JMSException {
126 super(session, clientId, selector, scheduler);
127
128 // register the endpoint with the destination
129 DestinationManager destmgr = DestinationManager.instance();
130 if (topic != null) {
131 _topic = topic;
132
133 if (_topic.isWildCard()) {
134 // if the topic is a wild card then we need to retrieve a
135 // set of matching destination caches.
136 _caches = destmgr.getTopicDestinationCaches(topic);
137 // for each cache register this endpoint as a consumer of
138 // it's messages. Before doing so register as a destination
139 // event listener with the DestinationManager
140 destmgr.addDestinationEventListener(this);
141 Iterator iter = _caches.values().iterator();
142 while (iter.hasNext()) {
143 ((TopicDestinationCache) iter.next()).registerConsumer(this);
144 }
145 } else {
146 // if the topic is not a wildcard then we need to get the
147 // destination cache. If one does not exist then we need to
148 // create it.
149 DestinationCache cache = destmgr.getDestinationCache(topic);
150 if (cache == null) {
151 cache = destmgr.createDestinationCache(topic);
152 }
153 _caches.put(topic, cache);
154 cache.registerConsumer(this);
155 }
156 }
157 }
158
159 /***
160 * Deliver messages in the cache to the consumer
161 *
162 * @return <code>true</code> if the endpoint should be rescheduled
163 */
164 public boolean deliverMessages() {
165 boolean reschedule = true;
166
167 for (int index = 0; index < MAX_MESSAGES;) {
168
169 // check if we should exit the loop
170 if (stopDelivery()) {
171 reschedule = false;
172 break;
173 }
174
175 // Process the first message on the list.
176 boolean processed = true;
177 MessageHandle handle = removeFirstMessage();
178 try {
179 if (_selector != null) {
180 MessageImpl m = handle.getMessage();
181 if ((m != null) && _selector.selects(m)) {
182 // this message has been selected by the selector
183 handle.setClientId(getClientId());
184 _listener.onMessage(handle, true);
185 index++;
186 } else {
187 // this message has not been selected
188 handle.destroy();
189 }
190 } else {
191 // send the message to the consumer
192 handle.setClientId(getClientId());
193 _listener.onMessage(handle, true);
194 index++;
195 }
196 } catch (ClientDisconnectionException exception) {
197 _listener = null;
198 returnMessage(handle);
199 } catch (JMSException exception) {
200 if (exception.getErrorCode().equals(
201 JMSErrorCodes.FailedToResolveHandle)) {
202 // do not return message back to the cache
203 _log.error("Dropping handle " + handle
204 + " since we cannot resolve it.");
205 } else {
206 _log.error(exception, exception);
207 returnMessage(handle);
208 }
209 } catch (Exception exception) {
210 _log.error(exception, exception);
211 returnMessage(handle);
212 }
213 }
214 return reschedule;
215 }
216
217 // implementation of ConsumerEndpoint.receiveMessage
218 public MessageHandle receiveMessage(long wait) {
219 synchronized (_waitingForMessageMonitor) {
220 MessageHandle handle = receiveNoWait();
221 if ((handle == null) &&
222 (wait >= 0)) {
223 // no message is currently available for this
224 // consumer. So set the flag to indicate that
225 // the consumer is waiting for a message
226 setWaitingForMessage();
227 }
228
229 return handle;
230 }
231 }
232
233 /***
234 * Returns the first available message
235 */
236 public MessageHandle receiveNoWait() {
237 MessageHandle handle = null;
238 while ((handle = removeFirstMessage()) != null) {
239 // make sure we can still get access to the message
240 MessageImpl message = handle.getMessage();
241 if (message != null) {
242 if (_selector == null || _selector.selects(message)) {
243 // got a message which is applicable for the endpoint
244 handle.setClientId(getClientId());
245 break;
246 } else {
247 // this message has been filtered out so we can destroy
248 // the handle.
249 handle.destroy();
250 }
251 }
252 handle = null;
253 }
254 return handle;
255 }
256
257 // override ConsumerEndpoint.messageRemoved
258 public boolean messageRemoved(MessageImpl message) {
259
260 boolean result = false;
261
262 if (message != null) {
263 try {
264 MessageHandle handle =
265 MessageHandleFactory.getHandle(this, message);
266
267 // if the message in the main cache then destroy the
268 // handle
269 if (removeMessage(handle)) {
270 if (handle.getConsumerName() == null) {
271 // @todo - the follwoing is required in order for
272 // MessageMgr to resolve this endpoint when the
273 // handle is destroyed. However, its not clear
274 // why topics need to go through MessageMgr at all...
275 handle.setConsumerName(getPersistentId());
276 }
277 handle.destroy();
278 result = true;
279 }
280
281 } catch (JMSException exception) {
282 _log.error("Failed to remove message from endpoint",
283 exception);
284 }
285 }
286
287 return result;
288 }
289
290 // override ConsumerEndpoint.messageRemoved
291 public boolean persistentMessageRemoved(Connection connection,
292 MessageImpl message)
293 throws PersistenceException {
294
295 boolean result = false;
296
297 if (message != null) {
298 try {
299 PersistentMessageHandle handle = (PersistentMessageHandle)
300 MessageHandleFactory.getHandle(this, message);
301
302 // if the message in the main cache then destroy the handle
303 if (removeMessage(handle)) {
304 MessageHandleFactory.destroyPersistentHandle(connection,
305 handle);
306 result = true;
307 }
308
309 } catch (JMSException exception) {
310 _log.error("Failed to remove persistent message from endpoint",
311 exception);
312 }
313 }
314
315 return result;
316 }
317
318 // implementation of ConsumerEndpoint.getDestination
319 public JmsDestination getDestination() {
320 return _topic;
321 }
322
323 // override ConsumerEndpoint.unregister
324 public void unregister() {
325 Iterator iter = _caches.values().iterator();
326 while (iter.hasNext()) {
327 ((TopicDestinationCache) iter.next()).unregisterConsumer(this);
328 }
329 _caches.clear();
330 }
331
332 public void recover() {
333 // no-op
334 }
335
336 // implementation of DestinationEventListener.destinationAdded
337 public void destinationAdded(JmsDestination destination,
338 DestinationCache cache) {
339 if ((destination instanceof JmsTopic) &&
340 (_topic.match((JmsTopic) destination)) &&
341 (!_caches.containsKey(destination))) {
342 synchronized (_caches) {
343 _caches.put(destination, cache);
344 }
345
346 ((TopicDestinationCache) cache).registerConsumer(this);
347 _log.debug(_topic + " registered with " + destination);
348 }
349 }
350
351 // implementation of DestinationEventListener.destinationRemoved
352 public void destinationRemoved(JmsDestination destination,
353 DestinationCache cache) {
354 if ((destination instanceof JmsTopic) &&
355 (_caches.containsKey(destination))) {
356 synchronized (_caches) {
357 _caches.remove(destination);
358 }
359 _log.debug(_topic + " unregistered with " + destination);
360 }
361 }
362
363 /***
364 * Closes this endpoint
365 */
366 protected void doClose() {
367 // unregister as a destination event listener
368 DestinationManager.instance().removeDestinationEventListener(this);
369 // unregister from the destination before continuing
370 Iterator iter = _caches.values().iterator();
371 while (iter.hasNext()) {
372 ((TopicDestinationCache) iter.next()).unregisterConsumer(this);
373 }
374 _caches.clear();
375 }
376
377 } //-- TopicConsumerEndpoint
This page was automatically generated by Maven