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 }