001package ca.uhn.fhir.jpa.subscription.match.deliver.message;
002
003/*-
004 * #%L
005 * HAPI FHIR Subscription Server
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.interceptor.api.HookParams;
025import ca.uhn.fhir.interceptor.api.Pointcut;
026import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
027import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
028import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer;
029import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber;
030import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
031import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
032import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
033import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
034import ca.uhn.fhir.rest.api.EncodingEnum;
035import org.hl7.fhir.instance.model.api.IBaseResource;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038import org.springframework.beans.factory.annotation.Autowired;
039import org.springframework.context.annotation.Scope;
040import org.springframework.messaging.MessagingException;
041
042import java.net.URI;
043import java.net.URISyntaxException;
044
045@Scope("prototype")
046public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDeliverySubscriber {
047        private static Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringMessageSubscriber.class);
048
049        @Autowired
050        private IChannelFactory myChannelFactory;
051
052        /**
053         * Constructor
054         */
055        public SubscriptionDeliveringMessageSubscriber() {
056                super();
057        }
058
059        protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, IChannelProducer theChannelProducer) {
060                IBaseResource payloadResource = theMsg.getPayload(myFhirContext);
061
062                // Regardless of whether we have a payload, the message should be sent.
063                doDelivery(theMsg, theSubscription, theChannelProducer, payloadResource);
064        }
065
066        protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, IChannelProducer theChannelProducer, IBaseResource thePayloadResource) {
067                ResourceModifiedMessage payload = new ResourceModifiedMessage(myFhirContext, thePayloadResource, theMsg.getOperationType());
068                payload.setMessageKey(theMsg.getMessageKeyOrNull());
069                payload.setTransactionId(theMsg.getTransactionId());
070                ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(payload);
071                theChannelProducer.send(message);
072                ourLog.debug("Delivering {} message payload {} for {}", theMsg.getOperationType(), theMsg.getPayloadId(), theSubscription.getIdElement(myFhirContext).toUnqualifiedVersionless().getValue());
073        }
074
075        @Override
076        public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException, URISyntaxException {
077                CanonicalSubscription subscription = theMessage.getSubscription();
078
079                // Interceptor call: SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY
080                HookParams params = new HookParams()
081                        .add(CanonicalSubscription.class, subscription)
082                        .add(ResourceDeliveryMessage.class, theMessage);
083                if (!getInterceptorBroadcaster().callHooks(Pointcut.SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY, params)) {
084                        return;
085                }
086                // Grab the endpoint from the subscription
087                String endpointUrl = subscription.getEndpointUrl();
088
089                String queueName = extractQueueNameFromEndpoint(endpointUrl);
090
091                ChannelProducerSettings channelSettings = new ChannelProducerSettings();
092                channelSettings.setQualifyChannelName(false);
093
094                IChannelProducer channelProducer = myChannelFactory.getOrCreateProducer(queueName, ResourceModifiedJsonMessage.class, channelSettings);
095
096                // Grab the payload type (encoding mimetype) from the subscription
097                String payloadString = subscription.getPayloadString();
098                EncodingEnum payloadType = null;
099                if (payloadString != null) {
100                        payloadType = EncodingEnum.forContentType(payloadString);
101                }
102
103                if (payloadType != EncodingEnum.JSON) {
104                        throw new UnsupportedOperationException(Msg.code(4) + "Only JSON payload type is currently supported for Message Subscriptions");
105                }
106
107                deliverPayload(theMessage, subscription, channelProducer);
108
109                // Interceptor call: SUBSCRIPTION_AFTER_MESSAGE_DELIVERY
110                params = new HookParams()
111                        .add(CanonicalSubscription.class, subscription)
112                        .add(ResourceDeliveryMessage.class, theMessage);
113                if (!getInterceptorBroadcaster().callHooks(Pointcut.SUBSCRIPTION_AFTER_MESSAGE_DELIVERY, params)) {
114                        //noinspection UnnecessaryReturnStatement
115                        return;
116                }
117        }
118
119        private String extractQueueNameFromEndpoint(String theEndpointUrl) throws URISyntaxException {
120                URI uri = new URI(theEndpointUrl);
121                return uri.getSchemeSpecificPart();
122        }
123}