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 021package org.granite.gravity.adapters; 022 023import java.util.concurrent.ConcurrentHashMap; 024import java.util.concurrent.ConcurrentMap; 025 026import org.granite.gravity.Channel; 027import org.granite.gravity.Subscription; 028 029import flex.messaging.messages.AsyncMessage; 030 031/** 032 * Adapted from Greg Wilkins code (Jetty). 033 * 034 * @author William DRAI 035 */ 036public 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}