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