/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.messaging.internal; import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; import com.liferay.portal.kernel.concurrent.ConcurrentHashSet; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.messaging.BaseAsyncDestination; import com.liferay.portal.kernel.messaging.BaseDestination; import com.liferay.portal.kernel.messaging.Destination; import com.liferay.portal.kernel.messaging.DestinationEventListener; import com.liferay.portal.kernel.messaging.Message; import com.liferay.portal.kernel.messaging.MessageBus; import com.liferay.portal.kernel.messaging.MessageBusEventListener; import com.liferay.portal.kernel.messaging.MessageListener; import com.liferay.portal.kernel.nio.intraband.messaging.IntrabandBridgeDestination; import com.liferay.portal.kernel.resiliency.spi.SPIUtil; import com.liferay.portal.kernel.util.ListUtil; import com.liferay.portal.kernel.util.MapUtil; import com.liferay.portal.messaging.configuration.DestinationWorkerConfiguration; import java.util.ArrayList; import java.util.Collection; import java.util.Dictionary; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.osgi.framework.Constants; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedServiceFactory; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.component.annotations.ReferencePolicyOption; /** * @author Michael C. Han */ @Component( immediate = true, property = Constants.SERVICE_PID + "=com.liferay.portal.messaging.configuration.DestinationWorkerConfiguration", service = {ManagedServiceFactory.class, MessageBus.class} ) public class DefaultMessageBus implements ManagedServiceFactory, MessageBus { @Override public synchronized void addDestination(Destination destination) { doAddDestination(destination); } @Override public boolean addMessageBusEventListener( MessageBusEventListener messageBusEventListener) { return _messageBusEventListeners.add(messageBusEventListener); } @Override public void deleted(String factoryPid) { String destinationName = _factoryPidsToDestinationName.remove( factoryPid); _destinationWorkerConfigurations.remove(destinationName); } @Override public Destination getDestination(String destinationName) { return _destinations.get(destinationName); } @Override public int getDestinationCount() { return _destinations.size(); } @Override public Collection<String> getDestinationNames() { return _destinations.keySet(); } @Override public Collection<Destination> getDestinations() { return _destinations.values(); } @Override public String getName() { return "Default Message Bus"; } @Override public boolean hasDestination(String destinationName) { return _destinations.containsKey(destinationName); } @Override public boolean hasMessageListener(String destinationName) { Destination destination = _destinations.get(destinationName); if ((destination != null) && destination.isRegistered()) { return true; } else { return false; } } @Override public synchronized boolean registerMessageListener( String destinationName, MessageListener messageListener) { Destination destination = _destinations.get(destinationName); if (destination != null) { return destination.register(messageListener); } List<MessageListener> queuedMessageListeners = _queuedMessageListeners.get(destinationName); if (queuedMessageListeners == null) { queuedMessageListeners = new ArrayList<>(); _queuedMessageListeners.put( destinationName, queuedMessageListeners); } queuedMessageListeners.add(messageListener); if (_log.isWarnEnabled()) { _log.warn( "Queuing message listener until destination " + destinationName + " is added"); } return false; } @Override public Destination removeDestination(String destinationName) { return removeDestination(destinationName, true); } @Override public synchronized Destination removeDestination( String destinationName, boolean closeOnRemove) { Destination destination = _destinations.remove(destinationName); if (destination == null) { return null; } if (closeOnRemove) { destination.close(true); } destination.removeDestinationEventListeners(); destination.unregisterMessageListeners(); for (MessageBusEventListener messageBusEventListener : _messageBusEventListeners) { messageBusEventListener.destinationRemoved(destination); } return destination; } @Override public boolean removeMessageBusEventListener( MessageBusEventListener messageBusEventListener) { return _messageBusEventListeners.remove(messageBusEventListener); } @Override public void replace(Destination destination) { replace(destination, true); } @Override public synchronized void replace( Destination destination, boolean closeOnRemove) { Destination oldDestination = _destinations.get(destination.getName()); oldDestination.copyDestinationEventListeners(destination); oldDestination.copyMessageListeners(destination); removeDestination(oldDestination.getName(), closeOnRemove); doAddDestination(destination); destination.open(); } @Override public void sendMessage(String destinationName, Message message) { Destination destination = _destinations.get(destinationName); if (destination == null) { if (_log.isWarnEnabled()) { _log.warn( "Destination " + destinationName + " is not configured"); } return; } message.setDestinationName(destinationName); destination.send(message); } @Override public void shutdown() { shutdown(false); } @Override public synchronized void shutdown(boolean force) { for (Destination destination : _destinations.values()) { destination.close(force); } } @Override public synchronized boolean unregisterMessageListener( String destinationName, MessageListener messageListener) { Destination destination = _destinations.get(destinationName); if (destination != null) { return destination.unregister(messageListener); } List<MessageListener> queuedMessageListeners = _queuedMessageListeners.get(destinationName); if (ListUtil.isEmpty(queuedMessageListeners)) { return false; } return queuedMessageListeners.remove(messageListener); } @Override public void updated(String factoryPid, Dictionary<String, ?> dictionary) throws ConfigurationException { DestinationWorkerConfiguration destinationWorkerConfiguration = ConfigurableUtil.createConfigurable( DestinationWorkerConfiguration.class, dictionary); _factoryPidsToDestinationName.put( factoryPid, destinationWorkerConfiguration.destinationName()); _destinationWorkerConfigurations.put( destinationWorkerConfiguration.destinationName(), destinationWorkerConfiguration); Destination destination = _destinations.get( destinationWorkerConfiguration.destinationName()); updateDestination(destination, destinationWorkerConfiguration); } @Deactivate protected void deactivate() { shutdown(true); for (Destination destination : _destinations.values()) { destination.destroy(); } _messageBusEventListeners.clear(); _destinations.clear(); } protected void doAddDestination(Destination destination) { Class<?> clazz = destination.getClass(); if (SPIUtil.isSPI() && !clazz.equals(IntrabandBridgeDestination.class)) { destination = new IntrabandBridgeDestination(destination); } _destinations.put(destination.getName(), destination); for (MessageBusEventListener messageBusEventListener : _messageBusEventListeners) { messageBusEventListener.destinationAdded(destination); } List<MessageListener> messageListeners = _queuedMessageListeners.remove( destination.getName()); if (ListUtil.isEmpty(messageListeners)) { return; } if (_log.isDebugEnabled()) { _log.debug( "Registering " + messageListeners.size() + " queued message listeners for destination " + destination.getName()); } for (MessageListener messageListener : messageListeners) { destination.register(messageListener); } } @Reference( cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, target = "(destination.name=*)", unbind = "unregisterDestination" ) protected synchronized void registerDestination( Destination destination, Map<String, Object> properties) { String destinationName = MapUtil.getString( properties, "destination.name"); if (BaseDestination.class.isInstance(destination)) { BaseDestination baseDestination = (BaseDestination)destination; baseDestination.setName(destinationName); baseDestination.afterPropertiesSet(); } if (_destinations.containsKey(destination.getName())) { replace(destination); } else { doAddDestination(destination); } DestinationWorkerConfiguration destinationWorkerConfiguration = _destinationWorkerConfigurations.get(destinationName); updateDestination(destination, destinationWorkerConfiguration); } @Reference( cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, target = "(destination.name=*)", unbind = "unregisterDestinationEventListener" ) protected synchronized void registerDestinationEventListener( DestinationEventListener destinationEventListener, Map<String, Object> properties) { String destinationName = MapUtil.getString( properties, "destination.name"); Destination destination = _destinations.get(destinationName); if (destination == null) { if (_log.isInfoEnabled()) { _log.info( "Unable to unregister destination event listener for " + destinationName); } return; } destination.addDestinationEventListener(destinationEventListener); } @Reference( cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, unbind = "unregisterMessageBusEventListener" ) protected void registerMessageBusEventListener( MessageBusEventListener messageBusEventListener) { addMessageBusEventListener(messageBusEventListener); } @Reference( cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, target = "(destination.name=*)", unbind = "unregisterMessageListener" ) protected synchronized void registerMessageListener( MessageListener messageListener, Map<String, Object> properties) { Thread currentThread = Thread.currentThread(); ClassLoader contextClassLoader = currentThread.getContextClassLoader(); try { ClassLoader operatingClassLoader = (ClassLoader)properties.get( "message.listener.operating.class.loader"); if (operatingClassLoader != null) { currentThread.setContextClassLoader(operatingClassLoader); } String destinationName = MapUtil.getString( properties, "destination.name"); registerMessageListener(destinationName, messageListener); } finally { currentThread.setContextClassLoader(contextClassLoader); } } protected synchronized void unregisterDestination( Destination destination, Map<String, Object> properties) { removeDestination(destination.getName()); destination.destroy(); } protected synchronized void unregisterDestinationEventListener( DestinationEventListener destinationEventListener, Map<String, Object> properties) { String destinationName = MapUtil.getString( properties, "destination.name"); Destination destination = _destinations.get(destinationName); if (destination == null) { if (_log.isInfoEnabled()) { _log.info( "Unable to unregister destination event listener for " + destinationName); } return; } destination.removeDestinationEventListener(destinationEventListener); } protected void unregisterMessageBusEventListener( MessageBusEventListener messageBusEventListener) { removeMessageBusEventListener(messageBusEventListener); } protected synchronized void unregisterMessageListener( MessageListener messageListener, Map<String, Object> properties) { String destinationName = MapUtil.getString( properties, "destination.name"); unregisterMessageListener(destinationName, messageListener); } protected void updateDestination( Destination destination, DestinationWorkerConfiguration destinationWorkerConfiguration) { if ((destination == null) || (destinationWorkerConfiguration == null)) { return; } if (destination instanceof BaseAsyncDestination) { BaseAsyncDestination baseAsyncDestination = (BaseAsyncDestination)destination; baseAsyncDestination.setMaximumQueueSize( destinationWorkerConfiguration.maxQueueSize()); baseAsyncDestination.setWorkersCoreSize( destinationWorkerConfiguration.workerCoreSize()); baseAsyncDestination.setWorkersMaxSize( destinationWorkerConfiguration.workerMaxSize()); } } private static final Log _log = LogFactoryUtil.getLog( DefaultMessageBus.class); private final Map<String, Destination> _destinations = new HashMap<>(); private final Map<String, DestinationWorkerConfiguration> _destinationWorkerConfigurations = new ConcurrentHashMap<>(); private final Map<String, String> _factoryPidsToDestinationName = new ConcurrentHashMap<>(); private final Set<MessageBusEventListener> _messageBusEventListeners = new ConcurrentHashSet<>(); private final Map<String, List<MessageListener>> _queuedMessageListeners = new HashMap<>(); }