/** * 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.test.rule.callback; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; 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.DestinationNames; import com.liferay.portal.kernel.messaging.Message; import com.liferay.portal.kernel.messaging.MessageBus; import com.liferay.portal.kernel.messaging.MessageBusUtil; import com.liferay.portal.kernel.messaging.SynchronousDestination; import com.liferay.portal.kernel.messaging.proxy.ProxyModeThreadLocal; import com.liferay.portal.kernel.search.SearchEngineHelperUtil; import com.liferay.portal.kernel.test.rule.Sync; import com.liferay.portal.kernel.test.rule.SynchronousDestinationTestRule; import com.liferay.portal.kernel.test.rule.callback.SynchronousDestinationTestCallback.SyncHandler; import com.liferay.portal.kernel.transaction.Propagation; import com.liferay.portal.kernel.transaction.TransactionConfig; import com.liferay.portal.kernel.transaction.TransactionInvokerUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.PropsKeys; import com.liferay.portal.kernel.util.PropsUtil; import com.liferay.registry.Filter; import com.liferay.registry.Registry; import com.liferay.registry.RegistryUtil; import com.liferay.registry.dependency.ServiceDependencyManager; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import org.junit.Test; import org.junit.runner.Description; /** * @author Shuyang Zhou */ public class SynchronousDestinationTestCallback implements TestCallback<SyncHandler, SyncHandler> { public static final SynchronousDestinationTestCallback INSTANCE = new SynchronousDestinationTestCallback(); @Override public void afterClass(Description description, SyncHandler syncHandler) throws Exception { if (syncHandler != null) { syncHandler.restorePreviousSync(); } } @Override public void afterMethod( Description description, SyncHandler syncHandler, Object target) { if (syncHandler != null) { syncHandler.restorePreviousSync(); } } @Override public SyncHandler beforeClass(Description description) throws Throwable { Class<?> testClass = description.getTestClass(); Sync sync = testClass.getAnnotation(Sync.class); if (sync != null) { return _createSyncHandler(sync); } boolean hasSyncedMethod = false; for (Method method : testClass.getMethods()) { if ((method.getAnnotation(Sync.class) != null) && (method.getAnnotation(Test.class) != null)) { hasSyncedMethod = true; break; } } if (!hasSyncedMethod) { throw new AssertionError( testClass.getName() + " uses " + SynchronousDestinationTestRule.class.getName() + " without any usage of " + Sync.class.getName()); } return null; } @Override public SyncHandler beforeMethod(Description description, Object target) { Class<?> testClass = description.getTestClass(); Sync sync = testClass.getAnnotation(Sync.class); if (sync != null) { return null; } sync = description.getAnnotation(Sync.class); if (sync == null) { return null; } return _createSyncHandler(sync); } public static class SyncHandler { public BaseDestination createSynchronousDestination( String destinationName) { SynchronousDestination synchronousDestination = null; if ((_sync != null) && _sync.cleanTransaction()) { synchronousDestination = new CleanTransactionSynchronousDestination(); } else { synchronousDestination = new SynchronousDestination(); } synchronousDestination.setName(destinationName); return synchronousDestination; } public void enableSync() { if (_sync == null) { return; } ServiceDependencyManager serviceDependencyManager = new ServiceDependencyManager(); Filter asyncFilter = _registerDestinationFilter( DestinationNames.ASYNC_SERVICE); Filter backgroundTaskFilter = _registerDestinationFilter( DestinationNames.BACKGROUND_TASK); Filter backgroundTaskStatusFilter = _registerDestinationFilter( DestinationNames.BACKGROUND_TASK_STATUS); Filter mailFilter = _registerDestinationFilter( DestinationNames.MAIL); Filter pdfProcessorFilter = _registerDestinationFilter( DestinationNames.DOCUMENT_LIBRARY_PDF_PROCESSOR); Filter rawMetaDataProcessorFilter = _registerDestinationFilter( DestinationNames.DOCUMENT_LIBRARY_RAW_METADATA_PROCESSOR); Filter subscrpitionSenderFilter = _registerDestinationFilter( DestinationNames.SUBSCRIPTION_SENDER); serviceDependencyManager.registerDependencies( asyncFilter, backgroundTaskFilter, backgroundTaskStatusFilter, mailFilter, pdfProcessorFilter, rawMetaDataProcessorFilter, subscrpitionSenderFilter); boolean schedulerEnabled = GetterUtil.getBoolean( PropsUtil.get(PropsKeys.SCHEDULER_ENABLED)); if (schedulerEnabled) { Filter kaleoGraphWalkerFilter = _registerDestinationFilter( "liferay/kaleo_graph_walker"); serviceDependencyManager.registerDependencies( kaleoGraphWalkerFilter); } serviceDependencyManager.waitForDependencies(); ProxyModeThreadLocal.setForceSync(true); replaceDestination(DestinationNames.ASYNC_SERVICE); replaceDestination(DestinationNames.BACKGROUND_TASK); replaceDestination(DestinationNames.BACKGROUND_TASK_STATUS); replaceDestination(DestinationNames.DOCUMENT_LIBRARY_PDF_PROCESSOR); replaceDestination( DestinationNames.DOCUMENT_LIBRARY_RAW_METADATA_PROCESSOR); replaceDestination( DestinationNames.DOCUMENT_LIBRARY_SYNC_EVENT_PROCESSOR); replaceDestination(DestinationNames.MAIL); replaceDestination(DestinationNames.SCHEDULER_ENGINE); replaceDestination(DestinationNames.SUBSCRIPTION_SENDER); if (schedulerEnabled) { replaceDestination("liferay/kaleo_graph_walker"); } for (String searchEngineId : SearchEngineHelperUtil.getSearchEngineIds()) { replaceDestination( SearchEngineHelperUtil.getSearchReaderDestinationName( searchEngineId)); replaceDestination( SearchEngineHelperUtil.getSearchWriterDestinationName( searchEngineId)); } } public void replaceDestination(String destinationName) { MessageBus messageBus = MessageBusUtil.getMessageBus(); Destination destination = messageBus.getDestination( destinationName); if (destination instanceof BaseAsyncDestination) { _asyncServiceDestinations.add(destination); messageBus.replace( createSynchronousDestination(destinationName), false); } if (destination == null) { _absentDestinationNames.add(destinationName); messageBus.addDestination( createSynchronousDestination(destinationName)); } } public void restorePreviousSync() { if (_sync == null) { return; } ProxyModeThreadLocal.setForceSync(_forceSync); MessageBus messageBus = MessageBusUtil.getMessageBus(); for (Destination destination : _asyncServiceDestinations) { messageBus.replace(destination); } _asyncServiceDestinations.clear(); for (String absentDestinationName : _absentDestinationNames) { messageBus.removeDestination(absentDestinationName); } } public void setForceSync(boolean forceSync) { _forceSync = forceSync; } public void setSync(Sync sync) { _sync = sync; } private Filter _registerDestinationFilter(String destinationName) { Registry registry = RegistryUtil.getRegistry(); return registry.getFilter( "(&(destination.name=" + destinationName + ")(objectClass=" + Destination.class.getName() + "))"); } private final List<String> _absentDestinationNames = new ArrayList<>(); private final List<Destination> _asyncServiceDestinations = new ArrayList<>(); private boolean _forceSync; private Sync _sync; } protected SynchronousDestinationTestCallback() { } private SyncHandler _createSyncHandler(Sync sync) { SyncHandler syncHandler = new SyncHandler(); syncHandler.setForceSync(ProxyModeThreadLocal.isForceSync()); syncHandler.setSync(sync); syncHandler.enableSync(); return syncHandler; } private static final TransactionConfig _transactionConfig; static { TransactionConfig.Builder builder = new TransactionConfig.Builder(); builder.setPropagation(Propagation.NOT_SUPPORTED); builder.setRollbackForClasses( PortalException.class, SystemException.class); _transactionConfig = builder.build(); } private static class CleanTransactionSynchronousDestination extends SynchronousDestination { @Override public void send(final Message message) { try { TransactionInvokerUtil.invoke( _transactionConfig, new Callable<Void>() { @Override public Void call() throws Exception { CleanTransactionSynchronousDestination.super.send( message); return null; } }); } catch (Throwable t) { throw new RuntimeException(t); } } } }