Skip to content

Commit 149c6fd

Browse files
Refactor to extend junit closeableResource to clean up jms resources (#112)
Co-authored-by: santhosh <santhosh.kotha@hmcts.net>
1 parent fa09856 commit 149c6fd

19 files changed

+423
-203
lines changed

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsMessageConsumerClientBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ public static JmsMessageConsumerClientBuilder newJmsMessageConsumerClientBuilder
2626
}
2727

2828
@VisibleForTesting
29-
JmsMessageConsumerClientBuilder(final String topicName, final JmsSingletonResourceProvider jmsSingletonResourceProvider) {
29+
JmsMessageConsumerClientBuilder(final String topicName, final JmsResourcesContext jmsResourcesContext) {
3030
this.topicName = topicName;
31-
this.jmsMessageClientFactory = jmsSingletonResourceProvider.getJmsMessageClientFactory();
31+
this.jmsMessageClientFactory = jmsResourcesContext.getJmsMessageClientFactory();
3232
}
3333

3434
private JmsMessageConsumerClientBuilder(final String topicName) {
35-
this(topicName, new JmsSingletonResourceProvider());
35+
this(topicName, new JmsResourcesContextProvider().get());
3636
}
3737

3838
public JmsMessageConsumerClientBuilder withEventNames(final String eventName, final String...additionalEventNames) {

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsMessageConsumerFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ MessageConsumer createAndStart(final ActiveMQTopic topic, final String messageSe
3030

3131
void close() {
3232
jmsSessionFactory.close(); //closes session and underlying connection, connectionFactory
33+
this.session = null;
3334
}
3435
}

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsMessageConsumerPool.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* Provides method to clear messages from all cached consumers (so that same consumers can be reused across tests)
1515
* Provides method to close cached consumers without closing underlying session and connection (provides capability to recycle consumers after each Test class)
1616
* Provides method to close underlying session/connection
17-
* This class is created through {@link JmsSingletonResourceProvider} which maintains singleton ness
1817
*/
1918
class JmsMessageConsumerPool {
2019

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsMessageProducerClient.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
/**
1414
* Use {@link JmsMessageProducerClientBuilder} to create instance
1515
* It's safe to create multiple instances of this class with same parameters, as underlying jms producer is cached and it retrieves existing producer
16+
* It's not recommended to cache these classes across Test classes, reusability is recommended only within same Test class. After each Test class all cached producers are closed.
17+
* Hence, don't create instances of this class in Helper/Utility classes and assign it to static class variables. Doing this within a Test class is totally fine though
1618
* Life cycle of underlying jms producer is not managed by this class (Managed by {@link JmsMessageConsumerPool} through Junit hooks {@link JmsResourceManagementExtension}) and hence these instances can be created without worrying about cleaning underlying jms resources
1719
* This class provides all various helper methods to send message to underlying topic
1820
* If there is no convenient method that you are looking for, please enhance this class rather than creating them in context ITs. This approach avoids duplication and promotes reusability across different context Integration tests

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsMessageProducerClientBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ public static JmsMessageProducerClientBuilder newPrivateJmsMessageProducerClient
1515
}
1616

1717
@VisibleForTesting
18-
JmsMessageProducerClientBuilder(final String topicName, final JmsSingletonResourceProvider jmsSingletonResourceProvider) {
18+
JmsMessageProducerClientBuilder(final String topicName, final JmsResourcesContext jmsResourcesContext) {
1919
this.topicName = topicName;
20-
this.jmsMessageClientFactory = jmsSingletonResourceProvider.getJmsMessageClientFactory();
20+
this.jmsMessageClientFactory = jmsResourcesContext.getJmsMessageClientFactory();
2121
}
2222

2323
private JmsMessageProducerClientBuilder(final String topicName) {
24-
this(topicName, new JmsSingletonResourceProvider());
24+
this(topicName, new JmsResourcesContextProvider().get());
2525
}
2626

2727
public JmsMessageProducerClient build() {

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsMessageProducerFactory.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
import java.util.Map;
1010
import java.util.concurrent.ConcurrentHashMap;
1111

12-
/**
13-
* This class need to be singleton and it's maintained by {@link JmsSingletonResourceProvider}
14-
*/
1512
class JmsMessageProducerFactory {
1613

1714
private Session session;
@@ -34,18 +31,21 @@ Session getSession(String queueUri) {
3431
return session;
3532
}
3633

37-
void close() {
34+
void closeProducers() {
3835
try{
3936
for(final MessageProducer messageProducer : messageProducers.values()) {
4037
messageProducer.close();
4138
}
4239
messageProducers.clear();
43-
jmsSessionFactory.close(); //closes session and underlying connection, connectionFactory
4440
} catch (JMSException e) {
4541
throw new JmsMessagingClientException("Failed to close producers", e);
4642
}
4743
}
4844

45+
void close() {
46+
jmsSessionFactory.close(); //closes session and underlying connection, connectionFactory
47+
}
48+
4949
private void createSessionIfNull(String queueUri) {
5050
if (session == null) {
5151
session = jmsSessionFactory.create(queueUri);

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsResourceManagementExtension.java

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,65 +5,67 @@
55
import org.junit.jupiter.api.extension.BeforeAllCallback;
66
import org.junit.jupiter.api.extension.BeforeEachCallback;
77
import org.junit.jupiter.api.extension.ExtensionContext;
8-
import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
8+
import org.junit.jupiter.api.extension.ExtensionContext.Store;
99

1010
import static org.junit.jupiter.api.extension.ExtensionContext.Namespace.GLOBAL;
1111

1212
/**
1313
* This manages Life cycle of Jms consumers and producers created by a Test class using various junit hooks.
1414
* As long as this extension is used by an Integration test, as a developer of writing integration tests you don't need to worry about managing underlying jms resources that are created in Tests
1515
*/
16-
public class JmsResourceManagementExtension implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback, CloseableResource {
16+
public class JmsResourceManagementExtension implements BeforeAllCallback, BeforeEachCallback, AfterAllCallback {
1717

18-
private final JmsMessageConsumerPool jmsMessageConsumerPool;
19-
20-
private final JmsMessageProducerFactory jmsMessageProducerFactory;
21-
22-
private static boolean registered = false;
18+
static final String TEST_SUITE_SHUTDOWN_JMS_RESOURCE_CLEANUP_HOOK = "JMS_RESOURCE_CLEANUP_HOOK";
19+
static final String TEST_SUITE_SHUTDOWN_EXECUTION_TIMER_HOOK = "EXECUTION_TIMER_HOOK";
20+
private final JmsResourcesContextProvider jmsResourcesContextProvider;
2321

2422
public JmsResourceManagementExtension() {
25-
this(new JmsSingletonResourceProvider().getJmsMessageConsumerPool(),
26-
new JmsSingletonResourceProvider().getJmsMessageProducerFactory());
23+
this(new JmsResourcesContextProvider());
2724

2825
}
2926

3027
@VisibleForTesting
31-
JmsResourceManagementExtension(JmsMessageConsumerPool jmsMessageConsumerPool, JmsMessageProducerFactory jmsMessageProducerFactory) {
32-
this.jmsMessageConsumerPool = jmsMessageConsumerPool;
33-
this.jmsMessageProducerFactory = jmsMessageProducerFactory;
28+
JmsResourceManagementExtension(final JmsResourcesContextProvider jmsResourcesContextProvider) {
29+
this.jmsResourcesContextProvider = jmsResourcesContextProvider;
3430
}
3531

3632
@Override
37-
public void beforeAll(final ExtensionContext context) {
38-
if (!registered) { //Not a nice way but there is no other elegant way to achieve this i.e. register hook only once
39-
registered = true;
40-
context.getRoot().getStore(GLOBAL).put("Clean JMS resources", this);
41-
}
33+
public void beforeAll(final ExtensionContext extensionContext) {
34+
final Store globalStore = getGlobalStore(extensionContext);
35+
registerTestSuiteShutdownHookForJmsResourceCleanup(globalStore);
36+
registerTestSuiteShutdownHookForExecutionTimer(globalStore);
4237
}
4338

4439
/**
4540
* Drains all consumer queues created during test, so that message consumers can be reused across other tests within same Test class
4641
*/
4742
@Override
4843
public void beforeEach(final ExtensionContext extensionContext) {
49-
jmsMessageConsumerPool.clearMessages();
44+
jmsResourcesContextProvider.get().clearMessages();
5045
}
5146

5247
/**
53-
* Closes all consumers that are created across all tests within a single Test class. So, consumers can't be reused across different Test classes (Without this recycling, number of message consumers may outgrow and can cause resource exhaustion)
48+
* Closes all consumers/producers that are created across all tests within a single Test class. So, consumers/producers can't be reused across different Test classes (Without this recycling, number of message consumers may outgrow and can cause resource exhaustion)
5449
*/
5550
@Override
5651
public void afterAll(final ExtensionContext extensionContext) {
57-
jmsMessageConsumerPool.closeConsumers();
52+
jmsResourcesContextProvider.get().closeConsumersAndProducers();
5853
}
5954

60-
/**
61-
* This is invoked at the end of test suite (through hook registered in beforeAll() method).
62-
* This promotes reusing jms session and connection across entire test suite
63-
*/
64-
@Override
65-
public void close() {
66-
jmsMessageConsumerPool.close();
67-
jmsMessageProducerFactory.close();
55+
private Store getGlobalStore(ExtensionContext extensionContext) {
56+
return extensionContext.getRoot().getStore(GLOBAL);
57+
}
58+
59+
private void registerTestSuiteShutdownHookForJmsResourceCleanup(Store globalStore) {
60+
if (globalStore.get(TEST_SUITE_SHUTDOWN_JMS_RESOURCE_CLEANUP_HOOK) == null) {
61+
System.out.println("----------Registering test suite shutdown hook (jms resource cleanup)-------------");
62+
globalStore.put(TEST_SUITE_SHUTDOWN_JMS_RESOURCE_CLEANUP_HOOK, jmsResourcesContextProvider);
63+
}
64+
}
65+
66+
private void registerTestSuiteShutdownHookForExecutionTimer(Store globalStore) {
67+
if (globalStore.get(TEST_SUITE_SHUTDOWN_EXECUTION_TIMER_HOOK) == null) {
68+
globalStore.put(TEST_SUITE_SHUTDOWN_EXECUTION_TIMER_HOOK, new TestSuiteExecutionTimeCalculator());
69+
}
6870
}
6971
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package uk.gov.justice.services.integrationtest.utils.jms;
2+
3+
import com.google.common.annotations.VisibleForTesting;
4+
import org.apache.commons.lang3.time.StopWatch;
5+
import uk.gov.justice.services.integrationtest.utils.jms.converters.ToJsonEnvelopeMessageConverter;
6+
import uk.gov.justice.services.integrationtest.utils.jms.converters.ToJsonPathMessageConverter;
7+
import uk.gov.justice.services.integrationtest.utils.jms.converters.ToStringMessageConverter;
8+
import uk.gov.justice.services.messaging.DefaultJsonObjectEnvelopeConverter;
9+
10+
import java.util.concurrent.TimeUnit;
11+
12+
public class JmsResourcesContext {
13+
14+
private final JmsMessageConsumerPool jmsMessageConsumerPool;
15+
private final JmsMessageProducerFactory jmsMessageProducerFactory;
16+
private final JmsMessageClientFactory jmsMessageClientFactory;
17+
private final StopWatch stopWatch;
18+
19+
JmsResourcesContext(final JmsMessageConsumerPool jmsMessageConsumerPool,
20+
final JmsMessageProducerFactory jmsMessageProducerFactory) {
21+
this(jmsMessageConsumerPool, jmsMessageProducerFactory,
22+
new JmsMessageClientFactory(jmsMessageProducerFactory,
23+
new ToStringMessageConverter(),
24+
new ToJsonEnvelopeMessageConverter(new DefaultJsonObjectEnvelopeConverter()),
25+
new ToJsonPathMessageConverter(),
26+
new JmsMessageReader(),
27+
jmsMessageConsumerPool),
28+
new StopWatch());
29+
}
30+
31+
@VisibleForTesting
32+
JmsResourcesContext(final JmsMessageConsumerPool jmsMessageConsumerPool,
33+
final JmsMessageProducerFactory jmsMessageProducerFactory,
34+
final JmsMessageClientFactory jmsMessageClientFactory,
35+
final StopWatch stopWatch) {
36+
this.jmsMessageConsumerPool = jmsMessageConsumerPool;
37+
this.jmsMessageProducerFactory = jmsMessageProducerFactory;
38+
this.jmsMessageClientFactory = jmsMessageClientFactory;
39+
this.stopWatch = stopWatch;
40+
}
41+
42+
JmsMessageClientFactory getJmsMessageClientFactory() {
43+
return jmsMessageClientFactory;
44+
}
45+
46+
void clearMessages() {
47+
jmsMessageConsumerPool.clearMessages();
48+
}
49+
50+
void closeConsumersAndProducers() {
51+
jmsMessageConsumerPool.closeConsumers();
52+
jmsMessageProducerFactory.closeProducers();
53+
}
54+
55+
void close() {
56+
System.out.println("----------Closing JMS resources-------------");
57+
58+
stopWatch.start();
59+
jmsMessageConsumerPool.close();
60+
jmsMessageProducerFactory.close();
61+
stopWatch.stop();
62+
63+
System.out.printf("----------JMS resources closed in %s secs\n", stopWatch.getTime(TimeUnit.SECONDS));
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package uk.gov.justice.services.integrationtest.utils.jms;
2+
3+
import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
4+
import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource;
5+
import uk.gov.justice.services.test.utils.core.messaging.TopicFactory;
6+
7+
class JmsResourcesContextProvider implements CloseableResource {
8+
9+
private static JmsResourcesContext jmsResourcesContext;
10+
11+
JmsResourcesContext get() {
12+
if(jmsResourcesContext == null) {
13+
jmsResourcesContext = new JmsResourcesContext(createJmsMessageConsumerPool(), createJmsMessageProducerFactory());
14+
}
15+
16+
return jmsResourcesContext;
17+
}
18+
19+
private JmsMessageConsumerPool createJmsMessageConsumerPool() {
20+
return new JmsMessageConsumerPool(new JmsMessageConsumerFactory(new JmsSessionFactory(new ActiveMQConnectionFactory())),
21+
new TopicFactory(), new JmsMessageReader());
22+
}
23+
24+
private JmsMessageProducerFactory createJmsMessageProducerFactory() {
25+
return new JmsMessageProducerFactory(new JmsSessionFactory(new ActiveMQConnectionFactory()), new TopicFactory());
26+
}
27+
28+
@Override
29+
public void close() {
30+
if(jmsResourcesContext != null) {
31+
jmsResourcesContext.close();
32+
jmsResourcesContext = null;
33+
}
34+
}
35+
}

test-utils/integration-test-utils-jms/src/main/java/uk/gov/justice/services/integrationtest/utils/jms/JmsSingletonResourceProvider.java

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)