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

io.mats3.spring.test.TestSpringMatsFactoryProvider Maven / Gradle / Ivy

Go to download

Mats^3 Spring-specific testing tools, for quickly firing up a test-harness using either JUnit or Jupiter (JUnit 5).

The newest version!
package io.mats3.spring.test;

import java.sql.Connection;
import java.util.concurrent.atomic.AtomicInteger;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.transaction.PlatformTransactionManager;

import io.mats3.MatsFactory;
import io.mats3.MatsFactory.FactoryConfig;
import io.mats3.MatsFactory.MatsFactoryWrapper;
import io.mats3.impl.jms.JmsMatsFactory;
import io.mats3.impl.jms.JmsMatsJmsSessionHandler;
import io.mats3.impl.jms.JmsMatsJmsSessionHandler_Pooling;
import io.mats3.impl.jms.JmsMatsTransactionManager;
import io.mats3.impl.jms.JmsMatsTransactionManager_Jms;
import io.mats3.serial.MatsSerializer;
import io.mats3.spring.jms.factories.SpringJmsMatsFactoryWrapper;
import io.mats3.spring.jms.tx.JmsMatsTransactionManager_JmsAndSpringManagedSqlTx;
import io.mats3.test.MatsTestFactory;
import io.mats3.test.broker.MatsTestBroker;

