001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.wsn.jms;
018    
019    import java.io.StringReader;
020    
021    import javax.jms.Connection;
022    import javax.jms.JMSException;
023    import javax.jms.Message;
024    import javax.jms.MessageConsumer;
025    import javax.jms.MessageListener;
026    import javax.jms.Session;
027    import javax.jms.TextMessage;
028    import javax.jms.Topic;
029    import javax.xml.datatype.XMLGregorianCalendar;
030    import javax.xml.parsers.DocumentBuilderFactory;
031    import javax.xml.xpath.XPath;
032    import javax.xml.xpath.XPathConstants;
033    import javax.xml.xpath.XPathExpression;
034    import javax.xml.xpath.XPathExpressionException;
035    import javax.xml.xpath.XPathFactory;
036    
037    import org.w3c.dom.Document;
038    import org.w3c.dom.Element;
039    import org.xml.sax.InputSource;
040    
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.apache.servicemix.wsn.AbstractSubscription;
044    import org.oasis_open.docs.wsn.b_2.InvalidTopicExpressionFaultType;
045    import org.oasis_open.docs.wsn.b_2.PauseFailedFaultType;
046    import org.oasis_open.docs.wsn.b_2.ResumeFailedFaultType;
047    import org.oasis_open.docs.wsn.b_2.Subscribe;
048    import org.oasis_open.docs.wsn.b_2.SubscribeCreationFailedFaultType;
049    import org.oasis_open.docs.wsn.b_2.UnableToDestroySubscriptionFaultType;
050    import org.oasis_open.docs.wsn.b_2.UnacceptableTerminationTimeFaultType;
051    import org.oasis_open.docs.wsn.bw_2.InvalidFilterFault;
052    import org.oasis_open.docs.wsn.bw_2.InvalidMessageContentExpressionFault;
053    import org.oasis_open.docs.wsn.bw_2.InvalidProducerPropertiesExpressionFault;
054    import org.oasis_open.docs.wsn.bw_2.InvalidTopicExpressionFault;
055    import org.oasis_open.docs.wsn.bw_2.PauseFailedFault;
056    import org.oasis_open.docs.wsn.bw_2.ResumeFailedFault;
057    import org.oasis_open.docs.wsn.bw_2.SubscribeCreationFailedFault;
058    import org.oasis_open.docs.wsn.bw_2.TopicExpressionDialectUnknownFault;
059    import org.oasis_open.docs.wsn.bw_2.TopicNotSupportedFault;
060    import org.oasis_open.docs.wsn.bw_2.UnableToDestroySubscriptionFault;
061    import org.oasis_open.docs.wsn.bw_2.UnacceptableInitialTerminationTimeFault;
062    import org.oasis_open.docs.wsn.bw_2.UnacceptableTerminationTimeFault;
063    import org.oasis_open.docs.wsn.bw_2.UnsupportedPolicyRequestFault;
064    import org.oasis_open.docs.wsn.bw_2.UnrecognizedPolicyRequestFault;
065    
066    public abstract class JmsSubscription extends AbstractSubscription implements MessageListener {
067    
068        private static Log log = LogFactory.getLog(JmsSubscription.class);
069    
070        private Connection connection;
071    
072        private Session session;
073    
074        private JmsTopicExpressionConverter topicConverter;
075    
076        private Topic jmsTopic;
077    
078        public JmsSubscription(String name) {
079            super(name);
080            topicConverter = new JmsTopicExpressionConverter();
081        }
082    
083        protected void start() throws SubscribeCreationFailedFault {
084            try {
085                session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
086                MessageConsumer consumer = session.createConsumer(jmsTopic);
087                consumer.setMessageListener(this);
088            } catch (JMSException e) {
089                SubscribeCreationFailedFaultType fault = new SubscribeCreationFailedFaultType();
090                throw new SubscribeCreationFailedFault("Error starting subscription", fault, e);
091            }
092        }
093    
094        @Override
095        protected void validateSubscription(Subscribe subscribeRequest) throws InvalidFilterFault,
096                InvalidMessageContentExpressionFault, InvalidProducerPropertiesExpressionFault,
097                InvalidTopicExpressionFault, SubscribeCreationFailedFault, TopicExpressionDialectUnknownFault,
098                TopicNotSupportedFault, UnacceptableInitialTerminationTimeFault,
099                UnsupportedPolicyRequestFault, UnrecognizedPolicyRequestFault {
100            super.validateSubscription(subscribeRequest);
101            try {
102                jmsTopic = topicConverter.toActiveMQTopic(topic);
103            } catch (InvalidTopicException e) {
104                InvalidTopicExpressionFaultType fault = new InvalidTopicExpressionFaultType();
105                throw new InvalidTopicExpressionFault(e.getMessage(), fault);
106            }
107        }
108    
109        @Override
110        protected void pause() throws PauseFailedFault {
111            if (session == null) {
112                PauseFailedFaultType fault = new PauseFailedFaultType();
113                throw new PauseFailedFault("Subscription is already paused", fault);
114            } else {
115                try {
116                    session.close();
117                } catch (JMSException e) {
118                    PauseFailedFaultType fault = new PauseFailedFaultType();
119                    throw new PauseFailedFault("Error pausing subscription", fault, e);
120                } finally {
121                    session = null;
122                }
123            }
124        }
125    
126        @Override
127        protected void resume() throws ResumeFailedFault {
128            if (session != null) {
129                ResumeFailedFaultType fault = new ResumeFailedFaultType();
130                throw new ResumeFailedFault("Subscription is already running", fault);
131            } else {
132                try {
133                    session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
134                    MessageConsumer consumer = session.createConsumer(jmsTopic);
135                    consumer.setMessageListener(this);
136                } catch (JMSException e) {
137                    ResumeFailedFaultType fault = new ResumeFailedFaultType();
138                    throw new ResumeFailedFault("Error resuming subscription", fault, e);
139                }
140            }
141        }
142    
143        @Override
144        protected void renew(XMLGregorianCalendar terminationTime) throws UnacceptableTerminationTimeFault {
145            UnacceptableTerminationTimeFaultType fault = new UnacceptableTerminationTimeFaultType();
146            throw new UnacceptableTerminationTimeFault("TerminationTime is not supported", fault);
147        }
148    
149        @Override
150        protected void unsubscribe() throws UnableToDestroySubscriptionFault {
151            super.unsubscribe();
152            if (session != null) {
153                try {
154                    session.close();
155                } catch (JMSException e) {
156                    UnableToDestroySubscriptionFaultType fault = new UnableToDestroySubscriptionFaultType();
157                    throw new UnableToDestroySubscriptionFault("Unable to unsubscribe", fault, e);
158                } finally {
159                    session = null;
160                }
161            }
162        }
163    
164        public Connection getConnection() {
165            return connection;
166        }
167    
168        public void setConnection(Connection connection) {
169            this.connection = connection;
170        }
171    
172        public void onMessage(Message jmsMessage) {
173            try {
174                TextMessage text = (TextMessage) jmsMessage;
175                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
176                factory.setNamespaceAware(true);
177                Document doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(text.getText())));
178                Element root = doc.getDocumentElement();
179                Element holder = (Element) root.getElementsByTagNameNS(WSN_URI, "NotificationMessage").item(0);
180                Element message = (Element) holder.getElementsByTagNameNS(WSN_URI, "Message").item(0);
181                Element content = null;
182                for (int i = 0; i < message.getChildNodes().getLength(); i++) {
183                    if (message.getChildNodes().item(i) instanceof Element) {
184                        content = (Element) message.getChildNodes().item(i);
185                        break;
186                    }
187                }
188                boolean match = doFilter(content);
189                if (match) {
190                    if (useRaw) {
191                        doNotify(content);
192                    } else {
193                        doNotify(root);
194                    }
195                }
196            } catch (Exception e) {
197                log.warn("Error notifying consumer", e);
198            }
199        }
200    
201        protected boolean doFilter(Element content) {
202            if (contentFilter != null) {
203                if (!contentFilter.getDialect().equals(XPATH1_URI)) {
204                    throw new IllegalStateException("Unsupported dialect: " + contentFilter.getDialect());
205                }
206                try {
207                    XPathFactory xpfactory = XPathFactory.newInstance();
208                    XPath xpath = xpfactory.newXPath();
209                    XPathExpression exp = xpath.compile(contentFilter.getContent().get(0).toString());
210                    Boolean ret = (Boolean) exp.evaluate(content, XPathConstants.BOOLEAN);
211                    return ret.booleanValue();
212                } catch (XPathExpressionException e) {
213                    log.warn("Could not filter notification", e);
214                }
215                return false;
216            }
217            return true;
218        }
219    
220        protected abstract void doNotify(Element content);
221    
222    }