001 /**
002 * GRANITE DATA SERVICES
003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 * This file is part of the Granite Data Services Platform.
006 *
007 * Granite Data Services is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * Granite Data Services is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 * General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 * USA, or see <http://www.gnu.org/licenses/>.
021 */
022 package org.granite.gravity.adapters;
023
024 import java.util.concurrent.ConcurrentHashMap;
025 import java.util.concurrent.ConcurrentMap;
026
027 import org.granite.gravity.Channel;
028 import org.granite.gravity.Subscription;
029
030 import flex.messaging.messages.AsyncMessage;
031
032 /**
033 * Adapted from Greg Wilkins code (Jetty).
034 *
035 * @author William DRAI
036 */
037 public class Topic {
038
039 private final TopicId id;
040 private final SimpleServiceAdapter serviceAdapter;
041
042 private ConcurrentMap<String, Subscription> subscriptions = new ConcurrentHashMap<String, Subscription>();
043 private ConcurrentMap<String, Topic> children = new ConcurrentHashMap<String, Topic>();
044 private Topic wild;
045 private Topic wildWild;
046
047
048 public Topic(String topicId, SimpleServiceAdapter serviceAdapter) {
049 this.id = new TopicId(topicId);
050 this.serviceAdapter = serviceAdapter;
051 }
052
053 public String getId() {
054 return id.toString();
055 }
056
057 public TopicId getTopicId() {
058 return id;
059 }
060
061 public Topic getChild(TopicId topicId) {
062 String next = topicId.getSegment(id.depth());
063 if (next == null)
064 return null;
065
066 Topic topic = children.get(next);
067
068 if (topic == null || topic.getTopicId().depth() == topicId.depth()) {
069 return topic;
070 }
071 return topic.getChild(topicId);
072 }
073
074 public void addChild(Topic topic) {
075 TopicId child = topic.getTopicId();
076 if (!id.isParentOf(child))
077 throw new IllegalArgumentException(id + " not parent of " + child);
078
079 String next = child.getSegment(id.depth());
080
081 if ((child.depth() - id.depth()) == 1) {
082 // add the topic to this topics
083 Topic old = children.putIfAbsent(next, topic);
084
085 if (old != null)
086 throw new IllegalArgumentException("Already Exists");
087
088 if (TopicId.WILD.equals(next))
089 wild = topic;
090 else if (TopicId.WILDWILD.equals(next))
091 wildWild = topic;
092 }
093 else {
094 Topic branch = serviceAdapter.getTopic((id.depth() == 0 ? "/" : (id.toString() + "/")) + next, true);
095 branch.addChild(topic);
096 }
097 }
098
099 public void subscribe(Channel channel, String destination, String subscriptionId, String selector, boolean noLocal) {
100 synchronized (this) {
101 Subscription subscription = channel.addSubscription(destination, getId(), subscriptionId, noLocal);
102 subscription.setSelector(selector);
103 subscriptions.putIfAbsent(subscriptionId, subscription);
104 }
105 }
106
107 public void unsubscribe(Channel channel, String subscriptionId) {
108 synchronized(this) {
109 subscriptions.remove(subscriptionId);
110 channel.removeSubscription(subscriptionId);
111 }
112 }
113
114
115 public void publish(TopicId to, Channel fromChannel, AsyncMessage msg) {
116 int tail = to.depth()-id.depth();
117
118 switch(tail) {
119 case 0:
120 for (Subscription subscription : subscriptions.values()) {
121 AsyncMessage m = msg.clone();
122 subscription.deliver(fromChannel, m);
123 }
124
125 break;
126
127 case 1:
128 if (wild != null) {
129 for (Subscription subscription : wild.subscriptions.values()) {
130 AsyncMessage m = msg.clone();
131 subscription.deliver(fromChannel, m);
132 }
133 }
134
135 default: {
136 if (wildWild != null) {
137 for (Subscription subscription : wildWild.subscriptions.values()) {
138 AsyncMessage m = msg.clone();
139 subscription.deliver(fromChannel, m);
140 }
141 }
142 String next = to.getSegment(id.depth());
143 Topic topic = children.get(next);
144 if (topic != null)
145 topic.publish(to, fromChannel, msg);
146 }
147 }
148 }
149
150 @Override
151 public String toString() {
152 return id.toString() + " {" + children.values() + "}";
153 }
154 }