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.apache.servicemix.wsn.jaxws.InvalidFilterFault;
045    import org.apache.servicemix.wsn.jaxws.InvalidMessageContentExpressionFault;
046    import org.apache.servicemix.wsn.jaxws.InvalidProducerPropertiesExpressionFault;
047    import org.apache.servicemix.wsn.jaxws.InvalidTopicExpressionFault;
048    import org.apache.servicemix.wsn.jaxws.PauseFailedFault;
049    import org.apache.servicemix.wsn.jaxws.ResumeFailedFault;
050    import org.apache.servicemix.wsn.jaxws.SubscribeCreationFailedFault;
051    import org.apache.servicemix.wsn.jaxws.TopicExpressionDialectUnknownFault;
052    import org.apache.servicemix.wsn.jaxws.TopicNotSupportedFault;
053    import org.apache.servicemix.wsn.jaxws.UnableToDestroySubscriptionFault;
054    import org.apache.servicemix.wsn.jaxws.UnacceptableInitialTerminationTimeFault;
055    import org.apache.servicemix.wsn.jaxws.UnacceptableTerminationTimeFault;
056    import org.oasis_open.docs.wsn.b_2.InvalidTopicExpressionFaultType;
057    import org.oasis_open.docs.wsn.b_2.PauseFailedFaultType;
058    import org.oasis_open.docs.wsn.b_2.ResumeFailedFaultType;
059    import org.oasis_open.docs.wsn.b_2.Subscribe;
060    import org.oasis_open.docs.wsn.b_2.SubscribeCreationFailedFaultType;
061    import org.oasis_open.docs.wsn.b_2.UnableToDestroySubscriptionFaultType;
062    import org.oasis_open.docs.wsn.b_2.UnacceptableTerminationTimeFaultType;
063    
064    public abstract class JmsSubscription extends AbstractSubscription implements MessageListener {
065    
066        private static Log log = LogFactory.getLog(JmsSubscription.class);
067    
068        private Connection connection;
069    
070        private Session session;
071    
072        private JmsTopicExpressionConverter topicConverter;
073    
074        private Topic jmsTopic;
075    
076        public JmsSubscription(String name) {
077            super(name);
078            topicConverter = new JmsTopicExpressionConverter();
079        }
080    
081        protected void start() throws SubscribeCreationFailedFault {
082            try {
083                session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
084                MessageConsumer consumer = session.createConsumer(jmsTopic);
085                consumer.setMessageListener(this);
086            } catch (JMSException e) {
087                SubscribeCreationFailedFaultType fault = new SubscribeCreationFailedFaultType();
088                throw new SubscribeCreationFailedFault("Error starting subscription", fault, e);
089            }
090        }
091    
092        @Override
093        protected void validateSubscription(Subscribe subscribeRequest) throws InvalidFilterFault,
094                InvalidMessageContentExpressionFault, InvalidProducerPropertiesExpressionFault,
095                InvalidTopicExpressionFault, SubscribeCreationFailedFault, TopicExpressionDialectUnknownFault,
096                TopicNotSupportedFault, UnacceptableInitialTerminationTimeFault {
097            super.validateSubscription(subscribeRequest);
098            try {
099                jmsTopic = topicConverter.toActiveMQTopic(topic);
100            } catch (InvalidTopicException e) {
101                InvalidTopicExpressionFaultType fault = new InvalidTopicExpressionFaultType();
102                throw new InvalidTopicExpressionFault(e.getMessage(), fault);
103            }
104        }
105    
106        @Override
107        protected void pause() throws PauseFailedFault {
108            if (session == null) {
109                PauseFailedFaultType fault = new PauseFailedFaultType();
110                throw new PauseFailedFault("Subscription is already paused", fault);
111            } else {
112                try {
113                    session.close();
114                } catch (JMSException e) {
115                    PauseFailedFaultType fault = new PauseFailedFaultType();
116                    throw new PauseFailedFault("Error pausing subscription", fault, e);
117                } finally {
118                    session = null;
119                }
120            }
121        }
122    
123        @Override
124        protected void resume() throws ResumeFailedFault {
125            if (session != null) {
126                ResumeFailedFaultType fault = new ResumeFailedFaultType();
127                throw new ResumeFailedFault("Subscription is already running", fault);
128            } else {
129                try {
130                    session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
131                    MessageConsumer consumer = session.createConsumer(jmsTopic);
132                    consumer.setMessageListener(this);
133                } catch (JMSException e) {
134                    ResumeFailedFaultType fault = new ResumeFailedFaultType();
135                    throw new ResumeFailedFault("Error resuming subscription", fault, e);
136                }
137            }
138        }
139    
140        @Override
141        protected void renew(XMLGregorianCalendar terminationTime) throws UnacceptableTerminationTimeFault {
142            UnacceptableTerminationTimeFaultType fault = new UnacceptableTerminationTimeFaultType();
143            throw new UnacceptableTerminationTimeFault("TerminationTime is not supported", fault);
144        }
145    
146        @Override
147        protected void unsubscribe() throws UnableToDestroySubscriptionFault {
148            super.unsubscribe();
149            if (session != null) {
150                try {
151                    session.close();
152                } catch (JMSException e) {
153                    UnableToDestroySubscriptionFaultType fault = new UnableToDestroySubscriptionFaultType();
154                    throw new UnableToDestroySubscriptionFault("Unable to unsubscribe", fault, e);
155                } finally {
156                    session = null;
157                }
158            }
159        }
160    
161        public Connection getConnection() {
162            return connection;
163        }
164    
165        public void setConnection(Connection connection) {
166            this.connection = connection;
167        }
168    
169        public void onMessage(Message jmsMessage) {
170            try {
171                TextMessage text = (TextMessage) jmsMessage;
172                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
173                factory.setNamespaceAware(true);
174                Document doc = factory.newDocumentBuilder().parse(new InputSource(new StringReader(text.getText())));
175                Element root = doc.getDocumentElement();
176                Element holder = (Element) root.getElementsByTagNameNS(WSN_URI, "NotificationMessage").item(0);
177                Element message = (Element) holder.getElementsByTagNameNS(WSN_URI, "Message").item(0);
178                Element content = null;
179                for (int i = 0; i < message.getChildNodes().getLength(); i++) {
180                    if (message.getChildNodes().item(i) instanceof Element) {
181                        content = (Element) message.getChildNodes().item(i);
182                        break;
183                    }
184                }
185                boolean match = doFilter(content);
186                if (match) {
187                    if (useRaw) {
188                        doNotify(content);
189                    } else {
190                        doNotify(root);
191                    }
192                }
193            } catch (Exception e) {
194                log.warn("Error notifying consumer", e);
195            }
196        }
197    
198        protected boolean doFilter(Element content) {
199            if (contentFilter != null) {
200                if (!contentFilter.getDialect().equals(XPATH1_URI)) {
201                    throw new IllegalStateException("Unsupported dialect: " + contentFilter.getDialect());
202                }
203                try {
204                    XPathFactory xpfactory = XPathFactory.newInstance();
205                    XPath xpath = xpfactory.newXPath();
206                    XPathExpression exp = xpath.compile(contentFilter.getContent().get(0).toString());
207                    Boolean ret = (Boolean) exp.evaluate(content, XPathConstants.BOOLEAN);
208                    return ret.booleanValue();
209                } catch (XPathExpressionException e) {
210                    log.warn("Could not filter notification", e);
211                }
212                return false;
213            }
214            return true;
215        }
216    
217        protected abstract void doNotify(Element content);
218    
219    }