001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2011 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    
021    package org.granite.gravity.adapters;
022    
023    import java.util.concurrent.ConcurrentHashMap;
024    import java.util.concurrent.ConcurrentMap;
025    
026    import org.granite.gravity.Channel;
027    import org.granite.gravity.Subscription;
028    
029    import flex.messaging.messages.AsyncMessage;
030    
031    /**
032     * Adapted from Greg Wilkins code (Jetty).
033     * 
034     * @author William DRAI
035     */
036    public class Topic {
037    
038        private final TopicId id;
039        private final SimpleServiceAdapter serviceAdapter;
040    
041        private ConcurrentMap<String, Subscription> subscriptions = new ConcurrentHashMap<String, Subscription>();
042        private ConcurrentMap<String, Topic> children = new ConcurrentHashMap<String, Topic>();
043        private Topic wild;
044        private Topic wildWild;
045    
046    
047        public Topic(String topicId, SimpleServiceAdapter serviceAdapter) {
048            this.id = new TopicId(topicId);
049            this.serviceAdapter = serviceAdapter;
050        }
051    
052        public String getId() {
053            return id.toString();
054        }
055    
056        public TopicId getTopicId() {
057            return id;
058        }
059    
060        public Topic getChild(TopicId topicId) {
061            String next = topicId.getSegment(id.depth());
062            if (next == null)
063                return null;
064    
065            Topic topic = children.get(next);
066    
067            if (topic == null || topic.getTopicId().depth() == topicId.depth()) {
068                return topic;
069            }
070            return topic.getChild(topicId);
071        }
072    
073        public void addChild(Topic topic) {
074            TopicId child = topic.getTopicId();
075            if (!id.isParentOf(child))
076                throw new IllegalArgumentException(id + " not parent of " + child);
077    
078            String next = child.getSegment(id.depth());
079    
080            if ((child.depth() - id.depth()) == 1) {
081                // add the topic to this topics
082                Topic old = children.putIfAbsent(next, topic);
083    
084                if (old != null)
085                    throw new IllegalArgumentException("Already Exists");
086    
087                if (TopicId.WILD.equals(next))
088                    wild = topic;
089                else if (TopicId.WILDWILD.equals(next))
090                    wildWild = topic;
091            }
092            else {
093                Topic branch = serviceAdapter.getTopic((id.depth() == 0 ? "/" : (id.toString() + "/")) + next, true);
094                branch.addChild(topic);
095            }
096        }
097    
098        public void subscribe(Channel channel, String destination, String subscriptionId, String selector, boolean noLocal) {
099            synchronized (this) {
100                Subscription subscription = channel.addSubscription(destination, getId(), subscriptionId, noLocal);
101                subscription.setSelector(selector);
102                subscriptions.putIfAbsent(subscriptionId, subscription);
103            }
104        }
105    
106        public void unsubscribe(Channel channel, String subscriptionId) {
107            synchronized(this) {
108                    subscriptions.remove(subscriptionId);
109                channel.removeSubscription(subscriptionId);
110            }
111        }
112    
113    
114        public void publish(TopicId to, Channel fromChannel, AsyncMessage msg) {
115            int tail = to.depth()-id.depth();
116    
117            switch(tail) {
118                case 0:
119                    for (Subscription subscription : subscriptions.values()) {
120                        AsyncMessage m = msg.clone();
121                        subscription.deliver(fromChannel, m);
122                    }
123    
124                    break;
125    
126                case 1:
127                    if (wild != null) {
128                        for (Subscription subscription : wild.subscriptions.values()) {
129                            AsyncMessage m = msg.clone();
130                            subscription.deliver(fromChannel, m);
131                        }
132                    }
133    
134                default: {
135                    if (wildWild != null) {
136                        for (Subscription subscription : wildWild.subscriptions.values()) {
137                            AsyncMessage m = msg.clone();
138                            subscription.deliver(fromChannel, m);
139                        }
140                    }
141                    String next = to.getSegment(id.depth());
142                    Topic topic = children.get(next);
143                    if (topic != null)
144                        topic.publish(to, fromChannel, msg);
145                }
146            }
147        }
148    
149        @Override
150        public String toString() {
151            return id.toString() + " {" + children.values() + "}";
152        }
153    }