/* * 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.module.extension.internal.runtime.config; import static java.util.Arrays.asList; import static java.util.Optional.empty; import static java.util.concurrent.TimeUnit.MINUTES; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.Assert.assertThat; import static org.junit.internal.matchers.ThrowableMessageMatcher.hasMessage; import static org.junit.rules.ExpectedException.none; import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; import static org.mule.runtime.api.util.ExtensionModelTestUtils.visitableMock; import static org.mule.runtime.module.extension.internal.runtime.resolver.ValueResolvingContext.from; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.mockClassLoaderModelProperty; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.mockConfigurationInstance; import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.mockInterceptors; import org.mule.runtime.api.connection.ConnectionProvider; import org.mule.runtime.api.exception.MuleException; import org.mule.runtime.api.lifecycle.InitialisationException; import org.mule.runtime.api.lifecycle.Lifecycle; import org.mule.runtime.api.meta.model.operation.OperationModel; import org.mule.runtime.core.util.collection.ImmutableListCollector; import org.mule.runtime.extension.api.runtime.ConfigurationInstance; import org.mule.runtime.extension.api.runtime.ExpirationPolicy; import org.mule.runtime.module.extension.internal.runtime.ImmutableExpirationPolicy; import org.mule.runtime.module.extension.internal.runtime.resolver.ConnectionProviderResolver; import org.mule.runtime.module.extension.internal.runtime.resolver.ResolverSet; import org.mule.runtime.module.extension.internal.runtime.resolver.ResolverSetResult; import org.mule.tck.size.SmallTest; import org.mule.test.heisenberg.extension.HeisenbergExtension; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.Optional; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.runners.MockitoJUnitRunner; @SmallTest @RunWith(MockitoJUnitRunner.class) public class DynamicConfigurationProviderTestCase extends AbstractConfigurationProviderTestCase<HeisenbergExtension> { private static final Class MODULE_CLASS = HeisenbergExtension.class; @Rule public ExpectedException expected = none(); @Mock private ResolverSet resolverSet; @Mock private OperationModel operationModel; @Mock(answer = RETURNS_DEEP_STUBS) private ResolverSetResult resolverSetResult; @Mock private ConnectionProviderResolver connectionProviderResolver; private ExpirationPolicy expirationPolicy; @Override @Before public void before() throws Exception { mockConfigurationInstance(configurationModel, MODULE_CLASS.newInstance()); mockInterceptors(configurationModel, null); when(configurationModel.getOperationModels()).thenReturn(asList(operationModel)); when(configurationModel.getSourceModels()).thenReturn(ImmutableList.of()); mockClassLoaderModelProperty(extensionModel, getClass().getClassLoader()); when(extensionModel.getSourceModels()).thenReturn(ImmutableList.of()); when(extensionModel.getOperationModels()).thenReturn(asList(operationModel)); when(extensionModel.getConfigurationModels()).thenReturn(asList(configurationModel)); when(operationModel.requiresConnection()).thenReturn(true); when(configurationModel.getOperationModels()).thenReturn(asList(operationModel)); when(configurationModel.getSourceModels()).thenReturn(ImmutableList.of()); when(resolverSet.resolve(from(event))).thenReturn(resolverSetResult); visitableMock(operationModel); expirationPolicy = new ImmutableExpirationPolicy(5, MINUTES, timeSupplier); when(connectionProviderResolver.getResolverSet()).thenReturn(empty()); when(connectionProviderResolver.resolve(any())).thenReturn(null); provider = new DynamicConfigurationProvider(CONFIG_NAME, extensionModel, configurationModel, resolverSet, connectionProviderResolver, expirationPolicy, muleContext); super.before(); provider.initialise(); provider.start(); } @After public void after() throws MuleException { provider.stop(); provider.dispose(); } @Test public void resolveCached() throws Exception { final int count = 10; HeisenbergExtension config = (HeisenbergExtension) provider.get(event).getValue(); for (int i = 1; i < count; i++) { assertThat(provider.get(event).getValue(), is(sameInstance(config))); } verify(resolverSet, times(count)).resolve(from(event)); } @Test public void resolveCachedWithProviderParams() throws Exception { ResolverSet providerResolverSet = mock(ResolverSet.class); when(connectionProviderResolver.getResolverSet()).thenReturn(Optional.of(providerResolverSet)); when(providerResolverSet.resolve(from(event))).thenReturn(mock(ResolverSetResult.class)); final int count = 10; HeisenbergExtension config = (HeisenbergExtension) provider.get(event).getValue(); for (int i = 1; i < count; i++) { assertThat(provider.get(event).getValue(), is(sameInstance(config))); } verify(providerResolverSet, times(count)).resolve(from(event)); verify(resolverSet, times(count)).resolve(from(event)); } @Test public void resolveProviderParamsDifferentInstance() throws Exception { HeisenbergExtension config = (HeisenbergExtension) provider.get(event).getValue(); mockConfigurationInstance(configurationModel, MODULE_CLASS.newInstance()); ResolverSet providerResolverSet = mock(ResolverSet.class); when(connectionProviderResolver.getResolverSet()).thenReturn(Optional.of(providerResolverSet)); when(providerResolverSet.resolve(from(event))).thenReturn(mock(ResolverSetResult.class)); assertThat(provider.get(event).getValue(), is(not(sameInstance(config)))); verify(resolverSet, times(2)).resolve(from(event)); verify(providerResolverSet, times(1)).resolve(from(event)); verify(connectionProviderResolver, times(2)).resolve(from(event)); } @Test public void resolveDifferentInstances() throws Exception { HeisenbergExtension instance1 = (HeisenbergExtension) provider.get(event).getValue(); HeisenbergExtension instance2 = makeAlternateInstance(); assertThat(instance2, is(not(sameInstance(instance1)))); } @Test public void getExpired() throws Exception { HeisenbergExtension instance1 = (HeisenbergExtension) provider.get(event).getValue(); HeisenbergExtension instance2 = makeAlternateInstance(); DynamicConfigurationProvider provider = (DynamicConfigurationProvider) this.provider; timeSupplier.move(1, MINUTES); List<ConfigurationInstance> expired = provider.getExpired(); assertThat(expired.isEmpty(), is(true)); timeSupplier.move(10, MINUTES); expired = provider.getExpired(); assertThat(expired.isEmpty(), is(false)); List<Object> configs = expired.stream().map(config -> config.getValue()).collect(new ImmutableListCollector<>()); assertThat(configs, containsInAnyOrder(instance1, instance2)); } private HeisenbergExtension makeAlternateInstance() throws Exception { ResolverSetResult alternateResult = mock(ResolverSetResult.class, Mockito.RETURNS_DEEP_STUBS); mockConfigurationInstance(configurationModel, MODULE_CLASS.newInstance()); when(resolverSet.resolve(from(event))).thenReturn(alternateResult); return (HeisenbergExtension) provider.get(event).getValue(); } @Test public void resolveDynamicConfigWithEquivalentEvent() throws Exception { assertSameInstancesResolved(); } @Test public void resolveDynamicConfigWithDifferentEvent() throws Exception { Object config1 = provider.get(event); when(resolverSet.resolve(from(event))).thenReturn(mock(ResolverSetResult.class)); Object config2 = provider.get(event); assertThat(config1, is(not(sameInstance(config2)))); } @Test public void configFailsOnInitialize() throws Exception { final Lifecycle connProvider = mock(Lifecycle.class, withSettings().extraInterfaces(ConnectionProvider.class)); final String expectedExceptionMessage = "Init failed!"; doThrow(new RuntimeException(expectedExceptionMessage)).when(connProvider).initialise(); when(connectionProviderResolver.resolve(any())).thenReturn((ConnectionProvider) connProvider); expected.expectCause(hasMessage(is(InitialisationException.class.getName() + ": " + expectedExceptionMessage))); try { provider.get(event); } finally { verify(connProvider).initialise(); verify(connProvider, never()).start(); verify(connProvider, never()).stop(); verify(connProvider).dispose(); } } @Test public void configFailsOnStart() throws Exception { final Lifecycle connProvider = mock(Lifecycle.class, withSettings().extraInterfaces(ConnectionProvider.class)); final RuntimeException toThrow = new RuntimeException("Start failed!"); doThrow(toThrow).when(connProvider).start(); when(connectionProviderResolver.resolve(any())).thenReturn((ConnectionProvider) connProvider); expected.expectCause(sameInstance(toThrow)); try { provider.get(event); } finally { verify(connProvider).initialise(); verify(connProvider).start(); verify(connProvider).stop(); verify(connProvider).dispose(); } } }