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; 024 025import org.granite.gravity.AsyncPublishedMessage; 026import org.granite.gravity.Channel; 027import org.granite.gravity.MessagePublishingException; 028import org.granite.logging.Logger; 029import org.granite.messaging.service.ServiceException; 030import org.granite.util.XMap; 031 032import flex.messaging.messages.AcknowledgeMessage; 033import flex.messaging.messages.AsyncMessage; 034import flex.messaging.messages.CommandMessage; 035import flex.messaging.messages.ErrorMessage; 036 037/** 038 * @author William DRAI 039 */ 040public class SimpleServiceAdapter extends ServiceAdapter { 041 042 private static final Logger log = Logger.getLogger(SimpleServiceAdapter.class); 043 044 private final Topic rootTopic = new Topic("/", this); 045 private transient ConcurrentHashMap<String, TopicId> _topicIdCache; 046 047 private boolean noLocal = false; 048 049 @Override 050 public void configure(XMap adapterProperties, XMap destinationProperties) throws ServiceException { 051 super.configure(adapterProperties, destinationProperties); 052 053 _topicIdCache = new ConcurrentHashMap<String, TopicId>(); 054 055 if (Boolean.TRUE.toString().equals(destinationProperties.get("no-local"))) 056 noLocal = true; 057 } 058 059 060 public Topic getTopic(TopicId id) { 061 return rootTopic.getChild(id); 062 } 063 064 public Topic getTopic(String id) { 065 TopicId cid = getTopicId(id); 066 if (cid.depth() == 0) 067 return null; 068 return rootTopic.getChild(cid); 069 } 070 071 public Topic getTopic(String id, boolean create) { 072 synchronized (this) { 073 Topic topic = getTopic(id); 074 075 if (topic == null && create) { 076 topic = new Topic(id, this); 077 rootTopic.addChild(topic); 078 log.debug("New Topic: %s", topic); 079 } 080 return topic; 081 } 082 } 083 084 public TopicId getTopicId(String id) { 085 TopicId tid = _topicIdCache.get(id); 086 if (tid == null) { 087 tid = new TopicId(id); 088 TopicId tmpTid = _topicIdCache.putIfAbsent(id, tid); 089 if (tmpTid != null) 090 tid = tmpTid; 091 } 092 return tid; 093 } 094 095 public boolean hasTopic(String id) { 096 TopicId cid = getTopicId(id); 097 return rootTopic.getChild(cid) != null; 098 } 099 100 @Override 101 public Object invoke(Channel fromChannel, AsyncMessage message) { 102 String topicId = TopicId.normalize(((String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER))); 103 104 AsyncMessage reply = null; 105 106 if (getSecurityPolicy().canPublish(fromChannel, topicId, message)) { 107 TopicId tid = getTopicId(topicId); 108 109 try { 110 fromChannel.publish(new AsyncPublishedMessage(rootTopic, tid, message)); 111 reply = new AcknowledgeMessage(message); 112 reply.setMessageId(message.getMessageId()); 113 } 114 catch (MessagePublishingException e) { 115 log.error(e, "Error while publishing message: %s from channel %s to topic: %s", message, fromChannel, tid); 116 reply = new ErrorMessage(message, null); 117 ((ErrorMessage)reply).setFaultString("Server.Publish.Error"); 118 } 119 } 120 else { 121 log.warn("Channel %s tried to publish a message to topic %s", fromChannel, topicId); 122 reply = new ErrorMessage(message, null); 123 ((ErrorMessage)reply).setFaultString("Server.Publish.Denied"); 124 } 125 126 return reply; 127 } 128 129 @Override 130 public Object manage(Channel fromChannel, CommandMessage message) { 131 AsyncMessage reply = null; 132 133 if (message.getOperation() == CommandMessage.SUBSCRIBE_OPERATION) { 134 String subscribeTopicId = TopicId.normalize(((String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER))); 135 136 if (getSecurityPolicy().canSubscribe(fromChannel, subscribeTopicId, message)) { 137 Topic topic = getTopic(subscribeTopicId); 138 if (topic == null && getSecurityPolicy().canCreate(fromChannel, subscribeTopicId, message)) 139 topic = getTopic(subscribeTopicId, true); 140 141 if (topic != null) { 142 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); 143 String selector = (String)message.getHeader(CommandMessage.SELECTOR_HEADER); 144 if (subscriptionId == null) 145 log.warn("No subscriptionId for subscription message"); 146 else 147 topic.subscribe(fromChannel, message.getDestination(), subscriptionId, selector, noLocal); 148 149 reply = new AcknowledgeMessage(message); 150 } 151 else { 152 reply = new ErrorMessage(message, null); 153 ((ErrorMessage)reply).setFaultString("Server.CreateTopic.Denied"); 154 } 155 } 156 else { 157 reply = new ErrorMessage(message, null); 158 ((ErrorMessage)reply).setFaultString("Server.Subscribe.Denied"); 159 } 160 } 161 else if (message.getOperation() == CommandMessage.UNSUBSCRIBE_OPERATION) { 162 String unsubscribeTopicId = TopicId.normalize(((String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER))); 163 164 Topic topic = getTopic(unsubscribeTopicId); 165 String subscriptionId = null; 166 if (topic != null) { 167 subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); 168 if (subscriptionId == null) 169 log.warn("No subscriptionId for unsubscription message"); 170 else 171 topic.unsubscribe(fromChannel, subscriptionId); 172 } 173 174 reply = new AcknowledgeMessage(message); 175 reply.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId); 176 } 177 else { 178 reply = new ErrorMessage(message, null); 179 ((ErrorMessage)reply).setFaultString("unknown operation"); 180 181 } 182 183 return reply; 184 } 185}