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;
018
019 import java.util.GregorianCalendar;
020
021 import javax.jws.WebMethod;
022 import javax.jws.WebParam;
023 import javax.jws.WebResult;
024 import javax.jws.WebService;
025 import javax.xml.bind.JAXBElement;
026 import javax.xml.datatype.DatatypeConfigurationException;
027 import javax.xml.datatype.DatatypeConstants;
028 import javax.xml.datatype.DatatypeFactory;
029 import javax.xml.datatype.Duration;
030 import javax.xml.datatype.XMLGregorianCalendar;
031 import javax.xml.namespace.QName;
032 import javax.xml.ws.wsaddressing.W3CEndpointReference;
033
034 import org.oasis_open.docs.wsn.b_2.InvalidFilterFaultType;
035 import org.oasis_open.docs.wsn.b_2.InvalidMessageContentExpressionFaultType;
036 import org.oasis_open.docs.wsn.b_2.InvalidProducerPropertiesExpressionFaultType;
037 import org.oasis_open.docs.wsn.b_2.InvalidTopicExpressionFaultType;
038 import org.oasis_open.docs.wsn.b_2.PauseSubscription;
039 import org.oasis_open.docs.wsn.b_2.PauseSubscriptionResponse;
040 import org.oasis_open.docs.wsn.b_2.QueryExpressionType;
041 import org.oasis_open.docs.wsn.b_2.Renew;
042 import org.oasis_open.docs.wsn.b_2.RenewResponse;
043 import org.oasis_open.docs.wsn.b_2.ResumeSubscription;
044 import org.oasis_open.docs.wsn.b_2.ResumeSubscriptionResponse;
045 import org.oasis_open.docs.wsn.b_2.Subscribe;
046 import org.oasis_open.docs.wsn.b_2.SubscribeCreationFailedFaultType;
047 import org.oasis_open.docs.wsn.b_2.TopicExpressionType;
048 import org.oasis_open.docs.wsn.b_2.UnableToDestroySubscriptionFaultType;
049 import org.oasis_open.docs.wsn.b_2.UnacceptableInitialTerminationTimeFaultType;
050 import org.oasis_open.docs.wsn.b_2.UnacceptableTerminationTimeFaultType;
051 import org.oasis_open.docs.wsn.b_2.Unsubscribe;
052 import org.oasis_open.docs.wsn.b_2.UnsubscribeResponse;
053 import org.oasis_open.docs.wsn.b_2.UseRaw;
054 import org.oasis_open.docs.wsn.b_2.UnrecognizedPolicyRequestFaultType;
055 import org.oasis_open.docs.wsn.bw_2.InvalidFilterFault;
056 import org.oasis_open.docs.wsn.bw_2.InvalidMessageContentExpressionFault;
057 import org.oasis_open.docs.wsn.bw_2.InvalidProducerPropertiesExpressionFault;
058 import org.oasis_open.docs.wsn.bw_2.InvalidTopicExpressionFault;
059 import org.oasis_open.docs.wsn.bw_2.PausableSubscriptionManager;
060 import org.oasis_open.docs.wsn.bw_2.PauseFailedFault;
061 import org.oasis_open.docs.wsn.bw_2.ResumeFailedFault;
062 import org.oasis_open.docs.wsn.bw_2.SubscribeCreationFailedFault;
063 import org.oasis_open.docs.wsn.bw_2.TopicExpressionDialectUnknownFault;
064 import org.oasis_open.docs.wsn.bw_2.TopicNotSupportedFault;
065 import org.oasis_open.docs.wsn.bw_2.UnableToDestroySubscriptionFault;
066 import org.oasis_open.docs.wsn.bw_2.UnacceptableInitialTerminationTimeFault;
067 import org.oasis_open.docs.wsn.bw_2.UnacceptableTerminationTimeFault;
068 import org.oasis_open.docs.wsn.bw_2.UnrecognizedPolicyRequestFault;
069 import org.oasis_open.docs.wsn.bw_2.UnsupportedPolicyRequestFault;
070 import org.oasis_open.docs.wsrf.rw_2.ResourceUnknownFault;
071
072 @WebService(endpointInterface = "org.oasis_open.docs.wsn.bw_2.PausableSubscriptionManager")
073 public abstract class AbstractSubscription extends AbstractEndpoint implements PausableSubscriptionManager {
074
075 public static final String WSN_URI = "http://docs.oasis-open.org/wsn/b-2";
076
077 public static final String XPATH1_URI = "http://www.w3.org/TR/1999/REC-xpath-19991116";
078
079 public static final QName QNAME_TOPIC_EXPRESSION = new QName(WSN_URI, "TopicExpression");
080
081 public static final QName QNAME_PRODUCER_PROPERTIES = new QName(WSN_URI, "ProducerProperties");
082
083 public static final QName QNAME_MESSAGE_CONTENT = new QName(WSN_URI, "MessageContent");
084
085 public static final QName QNAME_USE_RAW = new QName(WSN_URI, "UseRaw");
086
087 protected DatatypeFactory datatypeFactory;
088
089 protected XMLGregorianCalendar terminationTime;
090
091 protected boolean useRaw;
092
093 protected TopicExpressionType topic;
094
095 protected QueryExpressionType contentFilter;
096
097 protected W3CEndpointReference consumerReference;
098
099 protected AbstractNotificationBroker broker;
100
101 public AbstractSubscription(String name) {
102 super(name);
103 try {
104 this.datatypeFactory = DatatypeFactory.newInstance();
105 } catch (DatatypeConfigurationException e) {
106 throw new RuntimeException("Unable to initialize subscription", e);
107 }
108 }
109
110 /**
111 *
112 * @param renewRequest
113 * @return returns org.oasis_open.docs.wsn.b_1.RenewResponse
114 * @throws UnacceptableTerminationTimeFault
115 * @throws ResourceUnknownFault
116 */
117 @WebMethod(operationName = "Renew")
118 @WebResult(name = "RenewResponse",
119 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
120 partName = "RenewResponse")
121 public RenewResponse renew(
122 @WebParam(name = "Renew",
123 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
124 partName = "RenewRequest")
125 Renew renewRequest) throws ResourceUnknownFault, UnacceptableTerminationTimeFault {
126
127 XMLGregorianCalendar time = validateTerminationTime(renewRequest.getTerminationTime());
128 renew(time);
129 RenewResponse response = new RenewResponse();
130 response.setTerminationTime(time);
131 response.setCurrentTime(getCurrentTime());
132 return response;
133 }
134
135 /**
136 *
137 * @param unsubscribeRequest
138 * @return returns org.oasis_open.docs.wsn.b_1.UnsubscribeResponse
139 * @throws UnableToDestroySubscriptionFault
140 * @throws ResourceUnknownFault
141 */
142 @WebMethod(operationName = "Unsubscribe")
143 @WebResult(name = "UnsubscribeResponse",
144 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
145 partName = "UnsubscribeResponse")
146 public UnsubscribeResponse unsubscribe(
147 @WebParam(name = "Unsubscribe",
148 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
149 partName = "UnsubscribeRequest")
150 Unsubscribe unsubscribeRequest) throws ResourceUnknownFault, UnableToDestroySubscriptionFault {
151
152 broker.unsubscribe(getAddress());
153 return new UnsubscribeResponse();
154 }
155
156 /**
157 *
158 * @param pauseSubscriptionRequest
159 * @return returns org.oasis_open.docs.wsn.b_1.PauseSubscriptionResponse
160 * @throws PauseFailedFault
161 * @throws ResourceUnknownFault
162 */
163 @WebMethod(operationName = "PauseSubscription")
164 @WebResult(name = "PauseSubscriptionResponse",
165 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
166 partName = "PauseSubscriptionResponse")
167 public PauseSubscriptionResponse pauseSubscription(
168 @WebParam(name = "PauseSubscription",
169 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
170 partName = "PauseSubscriptionRequest")
171 PauseSubscription pauseSubscriptionRequest) throws PauseFailedFault, ResourceUnknownFault {
172
173 pause();
174 return new PauseSubscriptionResponse();
175 }
176
177 /**
178 *
179 * @param resumeSubscriptionRequest
180 * @return returns org.oasis_open.docs.wsn.b_1.ResumeSubscriptionResponse
181 * @throws ResumeFailedFault
182 * @throws ResourceUnknownFault
183 */
184 @WebMethod(operationName = "ResumeSubscription")
185 @WebResult(name = "ResumeSubscriptionResponse",
186 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
187 partName = "ResumeSubscriptionResponse")
188 public ResumeSubscriptionResponse resumeSubscription(
189 @WebParam(name = "ResumeSubscription",
190 targetNamespace = "http://docs.oasis-open.org/wsn/b-2",
191 partName = "ResumeSubscriptionRequest")
192 ResumeSubscription resumeSubscriptionRequest) throws ResourceUnknownFault, ResumeFailedFault {
193
194 resume();
195 return new ResumeSubscriptionResponse();
196 }
197
198 protected XMLGregorianCalendar validateInitialTerminationTime(String value)
199 throws UnacceptableInitialTerminationTimeFault {
200 XMLGregorianCalendar tt = parseTerminationTime(value);
201 if (tt == null) {
202 UnacceptableInitialTerminationTimeFaultType fault = new UnacceptableInitialTerminationTimeFaultType();
203 throw new UnacceptableInitialTerminationTimeFault("Unable to parse initial termination time: '" + value
204 + "'", fault);
205 }
206 XMLGregorianCalendar ct = getCurrentTime();
207 int c = tt.compare(ct);
208 if (c == DatatypeConstants.LESSER || c == DatatypeConstants.EQUAL) {
209 UnacceptableInitialTerminationTimeFaultType fault = new UnacceptableInitialTerminationTimeFaultType();
210 fault.setMinimumTime(ct);
211 throw new UnacceptableInitialTerminationTimeFault("Invalid initial termination time", fault);
212 }
213 return tt;
214 }
215
216 protected XMLGregorianCalendar validateTerminationTime(String value) throws UnacceptableTerminationTimeFault {
217 XMLGregorianCalendar tt = parseTerminationTime(value);
218 if (tt == null) {
219 UnacceptableTerminationTimeFaultType fault = new UnacceptableTerminationTimeFaultType();
220 throw new UnacceptableTerminationTimeFault("Unable to parse termination time: '" + value + "'", fault);
221 }
222 XMLGregorianCalendar ct = getCurrentTime();
223 int c = tt.compare(ct);
224 if (c == DatatypeConstants.LESSER || c == DatatypeConstants.EQUAL) {
225 UnacceptableTerminationTimeFaultType fault = new UnacceptableTerminationTimeFaultType();
226 fault.setMinimumTime(ct);
227 throw new UnacceptableTerminationTimeFault("Invalid termination time", fault);
228 }
229 return tt;
230 }
231
232 protected XMLGregorianCalendar parseTerminationTime(String value) {
233 try {
234 Duration d = datatypeFactory.newDuration(value);
235 XMLGregorianCalendar c = getCurrentTime();
236 c.add(d);
237 return c;
238 } catch (Exception e) {
239 // Ignore
240 }
241 try {
242 Duration d = datatypeFactory.newDurationDayTime(value);
243 XMLGregorianCalendar c = getCurrentTime();
244 c.add(d);
245 return c;
246 } catch (Exception e) {
247 // Ignore
248 }
249 try {
250 Duration d = datatypeFactory.newDurationYearMonth(value);
251 XMLGregorianCalendar c = getCurrentTime();
252 c.add(d);
253 return c;
254 } catch (Exception e) {
255 // Ignore
256 }
257 try {
258 return datatypeFactory.newXMLGregorianCalendar(value);
259 } catch (Exception e) {
260 // Ignore
261 }
262 return null;
263 }
264
265 protected XMLGregorianCalendar getCurrentTime() {
266 return datatypeFactory.newXMLGregorianCalendar(new GregorianCalendar());
267 }
268
269 public XMLGregorianCalendar getTerminationTime() {
270 return terminationTime;
271 }
272
273 public void setTerminationTime(XMLGregorianCalendar terminationTime) {
274 this.terminationTime = terminationTime;
275 }
276
277 public void create(Subscribe subscribeRequest) throws InvalidFilterFault, InvalidMessageContentExpressionFault,
278 InvalidProducerPropertiesExpressionFault, InvalidTopicExpressionFault, SubscribeCreationFailedFault,
279 TopicExpressionDialectUnknownFault, TopicNotSupportedFault, UnacceptableInitialTerminationTimeFault,
280 UnrecognizedPolicyRequestFault, UnsupportedPolicyRequestFault {
281 validateSubscription(subscribeRequest);
282 start();
283 }
284
285 protected abstract void start() throws SubscribeCreationFailedFault;
286
287 protected abstract void pause() throws PauseFailedFault;
288
289 protected abstract void resume() throws ResumeFailedFault;
290
291 protected abstract void renew(XMLGregorianCalendar time) throws UnacceptableTerminationTimeFault;
292
293 protected void unsubscribe() throws UnableToDestroySubscriptionFault {
294 try {
295 unregister();
296 } catch (EndpointRegistrationException e) {
297 UnableToDestroySubscriptionFaultType fault = new UnableToDestroySubscriptionFaultType();
298 throw new UnableToDestroySubscriptionFault("Error unregistering endpoint", fault, e);
299 }
300 }
301
302 protected String createAddress() {
303 return "http://servicemix.org/wsnotification/Subscription/" + getName();
304 }
305
306 protected void validateSubscription(Subscribe subscribeRequest) throws InvalidFilterFault,
307 InvalidMessageContentExpressionFault, InvalidProducerPropertiesExpressionFault,
308 InvalidTopicExpressionFault, SubscribeCreationFailedFault, TopicExpressionDialectUnknownFault,
309 TopicNotSupportedFault, UnacceptableInitialTerminationTimeFault, UnrecognizedPolicyRequestFault,
310 UnsupportedPolicyRequestFault {
311 // Check consumer reference
312 consumerReference = subscribeRequest.getConsumerReference();
313 // Check terminationTime
314 if (subscribeRequest.getInitialTerminationTime() != null
315 && !subscribeRequest.getInitialTerminationTime().isNil()
316 && subscribeRequest.getInitialTerminationTime().getValue() != null) {
317 String strTerminationTime = subscribeRequest.getInitialTerminationTime().getValue();
318 terminationTime = validateInitialTerminationTime(strTerminationTime.trim());
319 }
320 // Check filter
321 if (subscribeRequest.getFilter() != null) {
322 for (Object f : subscribeRequest.getFilter().getAny()) {
323 JAXBElement e = null;
324 if (f instanceof JAXBElement) {
325 e = (JAXBElement) f;
326 f = e.getValue();
327 }
328 if (f instanceof TopicExpressionType) {
329 if (!e.getName().equals(QNAME_TOPIC_EXPRESSION)) {
330 InvalidTopicExpressionFaultType fault = new InvalidTopicExpressionFaultType();
331 throw new InvalidTopicExpressionFault("Unrecognized TopicExpression: " + e, fault);
332 }
333 topic = (TopicExpressionType) f;
334 } else if (f instanceof QueryExpressionType) {
335 if (e != null && e.getName().equals(QNAME_PRODUCER_PROPERTIES)) {
336 InvalidProducerPropertiesExpressionFaultType fault =
337 new InvalidProducerPropertiesExpressionFaultType();
338 throw new InvalidProducerPropertiesExpressionFault("ProducerProperties are not supported",
339 fault);
340 } else if (e != null && e.getName().equals(QNAME_MESSAGE_CONTENT)) {
341 if (contentFilter != null) {
342 InvalidMessageContentExpressionFaultType fault =
343 new InvalidMessageContentExpressionFaultType();
344 throw new InvalidMessageContentExpressionFault(
345 "Only one MessageContent filter can be specified", fault);
346 }
347 contentFilter = (QueryExpressionType) f;
348 // Defaults to XPath 1.0
349 if (contentFilter.getDialect() == null) {
350 contentFilter.setDialect(XPATH1_URI);
351 }
352 } else {
353 InvalidFilterFaultType fault = new InvalidFilterFaultType();
354 throw new InvalidFilterFault("Unrecognized filter: " + (e != null ? e.getName() : f), fault);
355 }
356 } else {
357 InvalidFilterFaultType fault = new InvalidFilterFaultType();
358 throw new InvalidFilterFault("Unrecognized filter: " + (e != null ? e.getName() : f), fault);
359 }
360 }
361 }
362 // Check policy
363 if (subscribeRequest.getSubscriptionPolicy() != null) {
364 for (Object p : subscribeRequest.getSubscriptionPolicy().getAny()) {
365 JAXBElement e = null;
366 if (p instanceof JAXBElement) {
367 e = (JAXBElement) p;
368 p = e.getValue();
369 }
370 if (p instanceof UseRaw) {
371 useRaw = true;
372 } else {
373 UnrecognizedPolicyRequestFaultType fault = new UnrecognizedPolicyRequestFaultType();
374 throw new UnrecognizedPolicyRequestFault("Unrecognized policy: " + p, fault);
375 }
376 }
377 }
378 // Check all parameters
379 if (consumerReference == null) {
380 SubscribeCreationFailedFaultType fault = new SubscribeCreationFailedFaultType();
381 throw new SubscribeCreationFailedFault("Invalid ConsumerReference: null", fault);
382 }
383 // TODO check we can resolve endpoint
384 if (topic == null) {
385 InvalidFilterFaultType fault = new InvalidFilterFaultType();
386 throw new InvalidFilterFault("Must specify a topic to subscribe on", fault);
387 }
388 if (contentFilter != null && !contentFilter.getDialect().equals(XPATH1_URI)) {
389 InvalidMessageContentExpressionFaultType fault = new InvalidMessageContentExpressionFaultType();
390 throw new InvalidMessageContentExpressionFault("Unsupported MessageContent dialect: '"
391 + contentFilter.getDialect() + "'", fault);
392 }
393 if (terminationTime != null) {
394 UnacceptableInitialTerminationTimeFaultType fault = new UnacceptableInitialTerminationTimeFaultType();
395 throw new UnacceptableInitialTerminationTimeFault("InitialTerminationTime is not supported", fault);
396 }
397 }
398
399 public AbstractNotificationBroker getBroker() {
400 return broker;
401 }
402
403 public void setBroker(AbstractNotificationBroker broker) {
404 this.broker = broker;
405 }
406 }