All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cds.feature.messaging.em.service.EnterpriseMessagingService Maven / Gradle / Ivy

The newest version!
/**************************************************************************
 * (C) 2019-2024 SAP SE or an SAP affiliate company. All rights reserved. *
 **************************************************************************/
package com.sap.cds.feature.messaging.em.service;

import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.regex.Pattern;

import org.apache.qpid.jms.message.JmsBytesMessage;
import org.apache.qpid.jms.message.JmsTextMessage;
import org.apache.qpid.jms.provider.amqp.message.AmqpJmsBytesMessageFacade;
import org.apache.qpid.jms.provider.amqp.message.AmqpJmsTextMessageFacade;

import com.sap.cds.feature.messaging.em.client.EnterpriseMessagingManagementClient;
import com.sap.cds.feature.messaging.em.jms.EnterpriseMessagingConnectionProvider;
import com.sap.cds.services.environment.CdsProperties.Messaging.MessagingServiceConfig;
import com.sap.cds.services.messaging.TopicMessageEventContext;
import com.sap.cds.services.messaging.jms.BrokerConnection;
import com.sap.cds.services.messaging.service.AbstractMessagingService;
import com.sap.cds.services.messaging.service.MessageQueue;
import com.sap.cds.services.messaging.service.MessageTopic;
import com.sap.cds.services.messaging.service.MessagingBrokerQueueListener;
import com.sap.cds.services.runtime.CdsRuntime;
import com.sap.cds.services.utils.StringUtils;
import com.sap.cloud.environment.servicebinding.api.ServiceBinding;

import jakarta.jms.JMSException;
import jakarta.jms.Message;

/**
 * Implementation of the enterprise messaging connector.
 */
public class EnterpriseMessagingService extends AbstractMessagingService {

	private static final Pattern NAMESPACE_WILDCARD_PATTERN = Pattern.compile("([^/]*/[^/]*/[^/]*/)");
	private static final String NAMESPACE_WILDCARD = "+/+/+/";
	private static final String NAMESPACE_PLACEHOLDER = "$namespace";

	protected final EnterpriseMessagingManagementClient managementClient;
	private final EnterpriseMessagingConnectionProvider connectionProvider;
	private final String queueNamespace;
	private volatile BrokerConnection connection;

	public EnterpriseMessagingService(MessagingServiceConfig serviceConfig, ServiceBinding binding, EnterpriseMessagingConnectionProvider connectionProvider, CdsRuntime runtime) {
		super(resolveConfig(serviceConfig, getNamespaceFromBinding(binding)), runtime);
		this.connectionProvider = connectionProvider; // might be null, e.g. mt scenario
		this.managementClient = new EnterpriseMessagingManagementClient(binding);
		this.queueNamespace = normalizeNamespace(getNamespaceFromBinding(binding));
	}

	private static String getNamespaceFromBinding(ServiceBinding binding) {
		String namespace = (String) binding.getCredentials().get("namespace");
		if (namespace != null) {
			namespace = namespace.trim();
			if (namespace.isEmpty()) {
				namespace = null;
			}
		}
		return namespace;
	}

	private static MessagingServiceConfig resolveConfig(MessagingServiceConfig serviceConfig, String namespace) {
		if("cloudevents".equals(serviceConfig.getFormat())) {
			if(serviceConfig.getSubscribePrefix() == null) {
				serviceConfig.setSubscribePrefix(NAMESPACE_WILDCARD + "ce/");
			}
			if(serviceConfig.getPublishPrefix() == null) {
				serviceConfig.setPublishPrefix(NAMESPACE_PLACEHOLDER + "/ce/");
			}
		}

		String normalizedNamespace = normalizeNamespace(namespace);
		if(serviceConfig.getSubscribePrefix() != null) {
			serviceConfig.setSubscribePrefix(serviceConfig.getSubscribePrefix().replace(NAMESPACE_PLACEHOLDER, normalizedNamespace));
		}
		if(serviceConfig.getPublishPrefix() != null) {
			serviceConfig.setPublishPrefix(serviceConfig.getPublishPrefix().replace(NAMESPACE_PLACEHOLDER, normalizedNamespace));
		}

		serviceConfig.getQueue().getConfig().entrySet().forEach(e -> {
			if (e.getValue() instanceof String value && value.indexOf(NAMESPACE_PLACEHOLDER) != -1) {
				e.setValue(value.replace(NAMESPACE_PLACEHOLDER, normalizedNamespace));
			}
		});

		return serviceConfig;
	}

	private static String normalizeNamespace(String namespace) {
		return namespace == null ? "" : StringUtils.trim(namespace.trim(), '/');
	}

	@Override
	public void init() {
		// create the broker connection asynchronously with topic subscriptions
		connectionProvider.asyncConnectionInitialization(serviceConfig, connection -> {
			this.connection = connection;
			super.init();
		});
	}

	@Override
	public void stop() {
		if (connection != null) {
			try {
				connection.close();
			} catch (JMSException e) {
				// ignored
			}
		}
	}

	@Override
	protected void removeQueue(String name) throws IOException {
		managementClient.removeQueue(name);
	}

	@Override
	protected void createQueue(String name, Map properties) throws IOException {
		if (properties.containsKey("deadMsgQueue")) {
			String dmQueue = (String) properties.get("deadMsgQueue");
			if (managementClient.getQueue(dmQueue) == null) {
				managementClient.createQueue(dmQueue, Collections.emptyMap());
			}
		}
		managementClient.createQueue(name, properties);
	}

	@Override
	protected void createQueueSubscription(String queue, String topic) throws IOException {
		managementClient.createQueueSubscription(queue, topic);
	}

	@Override
	protected void registerQueueListener(String queue, MessagingBrokerQueueListener listener) throws IOException {
		connection.registerQueueListener("queue:" + queue, listener, m -> getMessageTopic(m));
	}

	@Override
	protected void emitTopicMessage(String topic, TopicMessageEventContext messageEventContext) {
		connection.emitTopicMessage("topic:" + topic, messageEventContext);
	}

	@Override
	protected String toFullyQualifiedQueueName(MessageQueue queue) {
		return queue.isFullyQualified() ? queue.getName().replace(NAMESPACE_PLACEHOLDER, queueNamespace) : queueNamespace + '/' + queue.getName();
	}

	@Override
	protected String toFullyQualifiedTopicName(String event, boolean inbound) {
		return super.toFullyQualifiedTopicName(isCloudEventsFormat() ? event.replace('.', '/') : event, inbound);
	}

	private String getMessageTopic(Message message) {
		if (message instanceof JmsTextMessage textMessage) {
			if (textMessage.getFacade() instanceof AmqpJmsTextMessageFacade textMessageFacade) {
				return textMessageFacade.getType();
			}
		} else if (message instanceof JmsBytesMessage bytesMessage
				&& bytesMessage.getFacade() instanceof AmqpJmsBytesMessageFacade bytesMessageFacade) {
			return bytesMessageFacade.getType();
		}
		return null;
	}

	@Override
	protected BiPredicate getTopicMatcher() {
		return (internalTopic, brokerTopic) -> {
			if (super.getTopicMatcher().test(internalTopic, brokerTopic)) {
				return true;
			}

			if (internalTopic.getBrokerName().startsWith(NAMESPACE_WILDCARD)) {
				String brokerTopicTail = NAMESPACE_WILDCARD_PATTERN.matcher(brokerTopic).replaceFirst("");
				String internalTopicTail = internalTopic.getBrokerName().substring(NAMESPACE_WILDCARD.length());
				if (brokerTopicTail.equals(internalTopicTail)) {
					return true;
				}
			}

			return false;
		};
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy