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 }