/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.internal.connection; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyVararg; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mule.runtime.api.config.PoolingProfile.DEFAULT_MAX_POOL_WAIT; import static org.mule.runtime.api.config.PoolingProfile.INITIALISE_NONE; import static org.mule.runtime.api.config.PoolingProfile.WHEN_EXHAUSTED_FAIL; import static org.mule.runtime.api.config.PoolingProfile.WHEN_EXHAUSTED_WAIT; import static org.mule.tck.MuleTestUtils.spyInjector; import org.mule.runtime.api.config.PoolingProfile; import org.mule.runtime.api.connection.ConnectionException; import org.mule.runtime.api.connection.ConnectionHandler; import org.mule.runtime.api.connection.ConnectionProvider; import org.mule.runtime.api.connection.ConnectionValidationResult; import org.mule.runtime.api.connection.PoolingListener; import org.mule.runtime.core.api.Injector; import org.mule.runtime.api.lifecycle.Lifecycle; import org.mule.tck.junit4.AbstractMuleContextTestCase; import java.util.Arrays; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class PoolingConnectionManagementStrategyTestCase extends AbstractMuleContextTestCase { private static final int MAX_ACTIVE = 2; private ConnectionProvider<Lifecycle> connectionProvider; private Object config = new Object(); private PoolingProfile poolingProfile = new PoolingProfile(MAX_ACTIVE, MAX_ACTIVE, DEFAULT_MAX_POOL_WAIT, WHEN_EXHAUSTED_WAIT, INITIALISE_NONE); private PoolingConnectionManagementStrategy<Lifecycle> strategy; private PoolingListener<Lifecycle> poolingListener; private Injector injector; private ConnectionHandler<Lifecycle> connection1; private ConnectionHandler<Lifecycle> connection2; @Before public void before() throws Exception { poolingListener = mock(PoolingListener.class); injector = spyInjector(muleContext); muleContext.start(); resetConnectionProvider(); initStrategy(); connection1 = strategy.getConnectionHandler(); connection2 = strategy.getConnectionHandler(); } @Test public void getConnection() throws Exception { assertThat(connection1, is(not(sameInstance(connection2)))); assertThat(connection1.getConnection(), is(not(sameInstance(connection2.getConnection())))); verify(connectionProvider, times(2)).connect(); verify(poolingListener).onBorrow(connection1.getConnection()); verify(poolingListener).onBorrow(connection2.getConnection()); verifyThat(Lifecycle::initialise); verifyThat(Lifecycle::start); } @Test public void poolingListenerFailsOnBorrow() throws Exception { initStrategy(); final RuntimeException exception = new RuntimeException(); doThrow(exception).when(poolingListener).onBorrow(any(Lifecycle.class)); try { strategy.getConnectionHandler(); fail("was expecting poolingListener to fail"); } catch (Exception e) { assertThat(e.getCause(), is(sameInstance(exception))); verify(connectionProvider).disconnect(any(Lifecycle.class)); } } @Test public void connectionsDependenciesInjected() throws Exception { verify(injector).inject(connection1.getConnection()); verify(injector).inject(connection2.getConnection()); } @Test public void exhaustion() throws Exception { poolingProfile = new PoolingProfile(1, 1, DEFAULT_MAX_POOL_WAIT, WHEN_EXHAUSTED_FAIL, INITIALISE_NONE); initStrategy(); ConnectionHandler<Lifecycle> connectionHandler = strategy.getConnectionHandler(); try { strategy.getConnectionHandler(); fail("Was expecting the pool to be exhausted"); } catch (ConnectionException e) { // wiiiii } connectionHandler.release(); assertThat(strategy.getConnectionHandler(), is(notNullValue())); } @Test public void release() throws Exception { connection1.release(); connection2.release(); strategy.close(); verify(connectionProvider, times(2)).disconnect(any(Lifecycle.class)); verifyThat(Lifecycle::stop); verifyThat(Lifecycle::dispose); } @Test(expected = ConnectionException.class) public void failDueToInvalidConnection() throws ConnectionException { when(connectionProvider.validate(anyVararg())).thenReturn(ConnectionValidationResult .failure("Invalid username or password", new Exception("401: UNAUTHORIZED"))); strategy.getConnectionHandler().getConnection(); } @Test(expected = ConnectionException.class) public void failDueToNullConnectionValidationResult() throws ConnectionException { when(connectionProvider.validate(anyVararg())).thenReturn(null); strategy.getConnectionHandler().getConnection(); } private void resetConnectionProvider() throws ConnectionException { ConnectionProvider<Lifecycle> connectionProvider = mock(ConnectionProvider.class); when(connectionProvider.connect()).thenAnswer(i -> mock(Lifecycle.class)); when(connectionProvider.validate(anyObject())).thenReturn(ConnectionValidationResult.success()); this.connectionProvider = spy(new LifecycleAwareConnectionProviderWrapper<>(connectionProvider, muleContext)); } private void initStrategy() { strategy = new PoolingConnectionManagementStrategy<>(connectionProvider, poolingProfile, poolingListener, muleContext); } private <T> void verifyThat(Assertion<T> assertion) throws Exception { verifyThat(assertion, (T) connection1.getConnection(), (T) connection2.getConnection()); } private <T> void verifyThat(Assertion<T> assertion, T... subjects) { Arrays.stream(subjects).forEach(subject -> { try { assertion.test(verify(subject)); } catch (Exception e) { throw new RuntimeException(e); } }); } @FunctionalInterface private interface Assertion<T> { void test(T subject) throws Exception; } }