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}