/** * 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.kernel.messaging; import com.liferay.portal.kernel.cluster.ClusterInvokeThreadLocal; import com.liferay.portal.kernel.concurrent.RejectedExecutionHandler; import com.liferay.portal.kernel.concurrent.ThreadPoolExecutor; import com.liferay.portal.kernel.concurrent.ThreadPoolHandlerAdapter; import com.liferay.portal.kernel.executor.PortalExecutorManager; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.User; import com.liferay.portal.kernel.security.auth.CompanyThreadLocal; import com.liferay.portal.kernel.security.auth.PrincipalThreadLocal; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.security.permission.PermissionCheckerFactoryUtil; import com.liferay.portal.kernel.security.permission.PermissionThreadLocal; import com.liferay.portal.kernel.service.UserLocalServiceUtil; import com.liferay.portal.kernel.util.GroupThreadLocal; import com.liferay.portal.kernel.util.LocaleThreadLocal; import com.liferay.portal.kernel.util.NamedThreadFactory; import com.liferay.portal.kernel.util.PortalClassLoaderUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.registry.Registry; import com.liferay.registry.RegistryUtil; import com.liferay.registry.ServiceReference; import com.liferay.registry.ServiceTracker; import com.liferay.registry.ServiceTrackerCustomizer; import java.util.Locale; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @author Michael C. Han * @author Shuyang Zhou */ public abstract class BaseAsyncDestination extends BaseDestination { @Override public void afterPropertiesSet() { super.afterPropertiesSet(); Registry registry = RegistryUtil.getRegistry(); serviceTracker = registry.trackServices( PortalExecutorManager.class, new PortalExecutorManagerServiceTrackerCustomizer()); serviceTracker.open(); } @Override public void close(boolean force) { if ((_threadPoolExecutor == null) || _threadPoolExecutor.isShutdown()) { return; } if (force) { _threadPoolExecutor.shutdownNow(); } else { _threadPoolExecutor.shutdown(); } } @Override public void destroy() { super.destroy(); serviceTracker.close(); } @Override public DestinationStatistics getDestinationStatistics() { DestinationStatistics destinationStatistics = new DestinationStatistics(); destinationStatistics.setActiveThreadCount( _threadPoolExecutor.getActiveCount()); destinationStatistics.setCurrentThreadCount( _threadPoolExecutor.getPoolSize()); destinationStatistics.setLargestThreadCount( _threadPoolExecutor.getLargestPoolSize()); destinationStatistics.setMaxThreadPoolSize( _threadPoolExecutor.getMaxPoolSize()); destinationStatistics.setMinThreadPoolSize( _threadPoolExecutor.getCorePoolSize()); destinationStatistics.setPendingMessageCount( _threadPoolExecutor.getPendingTaskCount()); destinationStatistics.setSentMessageCount( _threadPoolExecutor.getCompletedTaskCount()); return destinationStatistics; } public int getMaximumQueueSize() { return _maximumQueueSize; } public int getWorkersCoreSize() { return _workersCoreSize; } public int getWorkersMaxSize() { return _workersMaxSize; } @Override public void open() { if ((_threadPoolExecutor != null) && !_threadPoolExecutor.isShutdown()) { return; } ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader(); if (_rejectedExecutionHandler == null) { _rejectedExecutionHandler = createRejectionExecutionHandler(); } ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( _workersCoreSize, _workersMaxSize, 60L, TimeUnit.SECONDS, false, _maximumQueueSize, _rejectedExecutionHandler, new NamedThreadFactory( getName(), Thread.NORM_PRIORITY, classLoader), new ThreadPoolHandlerAdapter()); ThreadPoolExecutor oldThreadPoolExecutor = portalExecutorManager.registerPortalExecutor( getName(), threadPoolExecutor); if (oldThreadPoolExecutor != null) { if (_log.isWarnEnabled()) { _log.warn( "Abort creating a new thread pool for destination " + getName() + " and reuse previous one"); } threadPoolExecutor.shutdownNow(); threadPoolExecutor = oldThreadPoolExecutor; } _threadPoolExecutor = threadPoolExecutor; } @Override public void send(Message message) { if (messageListeners.isEmpty()) { if (_log.isDebugEnabled()) { _log.debug("No message listeners for destination " + getName()); } return; } ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor(); if (threadPoolExecutor.isShutdown()) { throw new IllegalStateException( "Destination " + getName() + " is shutdown and cannot " + "receive more messages"); } populateMessageFromThreadLocals(message); if (_log.isDebugEnabled()) { _log.debug( "Sending message " + message + " from destination " + getName() + " to message listeners " + messageListeners); } dispatch(messageListeners, message); } public void setMaximumQueueSize(int maximumQueueSize) { _maximumQueueSize = maximumQueueSize; } public void setRejectedExecutionHandler( RejectedExecutionHandler rejectedExecutionHandler) { _rejectedExecutionHandler = rejectedExecutionHandler; } public void setWorkersCoreSize(int workersCoreSize) { _workersCoreSize = workersCoreSize; if (_threadPoolExecutor != null) { _threadPoolExecutor.adjustPoolSize( workersCoreSize, _workersMaxSize); } } public void setWorkersMaxSize(int workersMaxSize) { _workersMaxSize = workersMaxSize; if (_threadPoolExecutor != null) { _threadPoolExecutor.adjustPoolSize( _workersCoreSize, workersMaxSize); } } protected RejectedExecutionHandler createRejectionExecutionHandler() { return new RejectedExecutionHandler() { @Override public void rejectedExecution( Runnable runnable, ThreadPoolExecutor threadPoolExecutor) { if (!_log.isWarnEnabled()) { return; } MessageRunnable messageRunnable = (MessageRunnable)runnable; _log.warn( "Discarding message " + messageRunnable.getMessage() + " because it exceeds the maximum queue size of " + _maximumQueueSize); } }; } protected abstract void dispatch( Set<MessageListener> messageListeners, Message message); protected ThreadPoolExecutor getThreadPoolExecutor() { return _threadPoolExecutor; } protected void populateMessageFromThreadLocals(Message message) { if (!message.contains("companyId")) { message.put("companyId", CompanyThreadLocal.getCompanyId()); } if (!ClusterInvokeThreadLocal.isEnabled()) { message.put("clusterInvoke", Boolean.FALSE); } if (!message.contains("defaultLocale")) { message.put("defaultLocale", LocaleThreadLocal.getDefaultLocale()); } if (!message.contains("groupId")) { message.put("groupId", GroupThreadLocal.getGroupId()); } if (!message.contains("permissionChecker")) { message.put( "permissionChecker", PermissionThreadLocal.getPermissionChecker()); } if (!message.contains("principalName")) { message.put("principalName", PrincipalThreadLocal.getName()); } if (!message.contains("principalPassword")) { message.put( "principalPassword", PrincipalThreadLocal.getPassword()); } if (!message.contains("siteDefaultLocale")) { message.put( "siteDefaultLocale", LocaleThreadLocal.getSiteDefaultLocale()); } if (!message.contains("themeDisplayLocale")) { message.put( "themeDisplayLocale", LocaleThreadLocal.getThemeDisplayLocale()); } } protected void populateThreadLocalsFromMessage(Message message) { long companyId = message.getLong("companyId"); if (companyId > 0) { CompanyThreadLocal.setCompanyId(companyId); } Boolean clusterInvoke = (Boolean)message.get("clusterInvoke"); if (clusterInvoke != null) { ClusterInvokeThreadLocal.setEnabled(clusterInvoke); } Locale defaultLocale = (Locale)message.get("defaultLocale"); if (defaultLocale != null) { LocaleThreadLocal.setDefaultLocale(defaultLocale); } long groupId = message.getLong("groupId"); if (groupId > 0) { GroupThreadLocal.setGroupId(groupId); } PermissionChecker permissionChecker = (PermissionChecker)message.get( "permissionChecker"); String principalName = message.getString("principalName"); if (Validator.isNotNull(principalName)) { PrincipalThreadLocal.setName(principalName); } if ((permissionChecker == null) && Validator.isNotNull(principalName)) { try { User user = UserLocalServiceUtil.fetchUser( PrincipalThreadLocal.getUserId()); permissionChecker = PermissionCheckerFactoryUtil.create(user); } catch (Exception e) { throw new RuntimeException(e); } } if (permissionChecker != null) { PermissionThreadLocal.setPermissionChecker(permissionChecker); } String principalPassword = message.getString("principalPassword"); if (Validator.isNotNull(principalPassword)) { PrincipalThreadLocal.setPassword(principalPassword); } Locale siteDefaultLocale = (Locale)message.get("siteDefaultLocale"); if (siteDefaultLocale != null) { LocaleThreadLocal.setSiteDefaultLocale(siteDefaultLocale); } Locale themeDisplayLocale = (Locale)message.get("themeDisplayLocale"); if (themeDisplayLocale != null) { LocaleThreadLocal.setThemeDisplayLocale(themeDisplayLocale); } } protected volatile PortalExecutorManager portalExecutorManager; protected ServiceTracker<PortalExecutorManager, PortalExecutorManager> serviceTracker; private static final int _WORKERS_CORE_SIZE = 2; private static final int _WORKERS_MAX_SIZE = 5; private static final Log _log = LogFactoryUtil.getLog( BaseAsyncDestination.class); private int _maximumQueueSize = Integer.MAX_VALUE; private RejectedExecutionHandler _rejectedExecutionHandler; private ThreadPoolExecutor _threadPoolExecutor; private int _workersCoreSize = _WORKERS_CORE_SIZE; private int _workersMaxSize = _WORKERS_MAX_SIZE; private class PortalExecutorManagerServiceTrackerCustomizer implements ServiceTrackerCustomizer <PortalExecutorManager, PortalExecutorManager> { @Override public PortalExecutorManager addingService( ServiceReference<PortalExecutorManager> serviceReference) { Registry registry = RegistryUtil.getRegistry(); portalExecutorManager = registry.getService(serviceReference); open(); return portalExecutorManager; } @Override public void modifiedService( ServiceReference<PortalExecutorManager> serviceReference, PortalExecutorManager portalExecutorManager) { } @Override public void removedService( ServiceReference<PortalExecutorManager> serviceReference, PortalExecutorManager portalExecutorManager) { portalExecutorManager = null; } } }