/**
 * A testing-oriented {@link MatsFactory}-provider which utilizes the {@link MatsTestBroker} for the produced
 * MatsFactory to connect to - this is for the scenarios where you do NOT have your test load the entire
 * application's Spring configuration, but instead "piece together" the relevant Spring
 * {@literal @Components} containing test-relevant Mats endpoints and other beans from your application
 * along with test-specific mocked-out endpoints: You will then probably not have the MatsFactory present in the Spring
 * context. You can then use this class to get a "quick and dirty" MatsFactory (with an in-vm MQ Broker backing it) on
 * which your application's endpoints, and mocked-out endpoints in the test, can run.
 * 

* If your requirements aren't all that exotic, you may get this indirectly invoked by using the * {@literal @}{@link MatsTestContext} annotation directly on the Test-class. If your requirements are slightly more * involved, check out the {@link MatsTestInfrastructureConfiguration} and {@link MatsTestInfrastructureDbConfiguration} * Spring Configuration classes (loaded in a test with {@literal @}{@link ContextConfiguration}), which employs the * methods in this class. * * @see MatsTestContext * @see MatsTestInfrastructureConfiguration * @see MatsTestInfrastructureDbConfiguration * @author Endre Stølsvik 2019-06-17 21:26 - http://stolsvik.com/, [email protected] */ public class TestSpringMatsFactoryProvider { private static final Logger log = LoggerFactory.getLogger(TestSpringMatsFactoryProvider.class); private static final String LOG_PREFIX = "#SPRINGJMATS(test)# "; private static final AtomicInteger _sequence = new AtomicInteger(0); /** * If you need a {@link MatsFactory} employing Spring's DataSourceTransactionManager (which you probably do in a * Spring environment utilizing SQL), this is your factory method. *

* Usage: In the test, make a @Bean-annotated method which returns the result of this method - or employ the * convenience {@link MatsTestInfrastructureDbConfiguration}. * * @param concurrency * The concurrency of the created {@link MatsFactory}, set with * {@link FactoryConfig#setConcurrency(int)}. * @param sqlDataSource * the SQL DataSource which to stash into a Spring {@link DataSourceTransactionManager}, and from which * SQL {@link Connection}s are fetched from, using {@link DataSource#getConnection()}. It is assumed that * if username and password is needed, you have configured that on the DataSource. * @param matsSerializer * the MatsSerializer to use. If you employ the standard, use MatsSerializerJson.create(). * * @return the produced {@link MatsFactory} */ public static MatsFactory createSpringDataSourceTxTestMatsFactory(int concurrency, DataSource sqlDataSource, MatsSerializer matsSerializer) { // JMS + Spring's DataSourceTransactionManager-based MatsTransactionManager JmsMatsTransactionManager springSqlTxMgr = JmsMatsTransactionManager_JmsAndSpringManagedSqlTx.create( sqlDataSource); return getMatsFactoryStopLocalVmBrokerWrapper(concurrency, matsSerializer, springSqlTxMgr); } /** * Convenience variant of {@link #createSpringDataSourceTxTestMatsFactory(int, DataSource, MatsSerializer)} where * concurrency is 2, which should be adequate for most testing - unless you explicitly want to test concurrency. * * @param sqlDataSource * the SQL DataSource which to stash into a Spring {@link DataSourceTransactionManager}, and from which * SQL {@link Connection}s are fetched from, using {@link DataSource#getConnection()}. It is assumed that * if username and password is needed, you have configured that on the DataSource. * @param matsSerializer * the MatsSerializer to use. If you employ the standard, use MatsSerializerJson.create(). * * @return the produced {@link MatsFactory} */ public static MatsFactory createSpringDataSourceTxTestMatsFactory(DataSource sqlDataSource, MatsSerializer matsSerializer) { return createSpringDataSourceTxTestMatsFactory(MatsTestFactory.TEST_CONCURRENCY, sqlDataSource, matsSerializer); } /** * If you need a {@link MatsFactory}, but you need to make your own {@link PlatformTransactionManager}, this is your * factory method - please note that the contained {@link DataSource} should have been wrapped using the static * method {@link JmsMatsTransactionManager_JmsAndSpringManagedSqlTx#wrapLazyConnectionDatasource(DataSource)}. *

* Usage: In the test, make a @Bean-annotated method which returns the result of this method - or employ the * convenience {@link MatsTestInfrastructureDbConfiguration}. * * @param concurrency * The concurrency of the created {@link MatsFactory}, set with * {@link FactoryConfig#setConcurrency(int)}. * @param platformTransactionManager * the {@link PlatformTransactionManager} that the SpringJmsMats transaction manager should employ. From * this, the {@link DataSource} is gotten using refelction to find a method 'getDataSource()'. please * note that the contained {@link DataSource} should have been wrapped using the static * method * {@link JmsMatsTransactionManager_JmsAndSpringManagedSqlTx#wrapLazyConnectionDatasource(DataSource)}. * It is assumed that if username and password is needed, you have configured that on the DataSource. * @param matsSerializer * the MatsSerializer to use. If you employ the standard, use MatsSerializerJson.create(). * * @return the produced {@link MatsFactory} */ public static MatsFactory createSpringDataSourceTxTestMatsFactory(int concurrency, PlatformTransactionManager platformTransactionManager, MatsSerializer matsSerializer) { // JMS + Spring's DataSourceTransactionManager-based MatsTransactionManager JmsMatsTransactionManager springSqlTxMgr = JmsMatsTransactionManager_JmsAndSpringManagedSqlTx.create( platformTransactionManager); return getMatsFactoryStopLocalVmBrokerWrapper(concurrency, matsSerializer, springSqlTxMgr); } /** * Convenience variant of * {@link #createSpringDataSourceTxTestMatsFactory(int, PlatformTransactionManager, MatsSerializer)} where * concurrency is 2, which should be adequate for most testing - unless you explicitly want to test concurrency. * * @param platformTransactionManager * the {@link PlatformTransactionManager} that the SpringJmsMats transaction manager should employ. From * this, the {@link DataSource} is gotten using reflection to find a method 'getDataSource()'. please * note that the contained {@link DataSource} should have been wrapped using the static method * {@link JmsMatsTransactionManager_JmsAndSpringManagedSqlTx#wrapLazyConnectionDatasource(DataSource)}. * It is assumed that if username and password is needed, you have configured that on the DataSource. * @param matsSerializer * the MatsSerializer to use. If you employ the standard, use MatsSerializerJson.create(). * * @return the produced {@link MatsFactory} */ public static MatsFactory createSpringDataSourceTxTestMatsFactory( PlatformTransactionManager platformTransactionManager, MatsSerializer matsSerializer) { return createSpringDataSourceTxTestMatsFactory(MatsTestFactory.TEST_CONCURRENCY, platformTransactionManager, matsSerializer); } /** * If you need a {@link MatsFactory} that only handles the JMS transactions, this is your factory method - but if * you DO make any database calls within any Mats endpoint lambda, you will now have no or poor transactional * demarcation, use {@link #createSpringDataSourceTxTestMatsFactory(int, DataSource, MatsSerializer) * createSpringDataSourceTxTestMatsFactory(..)} instead. *

* Usage: In the test, make a @Bean-annotated method which returns the result of this method - or employ the * convenience {@link MatsTestInfrastructureConfiguration}. * * @param concurrency * The concurrency of the created {@link MatsFactory}, set with * {@link FactoryConfig#setConcurrency(int)}. * @param matsSerializer * the MatsSerializer to use. If you employ the standard, use MatsSerializerJson.create(). * * @return the produced {@link MatsFactory} */ public static MatsFactory createJmsTxOnlyTestMatsFactory(int concurrency, MatsSerializer matsSerializer) { // JMS only MatsTransactionManager JmsMatsTransactionManager jmsOnlyTxMgr = JmsMatsTransactionManager_Jms.create(); return getMatsFactoryStopLocalVmBrokerWrapper(concurrency, matsSerializer, jmsOnlyTxMgr); } /** * Convenience variant of {@link #createJmsTxOnlyTestMatsFactory(int, MatsSerializer)} where concurrency is 2, which * should be adequate for most testing - unless you explicitly want to test concurrency. * * @param matsSerializer * the MatsSerializer to use. If you employ the standard, use MatsSerializerJson.create(). * * @return the produced {@link MatsFactory} */ public static MatsFactory createJmsTxOnlyTestMatsFactory(MatsSerializer matsSerializer) { return createJmsTxOnlyTestMatsFactory(MatsTestFactory.TEST_CONCURRENCY, matsSerializer); } private static SpringJmsMatsFactoryWrapper getMatsFactoryStopLocalVmBrokerWrapper(int concurrency, MatsSerializer matsSerializer, JmsMatsTransactionManager springSqlTxMgr) { // Naming broker as calling class, performing replacement of illegal chars according to ActiveMQ rules. String appName = getAppNamePrefix().replaceAll("[^a-zA-Z0-9._\\-:]", ".") + "_" + _sequence.getAndIncrement(); MatsTestBroker inVmActiveMq = MatsTestBroker.create(); // :: Create the JMS and Spring DataSourceTransactionManager-backed JMS MatsFactory. // JmsSessionHandler (pooler) JmsMatsJmsSessionHandler sessionPooler = JmsMatsJmsSessionHandler_Pooling.create( inVmActiveMq.getConnectionFactory()); // The MatsFactory itself, supplying the JmsSessionHandler and MatsTransactionManager. JmsMatsFactory matsFactory = JmsMatsFactory .createMatsFactory(appName, "#testing#", sessionPooler, springSqlTxMgr, matsSerializer); // Reduce number of redeliveries matsFactory.setMatsManagedDlqDivert(MatsTestFactory.TEST_MAX_DELIVERY_ATTEMPTS); // Set name matsFactory.getFactoryConfig().setName(TestSpringMatsFactoryProvider.class.getSimpleName() + "_MF"); // Set concurrency. matsFactory.getFactoryConfig().setConcurrency(concurrency); // Now wrap this in a wrapper that will close the LocalVM ActiveMQ broker upon matsFactory.stop(). MatsFactoryStopLocalVmBrokerWrapper matsFactoryStopLocalVmBrokerWrapper = new MatsFactoryStopLocalVmBrokerWrapper( matsFactory, inVmActiveMq); // And then finally wrap this in the Spring wrapper return new SpringJmsMatsFactoryWrapper(inVmActiveMq.getConnectionFactory(), matsFactoryStopLocalVmBrokerWrapper); } private static class MatsFactoryStopLocalVmBrokerWrapper extends MatsFactoryWrapper { private final MatsTestBroker _matsTestBroker; public MatsFactoryStopLocalVmBrokerWrapper(JmsMatsFactory targetMatsFactory, MatsTestBroker matsTestBroker) { super(targetMatsFactory); _matsTestBroker = matsTestBroker; } @Override public boolean stop(int gracefulShutdownMillis) { log.info(LOG_PREFIX + "Stopping test JmsMatsFactory."); boolean stopped = super.stop(gracefulShutdownMillis); log.info(LOG_PREFIX + "Stopping MatsTestBroker (stopping any in-vm broker)."); _matsTestBroker.close(); return stopped; } } /** * from Stackoverflow - Denys Séguret. */ private static String getAppNamePrefix() { StackTraceElement[] stElements = Thread.currentThread().getStackTrace(); for (int i = 1; i < stElements.length; i++) { StackTraceElement ste = stElements[i]; if (!ste.getClassName().equals(TestSpringMatsFactoryProvider.class.getName()) && !ste.getClassName().startsWith(MatsTestInfrastructureConfiguration.class.getName()) && !ste.getClassName().startsWith(MatsTestInfrastructureDbConfiguration.class.getName()) && !ste.getClassName().startsWith("org.springframework.") && !ste.getClassName().startsWith("sun.") && !ste.getClassName().startsWith("java.") && !ste.getClassName().startsWith("org.junit.") && !ste.getClassName().startsWith("com.intellij.")) { return ste.getClassName(); } } // E-> Could not determine a reasonable suggestion for "app name" based on stack frames. return "Testing_" + TestSpringMatsFactoryProvider.class.getSimpleName(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy