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