/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.wsn; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.jws.Oneway; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.management.ObjectName; import javax.xml.namespace.QName; import javax.xml.ws.wsaddressing.W3CEndpointReference; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.wsn.util.IdGenerator; import org.apache.cxf.wsn.util.WSNHelper; import org.oasis_open.docs.wsn.b_2.GetCurrentMessage; import org.oasis_open.docs.wsn.b_2.GetCurrentMessageResponse; import org.oasis_open.docs.wsn.b_2.NoCurrentMessageOnTopicFaultType; import org.oasis_open.docs.wsn.b_2.NotificationMessageHolderType; import org.oasis_open.docs.wsn.b_2.Notify; import org.oasis_open.docs.wsn.b_2.Subscribe; import org.oasis_open.docs.wsn.b_2.SubscribeCreationFailedFaultType; import org.oasis_open.docs.wsn.b_2.SubscribeResponse; import org.oasis_open.docs.wsn.br_2.PublisherRegistrationFailedFaultType; import org.oasis_open.docs.wsn.br_2.RegisterPublisher; import org.oasis_open.docs.wsn.br_2.RegisterPublisherResponse; import org.oasis_open.docs.wsn.brw_2.NotificationBroker; import org.oasis_open.docs.wsn.brw_2.PublisherRegistrationFailedFault; import org.oasis_open.docs.wsn.brw_2.PublisherRegistrationRejectedFault; import org.oasis_open.docs.wsn.brw_2.ResourceNotDestroyedFault; import org.oasis_open.docs.wsn.bw_2.InvalidFilterFault; import org.oasis_open.docs.wsn.bw_2.InvalidMessageContentExpressionFault; import org.oasis_open.docs.wsn.bw_2.InvalidProducerPropertiesExpressionFault; import org.oasis_open.docs.wsn.bw_2.InvalidTopicExpressionFault; import org.oasis_open.docs.wsn.bw_2.MultipleTopicsSpecifiedFault; import org.oasis_open.docs.wsn.bw_2.NoCurrentMessageOnTopicFault; import org.oasis_open.docs.wsn.bw_2.SubscribeCreationFailedFault; import org.oasis_open.docs.wsn.bw_2.TopicExpressionDialectUnknownFault; import org.oasis_open.docs.wsn.bw_2.TopicNotSupportedFault; import org.oasis_open.docs.wsn.bw_2.UnableToDestroySubscriptionFault; import org.oasis_open.docs.wsn.bw_2.UnacceptableInitialTerminationTimeFault; import org.oasis_open.docs.wsn.bw_2.UnrecognizedPolicyRequestFault; import org.oasis_open.docs.wsn.bw_2.UnsupportedPolicyRequestFault; import org.oasis_open.docs.wsrf.rp_2.GetResourcePropertyResponse; import org.oasis_open.docs.wsrf.rp_2.InvalidResourcePropertyQNameFaultType; import org.oasis_open.docs.wsrf.rpw_2.GetResourceProperty; import org.oasis_open.docs.wsrf.rpw_2.InvalidResourcePropertyQNameFault; import org.oasis_open.docs.wsrf.rw_2.ResourceUnavailableFault; import org.oasis_open.docs.wsrf.rw_2.ResourceUnknownFault; @WebService(endpointInterface = "org.oasis_open.docs.wsn.brw_2.NotificationBroker") public abstract class AbstractNotificationBroker extends AbstractEndpoint implements NotificationBroker, NotificationBrokerMBean, GetResourceProperty { public static final String NAMESPACE_URI = "http://docs.oasis-open.org/wsn/b-2"; public static final String PREFIX = "wsnt"; public static final QName TOPIC_EXPRESSION_QNAME = new QName(NAMESPACE_URI, "TopicExpression", PREFIX); public static final QName FIXED_TOPIC_SET_QNAME = new QName(NAMESPACE_URI, "FixedTopicSet", PREFIX); public static final QName TOPIC_EXPRESSION_DIALECT_QNAME = new QName(NAMESPACE_URI, "TopicExpressionDialect", PREFIX); public static final QName TOPIC_SET_QNAME = new QName(NAMESPACE_URI, "TopicSet", PREFIX); private static final Logger LOGGER = LogUtils.getL7dLogger(AbstractNotificationBroker.class); private IdGenerator idGenerator; private AbstractPublisher anonymousPublisher; private Map<String, AbstractPublisher> publishers; private List<AbstractPublisher> nonContactPublishers; private Map<String, AbstractSubscription> subscriptions; public AbstractNotificationBroker(String name) { super(name); idGenerator = new IdGenerator(); subscriptions = new ConcurrentHashMap<String, AbstractSubscription>(); publishers = new ConcurrentHashMap<String, AbstractPublisher>(); nonContactPublishers = new CopyOnWriteArrayList<AbstractPublisher>(); } @Override public ObjectName getMBeanName() { try { return new ObjectName("org.apache.cxf.service.wsn:type=WSNotificationBroker"); } catch (Exception e) { return null; } } public void init() throws Exception { register(); anonymousPublisher = createPublisher("AnonymousPublisher"); anonymousPublisher.setAddress(new URI(getAddress()).resolve(anonymousPublisher.getName()).toString()); anonymousPublisher.register(); } public void destroy() throws Exception { anonymousPublisher.destroy(); unregister(); } public List<String> getPublisher() { return new ArrayList<>(publishers.keySet()); } public List<String> getSubscriptions() { return new ArrayList<>(subscriptions.keySet()); } public EndpointMBean getPublisher(String name) { return publishers.get(name); } public EndpointMBean getSubscription(String name) { return subscriptions.get(name); } public EndpointMBean getAnonymousPublisher() { return anonymousPublisher; } /** * * @param notify */ @WebMethod(operationName = "Notify") @Oneway public void notify( @WebParam(name = "Notify", targetNamespace = "http://docs.oasis-open.org/wsn/b-1", partName = "Notify") Notify notify) { LOGGER.finest("Notify"); handleNotify(notify); } protected void handleNotify(Notify notify) { for (NotificationMessageHolderType messageHolder : notify.getNotificationMessage()) { W3CEndpointReference producerReference = messageHolder.getProducerReference(); AbstractPublisher publisher = getPublisher(producerReference); if (publisher != null) { publisher.notify(messageHolder); } } } protected AbstractPublisher getPublisher(W3CEndpointReference producerReference) { AbstractPublisher publisher = null; if (producerReference != null) { String address = WSNHelper.getInstance().getWSAAddress(producerReference); publisher = publishers.get(address); } if (publisher == null) { publisher = anonymousPublisher; } return publisher; } /** * * @param subscribeRequest * @return returns org.oasis_open.docs.wsn.b_1.SubscribeResponse * @throws SubscribeCreationFailedFault * @throws InvalidTopicExpressionFault * @throws TopicNotSupportedFault * @throws InvalidFilterFault * @throws InvalidProducerPropertiesExpressionFault * @throws ResourceUnknownFault * @throws InvalidMessageContentExpressionFault * @throws TopicExpressionDialectUnknownFault * @throws UnacceptableInitialTerminationTimeFault */ @WebMethod(operationName = "Subscribe") @WebResult(name = "SubscribeResponse", targetNamespace = "http://docs.oasis-open.org/wsn/b-1", partName = "SubscribeResponse") public SubscribeResponse subscribe( @WebParam(name = "Subscribe", targetNamespace = "http://docs.oasis-open.org/wsn/b-1", partName = "SubscribeRequest") Subscribe subscribeRequest) //CHECKSTYLE:OFF - WS-Notification spec throws a lot of faults throws InvalidFilterFault, InvalidMessageContentExpressionFault, InvalidProducerPropertiesExpressionFault, InvalidTopicExpressionFault, ResourceUnknownFault, SubscribeCreationFailedFault, TopicExpressionDialectUnknownFault, TopicNotSupportedFault, UnacceptableInitialTerminationTimeFault, UnsupportedPolicyRequestFault, UnrecognizedPolicyRequestFault { //CHECKSTYLE:ON LOGGER.finest("Subscribe"); return handleSubscribe(subscribeRequest, null); } public SubscribeResponse handleSubscribe( Subscribe subscribeRequest, EndpointManager manager) //CHECKSTYLE:OFF - WS-Notification spec throws a lot of faults throws InvalidFilterFault, InvalidMessageContentExpressionFault, InvalidProducerPropertiesExpressionFault, InvalidTopicExpressionFault, SubscribeCreationFailedFault, TopicExpressionDialectUnknownFault, TopicNotSupportedFault, UnacceptableInitialTerminationTimeFault, UnsupportedPolicyRequestFault, UnrecognizedPolicyRequestFault { //CHECKSTYLE:ON AbstractSubscription subscription = null; boolean success = false; try { subscription = createSubscription(idGenerator.generateSanitizedId()); subscription.setBroker(this); subscriptions.put(subscription.getAddress(), subscription); subscription.create(subscribeRequest); if (manager != null) { subscription.setManager(manager); } subscription.register(); SubscribeResponse response = new SubscribeResponse(); response.setSubscriptionReference(subscription.getEpr()); success = true; return response; } catch (EndpointRegistrationException e) { LOGGER.log(Level.WARNING, "Unable to register new endpoint", e); SubscribeCreationFailedFaultType fault = new SubscribeCreationFailedFaultType(); throw new SubscribeCreationFailedFault("Unable to register new endpoint", fault, e); } finally { if (!success && subscription != null) { if (subscription.getAddress() != null) { subscriptions.remove(subscription.getAddress()); } try { subscription.unsubscribe(); } catch (UnableToDestroySubscriptionFault e) { LOGGER.log(Level.INFO, "Error destroying subscription", e); } } } } public void unsubscribe(String address) throws UnableToDestroySubscriptionFault { AbstractSubscription subscription = subscriptions.remove(address); if (subscription != null) { subscription.unsubscribe(); } } /** * * @param getCurrentMessageRequest * @return returns org.oasis_open.docs.wsn.b_1.GetCurrentMessageResponse * @throws MultipleTopicsSpecifiedFault * @throws TopicNotSupportedFault * @throws InvalidTopicExpressionFault * @throws ResourceUnknownFault * @throws TopicExpressionDialectUnknownFault * @throws NoCurrentMessageOnTopicFault */ @WebMethod(operationName = "GetCurrentMessage") @WebResult(name = "GetCurrentMessageResponse", targetNamespace = "http://docs.oasis-open.org/wsn/b-1", partName = "GetCurrentMessageResponse") public GetCurrentMessageResponse getCurrentMessage( @WebParam(name = "GetCurrentMessage", targetNamespace = "http://docs.oasis-open.org/wsn/b-1", partName = "GetCurrentMessageRequest") GetCurrentMessage getCurrentMessageRequest) //CHECKSTYLE:OFF - WS-Notification spec throws a lot of faults throws InvalidTopicExpressionFault, MultipleTopicsSpecifiedFault, NoCurrentMessageOnTopicFault, ResourceUnknownFault, TopicExpressionDialectUnknownFault, TopicNotSupportedFault { //CHECKSTYLE:ON LOGGER.finest("GetCurrentMessage"); NoCurrentMessageOnTopicFaultType fault = new NoCurrentMessageOnTopicFaultType(); throw new NoCurrentMessageOnTopicFault("There is no current message on this topic.", fault); } /** * * @param registerPublisherRequest * @return returns org.oasis_open.docs.wsn.br_1.RegisterPublisherResponse * @throws PublisherRegistrationRejectedFault * @throws InvalidTopicExpressionFault * @throws TopicNotSupportedFault * @throws ResourceUnknownFault * @throws PublisherRegistrationFailedFault */ @WebMethod(operationName = "RegisterPublisher") @WebResult(name = "RegisterPublisherResponse", targetNamespace = "http://docs.oasis-open.org/wsn/br-1", partName = "RegisterPublisherResponse") public RegisterPublisherResponse registerPublisher( @WebParam(name = "RegisterPublisher", targetNamespace = "http://docs.oasis-open.org/wsn/br-1", partName = "RegisterPublisherRequest") RegisterPublisher registerPublisherRequest) throws InvalidTopicExpressionFault, PublisherRegistrationFailedFault, PublisherRegistrationRejectedFault, ResourceUnknownFault, TopicNotSupportedFault { LOGGER.finest("RegisterPublisher"); return handleRegisterPublisher(registerPublisherRequest); } public RegisterPublisherResponse handleRegisterPublisher(RegisterPublisher registerPublisherRequest) throws InvalidTopicExpressionFault, PublisherRegistrationFailedFault, PublisherRegistrationRejectedFault, ResourceUnknownFault, TopicNotSupportedFault { AbstractPublisher publisher = null; boolean success = false; try { publisher = createPublisher(idGenerator.generateSanitizedId()); publisher.register(); publisher.create(registerPublisherRequest); RegisterPublisherResponse response = new RegisterPublisherResponse(); response.setPublisherRegistrationReference(publisher.getEpr()); if (publisher.getPublisherReference() != null) { publishers.put(WSNHelper.getInstance() .getWSAAddress(publisher.getPublisherReference()), publisher); } else { nonContactPublishers.add(publisher); } success = true; return response; } catch (EndpointRegistrationException e) { LOGGER.log(Level.WARNING, "Unable to register new endpoint", e); PublisherRegistrationFailedFaultType fault = new PublisherRegistrationFailedFaultType(); throw new PublisherRegistrationFailedFault("Unable to register new endpoint", fault, e); } finally { if (!success && publisher != null) { try { publisher.destroy(); } catch (ResourceNotDestroyedFault e) { LOGGER.log(Level.INFO, "Error destroying publisher", e); } } } } protected abstract AbstractPublisher createPublisher(String name); protected abstract AbstractSubscription createSubscription(String name); @WebResult(name = "GetResourcePropertyResponse", targetNamespace = "http://docs.oasis-open.org/wsrf/rp-2", partName = "GetResourcePropertyResponse") @WebMethod(operationName = "GetResourceProperty") public GetResourcePropertyResponse getResourceProperty( @WebParam(partName = "GetResourcePropertyRequest", name = "GetResourceProperty", targetNamespace = "http://docs.oasis-open.org/wsrf/rp-2") javax.xml.namespace.QName getResourcePropertyRequest ) throws ResourceUnavailableFault, ResourceUnknownFault, InvalidResourcePropertyQNameFault { LOGGER.finest("GetResourceProperty"); return handleGetResourceProperty(getResourcePropertyRequest); } protected GetResourcePropertyResponse handleGetResourceProperty(QName property) throws ResourceUnavailableFault, ResourceUnknownFault, InvalidResourcePropertyQNameFault { InvalidResourcePropertyQNameFaultType fault = new InvalidResourcePropertyQNameFaultType(); throw new InvalidResourcePropertyQNameFault("Invalid resource property QName: " + property, fault); } }