001    /**
002     *   GRANITE DATA SERVICES
003     *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004     *
005     *   This file is part of Granite Data Services.
006     *
007     *   Granite Data Services is free software; you can redistribute it and/or modify
008     *   it under the terms of the GNU Library General Public License as published by
009     *   the Free Software Foundation; either version 2 of the License, or (at your
010     *   option) any later version.
011     *
012     *   Granite Data Services is distributed in the hope that it will be useful, but
013     *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014     *   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015     *   for more details.
016     *
017     *   You should have received a copy of the GNU Library General Public License
018     *   along with this library; if not, see <http://www.gnu.org/licenses/>.
019     */
020    package org.granite.client.messaging;
021    
022    import java.util.concurrent.ConcurrentHashMap;
023    
024    import org.granite.client.messaging.channel.MessagingChannel;
025    import org.granite.client.messaging.channel.ResponseMessageFuture;
026    import org.granite.client.messaging.events.IssueEvent;
027    import org.granite.client.messaging.events.ResultEvent;
028    import org.granite.client.messaging.events.TopicMessageEvent;
029    import org.granite.client.messaging.messages.push.TopicMessage;
030    import org.granite.client.messaging.messages.requests.SubscribeMessage;
031    import org.granite.client.messaging.messages.requests.UnsubscribeMessage;
032    import org.granite.logging.Logger;
033    
034    /**
035     * @author Franck WOLFF
036     */
037    public class Consumer extends AbstractTopicAgent {
038            
039            private static final Logger log = Logger.getLogger(Consumer.class);
040    
041            private final ConcurrentHashMap<TopicMessageListener, Boolean> listeners = new ConcurrentHashMap<TopicMessageListener, Boolean>();
042            
043            private String subscriptionId = null;
044            private String selector = null;
045            
046            public Consumer(MessagingChannel channel, String destination, String topic) {
047                    super(channel, destination, topic);
048            }
049    
050            public String getSelector() {
051                    return selector;
052            }
053    
054            public void setSelector(String selector) {
055                    this.selector = selector;
056            }
057            
058            public boolean isSubscribed() {
059                    return subscriptionId != null;
060            }
061    
062            public String getSubscriptionId() {
063                    return subscriptionId;
064            }
065    
066            public ResponseMessageFuture subscribe(ResponseListener...listeners) {
067                    SubscribeMessage subscribeMessage = new SubscribeMessage(destination, topic, selector);
068                    subscribeMessage.getHeaders().putAll(defaultHeaders);
069                    
070                    final Consumer consumer = this;
071                    ResponseListener listener = new ResultIssuesResponseListener() {
072                            
073                            @Override
074                            public void onResult(ResultEvent event) {
075                                    subscriptionId = (String)event.getResult();
076                                    channel.addConsumer(consumer);
077                            }
078                            
079                            @Override
080                            public void onIssue(IssueEvent event) {
081                                    log.error("Subscription failed %s: %s", consumer, event);
082                            }
083                    };
084                    
085                    if (listeners == null || listeners.length == 0)
086                            listeners = new ResponseListener[]{listener};
087                    else {
088                            ResponseListener[] tmp = new ResponseListener[listeners.length + 1];
089                            System.arraycopy(listeners, 0, tmp, 0, listeners.length);
090                            tmp[listeners.length] = listener;
091                            listeners = tmp;
092                    }
093                    
094                    return channel.send(subscribeMessage, listeners);
095            }
096    
097            public ResponseMessageFuture unsubscribe(ResponseListener...listeners) {
098                    UnsubscribeMessage unsubscribeMessage = new UnsubscribeMessage(destination, topic, subscriptionId);
099                    unsubscribeMessage.getHeaders().putAll(defaultHeaders);
100                    
101                    final Consumer consumer = this;
102                    ResponseListener listener = new ResultIssuesResponseListener() {
103                            
104                            @Override
105                            public void onResult(ResultEvent event) {
106                                    channel.removeConsumer(consumer);
107                                    subscriptionId = null;
108                            }
109                            
110                            @Override
111                            public void onIssue(IssueEvent event) {
112                                    log.error("Unsubscription failed %s: %s", consumer, event);
113                            }
114                    };
115                    
116                    if (listeners == null || listeners.length == 0)
117                            listeners = new ResponseListener[]{listener};
118                    else {
119                            ResponseListener[] tmp = new ResponseListener[listeners.length + 1];
120                            System.arraycopy(listeners, 0, tmp, 0, listeners.length);
121                            tmp[listeners.length] = listener;
122                            listeners = tmp;
123                    }
124    
125                    return channel.send(unsubscribeMessage, listeners);
126            }
127    
128            public void addMessageListener(TopicMessageListener listener) {
129                    listeners.putIfAbsent(listener, Boolean.TRUE);
130            }
131            
132            public boolean removeMessageListener(TopicMessageListener listener) {
133                    return listeners.remove(listener) != null;
134            }
135            
136            public void onDisconnect() {
137                    subscriptionId = null;
138            }
139    
140            public void onMessage(TopicMessage message) {
141                    for (TopicMessageListener listener : listeners.keySet()) {
142                            try {
143                                    listener.onMessage(new TopicMessageEvent(this, message));
144                            }
145                            catch (Exception e) {
146                                    log.error(e, "Consumer listener threw an exception: ", listener);
147                            }
148                    }
149            }
150    
151            @Override
152            public String toString() {
153                    return getClass().getName() + " {subscriptionId=" + subscriptionId +
154                            ", destination=" + destination +
155                            ", topic=" + topic +
156                            ", selector=" + selector +
157                    "}";
158            }
159    }