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
026 import org.granite.gravity.AsyncPublishedMessage;
027 import org.granite.gravity.Channel;
028 import org.granite.gravity.MessagePublishingException;
029 import org.granite.logging.Logger;
030 import org.granite.messaging.service.ServiceException;
031 import org.granite.util.XMap;
032
033 import flex.messaging.messages.AcknowledgeMessage;
034 import flex.messaging.messages.AsyncMessage;
035 import flex.messaging.messages.CommandMessage;
036 import flex.messaging.messages.ErrorMessage;
037
038 /**
039 * @author William DRAI
040 */
041 public 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 }