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;
023
024import java.io.IOException;
025import java.io.Serializable;
026import java.lang.reflect.Constructor;
027
028import org.granite.context.GraniteContext;
029import org.granite.gravity.selector.GravityMessageSelector;
030import org.granite.gravity.selector.MessageSelector;
031import org.granite.logging.Logger;
032import org.granite.util.TypeUtil;
033
034import flex.messaging.messages.AsyncMessage;
035import flex.messaging.messages.CommandMessage;
036import flex.messaging.messages.Message;
037
038/**
039 * @author William DRAI
040 */
041public class Subscription implements Serializable {
042
043        private static final long serialVersionUID = -8527072003319223252L;
044
045        private static final Logger log = Logger.getLogger(Subscription.class);
046        
047    private final Channel channel;
048    private final String destination;
049    private final String subTopicId;
050    private final String subscriptionId;
051    private String selectorText;
052    private String selectorClassName;
053    private transient Constructor<?> messageSelectorConstructor;
054    private transient MessageSelector selector;
055    private final boolean noLocal;
056
057
058    public Subscription(Channel channel, String destination, String subTopicId, String subscriptionId, boolean noLocal) {
059        super();
060        this.channel = channel;
061        this.destination = destination;
062        this.subTopicId = subTopicId;
063        this.subscriptionId = subscriptionId;
064        this.noLocal = noLocal;
065    }
066    
067    private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException {
068        in.defaultReadObject();
069        if (selectorClassName != null) {
070                try {
071                        messageSelectorConstructor = TypeUtil.getConstructor(selectorClassName, new Class<?>[] { String.class });
072                }
073                catch (NoSuchMethodException e) {
074                        throw new IOException("Could not get message selector: " + selectorClassName);
075                }
076        }
077        parseSelector();
078    }
079
080    public Channel getChannel() {
081        return channel;
082    }
083
084    public String getSubTopicId() {
085        return subTopicId;
086    }
087
088    public String getSubscriptionId() {
089        return subscriptionId;
090    }
091
092
093    public void setSelector(String selector) {
094        this.selectorText = selector;
095        parseSelector();
096    }
097    
098    private void parseSelector() {
099        if (selectorText != null) {
100            try {
101                Constructor<?> messageSelectorConstructor = this.messageSelectorConstructor;
102                if (messageSelectorConstructor == null) {
103                        GraniteContext context = GraniteContext.getCurrentInstance();
104                        if (context == null)
105                                throw new IllegalStateException("Cannot parse selector outside of GDS context");
106                        messageSelectorConstructor = context.getGraniteConfig().getMessageSelectorConstructor();
107                }
108                if (messageSelectorConstructor == null)
109                    this.selector = new GravityMessageSelector(selectorText);
110                else
111                    this.selector = (MessageSelector)messageSelectorConstructor.newInstance(selectorText);
112            }
113            catch (Exception e) {
114                throw new RuntimeException("Could not create message selector", e);
115            }
116        }
117    }
118
119
120    public boolean deliver(Channel fromClient, AsyncMessage message) {
121        if (noLocal && fromClient.getId().equals(channel.getId()))
122                return false;
123        
124        if (selector == null || selector.accept(message)) {
125            try {
126                        message.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId);
127                                getChannel().receive(message);
128                    return true;
129                        } catch (MessageReceivingException e) {
130                                log.error(e, "Could not deliver message");
131                        }
132        }
133
134        return false;
135    }
136
137    public Message getUnsubscribeMessage() {
138        CommandMessage unsubscribeMessage = new CommandMessage();
139        unsubscribeMessage.setOperation(CommandMessage.UNSUBSCRIBE_OPERATION);
140        unsubscribeMessage.setClientId(getChannel().getId());
141        unsubscribeMessage.setDestination(destination);
142        unsubscribeMessage.setHeader(AsyncMessage.SUBTOPIC_HEADER, getSubTopicId());
143        unsubscribeMessage.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, getSubscriptionId());
144        return unsubscribeMessage;
145    }
146    
147
148    @Override
149    public boolean equals(Object o) {
150        if (!o.getClass().equals(Subscription.class))
151            return false;
152
153        Subscription s = (Subscription)o;
154        return getChannel().equals(s.getChannel()) && getSubscriptionId().equals(s.getSubscriptionId());
155    }
156
157        @Override
158        public String toString() {
159                return subscriptionId + ":" + subTopicId;
160        }
161}