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.gae; 023 024import java.util.concurrent.ConcurrentHashMap; 025 026import org.granite.gravity.Channel; 027import org.granite.gravity.adapters.ServiceAdapter; 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 GAEServiceAdapter extends ServiceAdapter { 041 042 private static final Logger log = Logger.getLogger(GAEServiceAdapter.class); 043 044 private final GAETopic rootTopic = new GAETopic("/", this); 045 private transient ConcurrentHashMap<String, GAETopicId> _topicIdCache; 046 047 private boolean noLocal = false; 048 049 050 @Override 051 public void configure(XMap adapterProperties, XMap destinationProperties) throws ServiceException { 052 _topicIdCache = new ConcurrentHashMap<String, GAETopicId>(); 053 054 if (Boolean.TRUE.toString().equals(destinationProperties.get("no-local"))) 055 noLocal = true; 056 } 057 058 059 public GAETopic getTopic(GAETopicId id) { 060 return rootTopic.getChild(id); 061 } 062 063 public GAETopic getTopic(String id) { 064 GAETopicId cid = getTopicId(id); 065 if (cid.depth() == 0) 066 return null; 067 return rootTopic.getChild(cid); 068 } 069 070 public GAETopic getTopic(String id, boolean create) { 071 synchronized (this) { 072 GAETopic topic = getTopic(id); 073 074 if (topic == null && create) { 075 topic = new GAETopic(id, this); 076 rootTopic.addChild(topic); 077 log.debug("New Topic: %s", topic); 078 } 079 return topic; 080 } 081 } 082 083 public GAETopicId getTopicId(String id) { 084 GAETopicId tid = _topicIdCache.get(id); 085 if (tid == null) { 086 tid = new GAETopicId(id); 087 GAETopicId tmpTid = _topicIdCache.putIfAbsent(id, tid); 088 if(tmpTid != null) 089 tid = tmpTid; 090 } 091 return tid; 092 } 093 094 public boolean hasTopic(String id) { 095 GAETopicId cid = getTopicId(id); 096 return rootTopic.getChild(cid) != null; 097 } 098 099 @Override 100 public Object invoke(Channel fromChannel, AsyncMessage message) { 101 String topicId = GAETopicId.normalize(((String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER))); 102 103 AsyncMessage reply = null; 104 105 if (message.getBody() != null && getSecurityPolicy().canPublish(fromChannel, topicId, message)) { 106 GAETopicId tid = getTopicId(topicId); 107 108 rootTopic.publish(tid, fromChannel, message); 109 110 reply = new AcknowledgeMessage(message); 111 reply.setMessageId(message.getMessageId()); 112 } 113 else { 114 reply = new ErrorMessage(message, null); 115 ((ErrorMessage)reply).setFaultString("unknown channel"); 116 } 117 118 return reply; 119 } 120 121 @Override 122 public Object manage(Channel fromChannel, CommandMessage message) { 123 AsyncMessage reply = null; 124 125 if (message.getOperation() == CommandMessage.SUBSCRIBE_OPERATION) { 126 String subscribeTopicId = GAETopicId.normalize(((String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER))); 127 128 if (getSecurityPolicy().canSubscribe(fromChannel, subscribeTopicId, message)) { 129 GAETopic topic = getTopic(subscribeTopicId); 130 if (topic == null && getSecurityPolicy().canCreate(fromChannel, subscribeTopicId, message)) 131 topic = getTopic(subscribeTopicId, true); 132 133 if (topic != null) { 134 String subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); 135 String selector = (String)message.getHeader(CommandMessage.SELECTOR_HEADER); 136 topic.subscribe(fromChannel, message.getDestination(), subscriptionId, selector, noLocal); 137 138 reply = new AcknowledgeMessage(message); 139 } 140 else { 141 reply = new ErrorMessage(message, null); 142 ((ErrorMessage)reply).setFaultString("cannot create"); 143 } 144 } 145 else { 146 reply = new ErrorMessage(message, null); 147 ((ErrorMessage)reply).setFaultString("cannot subscribe"); 148 } 149 } 150 else if (message.getOperation() == CommandMessage.UNSUBSCRIBE_OPERATION) { 151 String unsubscribeTopicId = GAETopicId.normalize(((String)message.getHeader(AsyncMessage.SUBTOPIC_HEADER))); 152 153 GAETopic topic = getTopic(unsubscribeTopicId); 154 String subscriptionId = null; 155 if (topic != null) { 156 subscriptionId = (String)message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER); 157 topic.unsubscribe(fromChannel, subscriptionId); 158 } 159 160 reply = new AcknowledgeMessage(message); 161 reply.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId); 162 } 163 else { 164 reply = new ErrorMessage(message, null); 165 ((ErrorMessage)reply).setFaultString("unknown operation"); 166 167 } 168 169 return reply; 170 } 171}