001package gu.simplemq.activemq;
002
003import java.util.HashMap;
004import java.util.Iterator;
005
006import javax.jms.Connection;
007import javax.jms.JMSException;
008import javax.jms.Message;
009import javax.jms.MessageConsumer;
010import javax.jms.MessageListener;
011import javax.jms.Session;
012
013import org.apache.activemq.advisory.AdvisorySupport;
014import org.apache.activemq.command.ActiveMQDestination;
015import org.apache.activemq.command.ActiveMQQueue;
016import org.apache.activemq.command.ActiveMQTopic;
017
018import com.google.common.cache.CacheBuilder;
019import com.google.common.cache.CacheLoader;
020import com.google.common.cache.LoadingCache;
021import com.google.common.collect.Maps;
022
023import gu.simplemq.IConsumerAdvisor;
024
025import static com.google.common.base.Preconditions.*;
026/**
027 * threadSafe
028 * @author guyadong
029 *
030 */
031public class AdvisoryMessageManager implements AutoCloseable,IConsumerAdvisor,ActivemqConstants{
032        private final HashMap<String, MessageConsumer> advisoryConsumers = Maps.newHashMap();
033        private static final LoadingCache<ActivemqPoolLazy,AdvisoryMessageManager> CACHE = CacheBuilder.newBuilder().build(new CacheLoader<ActivemqPoolLazy, AdvisoryMessageManager>(){
034
035                @Override
036                public AdvisoryMessageManager load(ActivemqPoolLazy key) throws Exception {
037                        return new AdvisoryMessageManager(key);
038                }});
039        private volatile Connection advisoryConnection;
040        private volatile Session advisorySession;
041        private final ActivemqPoolLazy poolLazy;
042        private AdvisoryMessageManager(ActivemqPoolLazy poolLazy) {
043                this.poolLazy = checkNotNull(poolLazy, "poolLazy is null");
044        }
045        private void init() throws JMSException{
046                if(advisoryConnection == null){
047                        advisoryConnection = this.poolLazy.borrow();
048                        advisoryConnection.start();
049                        advisorySession = advisoryConnection.createSession(Boolean.FALSE, Session.AUTO_ACKNOWLEDGE);
050                }
051        }
052        
053        void addAdvisoryConsumerIfAbsent(ActiveMQDestination destination) throws JMSException {
054                String name = destination.getPhysicalName();
055                synchronized (this) {
056                        if(!advisoryConsumers.containsKey(name)){
057                                init();
058                                ActiveMQTopic advisoryTopic = AdvisorySupport.getConsumerAdvisoryTopic(destination);
059                                MessageConsumer advisoryConsumer = advisorySession.createConsumer(advisoryTopic);
060                                advisoryConsumer.setMessageListener(new AdvisoryListener());            
061                                advisoryConsumers.put(name, advisoryConsumer);
062                        }
063                }
064        }
065        @Override
066        public void addAdvisoryTopicIfAbsent(String name)  {
067                try {
068                        addAdvisoryConsumerIfAbsent(new ActiveMQTopic(name));
069                } catch (JMSException e) {
070                        throw new RuntimeException(e);
071                }
072        }
073        @Override
074        public void addAdvisoryQueueIfAbsent(String name)  {
075                try {
076                        addAdvisoryConsumerIfAbsent(new ActiveMQQueue(name));
077                } catch (JMSException e) {
078                        throw new RuntimeException(e);
079                }
080        }
081        /**
082         * 返回指定频道的 advisory message<br>
083         * @param channelName
084         * @return advisory message,没有收到消息返回{@code null}
085         * @throws JMSException
086         */
087        Message advisoryMessageOf(String channelName) throws JMSException{
088                synchronized (this) {
089                        if(advisoryConsumers.containsKey(channelName)){
090                                MessageConsumer advisoryConsumer = advisoryConsumers.get(channelName);
091                                AdvisoryListener advisoryListener = (AdvisoryListener) advisoryConsumer.getMessageListener();
092                                if(advisoryListener.message == null){
093                                        synchronized (advisoryListener) {
094                                                try {
095                                                        advisoryListener.wait(5000);
096                                                } catch (InterruptedException e) {
097                                                }
098                                        }
099                                }
100                                return advisoryListener.message;        
101                        }
102                        return null;
103                }
104        }
105        @Override
106        public void close() {
107                synchronized (this) {
108                        // 关闭所有 MessageConsumer 实例
109                        for(Iterator<MessageConsumer> itor = advisoryConsumers.values().iterator();itor.hasNext();){
110                                try {
111                                        itor.next().close();
112                                } catch (JMSException e) {
113                                        e.printStackTrace();
114                                }
115                                itor.remove();
116                        }
117                        try {
118                                if(null != advisorySession){
119                                        advisorySession.close();
120                                        advisorySession = null;
121                                }
122                        } catch (JMSException e) {
123                                e.printStackTrace();
124                        }
125                        if(null != advisoryConnection){
126                                this.poolLazy.release(advisoryConnection);
127                                advisoryConnection = null;
128                        }
129                }
130        }
131        
132        @Override
133        public int consumerCountOf(String channelName) {
134                try {
135                        Message message = advisoryMessageOf(channelName);
136                        return message == null ? 0 : message.getIntProperty(PROP_CONSUMER_COUNT);
137                } catch (JMSException e) {
138                        throw new RuntimeException(e);
139                }
140        }
141        
142        public static AdvisoryMessageManager instanceOf(ActivemqPoolLazy pool){
143                return CACHE.getUnchecked(pool);
144        }
145        /**
146         * 关闭并删除所有资源池中的{@link AdvisoryMessageManager}实例
147         */
148        public synchronized static void closeAll(){
149                for(Iterator<AdvisoryMessageManager> itor = CACHE.asMap().values().iterator();itor.hasNext();){
150                        AdvisoryMessageManager p = itor.next();
151                        itor.remove();
152                        p.close();
153                }
154        }
155        private static class AdvisoryListener implements MessageListener{
156                /** 最近一次收到的 advisory 消息 */
157                private Message message ;
158
159                @Override
160                public void onMessage(Message message) {
161                        this.message = message;
162                        synchronized (this) {
163                                this.notifyAll();
164                        }
165                }
166        }
167}