/*
* 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.manager;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mule.metadata.api.builder.BaseTypeBuilder.create;
import static org.mule.metadata.api.model.MetadataFormat.JAVA;
import static org.mule.runtime.api.util.ExtensionModelTestUtils.visitableMock;
import static org.mule.runtime.core.api.config.MuleProperties.OBJECT_CONNECTION_MANAGER;
import static org.mule.runtime.core.api.config.MuleProperties.OBJECT_METADATA_SERVICE;
import static org.mule.runtime.module.extension.internal.util.MuleExtensionUtils.getImplicitConfigurationProviderName;
import static org.mule.tck.util.MuleContextUtils.mockContextWithServices;
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.mockExecutorFactory;
import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.mockInterceptors;
import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.mockParameters;
import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.stubRegistryKeys;
import static org.mule.test.module.extension.internal.util.ExtensionsTestUtils.toMetadataType;
import org.mule.runtime.api.lifecycle.InitialisationException;
import org.mule.runtime.api.meta.model.ExtensionModel;
import org.mule.runtime.api.meta.model.XmlDslModel;
import org.mule.runtime.api.meta.model.config.ConfigurationModel;
import org.mule.runtime.api.meta.model.connection.ConnectionProviderModel;
import org.mule.runtime.api.meta.model.operation.OperationModel;
import org.mule.runtime.api.meta.model.parameter.ParameterModel;
import org.mule.runtime.core.api.Event;
import org.mule.runtime.core.api.MuleContext;
import org.mule.runtime.core.api.extension.ExtensionManager;
import org.mule.runtime.core.api.registry.MuleRegistry;
import org.mule.runtime.core.api.registry.RegistrationException;
import org.mule.runtime.core.internal.connection.ConnectionManagerAdapter;
import org.mule.runtime.core.internal.metadata.MuleMetadataService;
import org.mule.runtime.core.retry.policies.NoRetryPolicyTemplate;
import org.mule.runtime.core.transformer.simple.StringToEnum;
import org.mule.runtime.extension.api.runtime.ConfigurationInstance;
import org.mule.runtime.extension.api.runtime.ConfigurationProvider;
import org.mule.runtime.extension.api.runtime.connectivity.ConnectionProviderFactory;
import org.mule.runtime.extension.api.runtime.operation.OperationExecutor;
import org.mule.runtime.extension.api.runtime.operation.OperationExecutorFactory;
import org.mule.runtime.module.extension.internal.loader.java.property.ClassLoaderModelProperty;
import org.mule.runtime.module.extension.internal.loader.java.property.ConnectionProviderFactoryModelProperty;
import org.mule.runtime.module.extension.internal.loader.java.property.ParameterGroupModelProperty;
import org.mule.runtime.module.extension.internal.runtime.ExecutionContextAdapter;
import org.mule.tck.junit4.AbstractMuleTestCase;
import org.mule.tck.size.SmallTest;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@SmallTest
@RunWith(MockitoJUnitRunner.class)
public class DefaultExtensionManagerTestCase extends AbstractMuleTestCase {
private static final String MULESOFT = "MuleSoft";
private static final String OTHER_VENDOR = "OtherVendor";
private static final XmlDslModel XML_DSL_MODEL = XmlDslModel.builder().setPrefix("extension-prefix").build();
private ExtensionManager extensionsManager;
private static final String EXTENSION1_NAME = "extension1";
private static final String EXTENSION1_CONFIG_NAME = "extension1Config";
private static final String EXTENSION1_CONFIG_INSTANCE_NAME = "extension1ConfigInstanceName";
private static final String EXTENSION1_OPERATION_NAME = "extension1OperationName";
private static final String EXTENSION2_NAME = "extension2";
private static final String EXTENSION1_VERSION = "3.6.0";
private static final String EXTENSION2_VERSION = "3.6.0";
@Mock
private ExtensionModel extensionModel1;
@Mock
private ExtensionModel extensionModel2;
@Mock
private ExtensionModel extensionModel3WithRepeatedName;
private MuleContext muleContext = mockContextWithServices();
@Mock(answer = RETURNS_DEEP_STUBS)
private ConfigurationModel extension1ConfigurationModel;
@Mock(answer = RETURNS_DEEP_STUBS)
private ConnectionProviderModel connectionProviderModel;
@Mock(answer = RETURNS_DEEP_STUBS)
private ConnectionManagerAdapter connectionManagerAdapter;
@Mock
private OperationModel extension1OperationModel;
@Mock
private ExecutionContextAdapter extension1OperationContext;
@Mock
private ConfigurationProvider extension1ConfigurationProvider;
@Mock(answer = RETURNS_DEEP_STUBS)
private ConfigurationInstance extension1ConfigurationInstance = mock(ConfigurationInstance.class);
@Mock
private OperationExecutorFactory executorFactory;
@Mock
private OperationExecutor executor;
@Mock(answer = RETURNS_DEEP_STUBS)
private Event event;
private ClassLoader classLoader;
private final Object configInstance = new Object();
@Before
public void before() throws InitialisationException {
DefaultExtensionManager extensionsManager = new DefaultExtensionManager();
extensionsManager.setMuleContext(muleContext);
extensionsManager.initialise();
this.extensionsManager = extensionsManager;
mockClassLoaderModelProperty(extensionModel1, getClass().getClassLoader());
mockClassLoaderModelProperty(extensionModel2, getClass().getClassLoader());
mockClassLoaderModelProperty(extensionModel3WithRepeatedName, getClass().getClassLoader());
when(extensionModel1.getName()).thenReturn(EXTENSION1_NAME);
mockClassLoaderModelProperty(extensionModel1, getClass().getClassLoader());
when(extensionModel1.getXmlDslModel()).thenReturn(XML_DSL_MODEL);
when(extensionModel2.getXmlDslModel()).thenReturn(XML_DSL_MODEL);
when(extensionModel1.getConfigurationModels()).thenReturn(asList(extension1ConfigurationModel));
when(extensionModel2.getName()).thenReturn(EXTENSION2_NAME);
when(extensionModel3WithRepeatedName.getName()).thenReturn(EXTENSION2_NAME);
when(extensionModel1.getVendor()).thenReturn(MULESOFT);
when(extensionModel2.getVendor()).thenReturn(MULESOFT);
when(extensionModel3WithRepeatedName.getVendor()).thenReturn(OTHER_VENDOR);
when(extensionModel1.getVersion()).thenReturn(EXTENSION1_VERSION);
when(extensionModel2.getVersion()).thenReturn(EXTENSION2_VERSION);
mockClassLoaderModelProperty(extensionModel1, getClass().getClassLoader());
when(extensionModel3WithRepeatedName.getVersion()).thenReturn(EXTENSION2_VERSION);
when(extension1ConfigurationModel.getName()).thenReturn(EXTENSION1_CONFIG_NAME);
mockConfigurationInstance(extension1ConfigurationModel, configInstance);
mockInterceptors(extension1ConfigurationModel, null);
when(extension1ConfigurationModel.getOperationModels()).thenReturn(ImmutableList.of(extension1OperationModel));
when(extension1ConfigurationModel.getSourceModels()).thenReturn(ImmutableList.of());
when(extension1ConfigurationModel.getConnectionProviders()).thenReturn(asList(connectionProviderModel));
when(connectionProviderModel.getAllParameterModels()).thenReturn(emptyList());
when(connectionProviderModel.getModelProperty(ConnectionProviderFactoryModelProperty.class))
.thenReturn(Optional.of(new ConnectionProviderFactoryModelProperty(mock(ConnectionProviderFactory.class))));
mockParameters(extension1ConfigurationModel);
mockConfigurationInstance(extension1ConfigurationModel, configInstance);
when(extensionModel1.getConfigurationModel(EXTENSION1_CONFIG_NAME)).thenReturn(of(extension1ConfigurationModel));
when(extensionModel1.getOperationModel(EXTENSION1_OPERATION_NAME)).thenReturn(of(extension1OperationModel));
when(extension1OperationModel.getName()).thenReturn(EXTENSION1_OPERATION_NAME);
when(extension1ConfigurationInstance.getValue()).thenReturn(configInstance);
when(extension1ConfigurationInstance.getModel()).thenReturn(extension1ConfigurationModel);
when(extension1ConfigurationInstance.getName()).thenReturn(EXTENSION1_CONFIG_INSTANCE_NAME);
when(extension1ConfigurationProvider.get(event)).thenReturn(extension1ConfigurationInstance);
when(extension1ConfigurationProvider.getConfigurationModel()).thenReturn(extension1ConfigurationModel);
when(extension1ConfigurationProvider.getExtensionModel()).thenReturn(extensionModel1);
when(extension1ConfigurationProvider.getName()).thenReturn(EXTENSION1_CONFIG_INSTANCE_NAME);
visitableMock(extension1OperationModel);
mockExecutorFactory(extension1OperationModel, executorFactory);
when(executorFactory.createExecutor(extension1OperationModel)).thenReturn(executor);
classLoader = getClass().getClassLoader();
registerExtensions(extensionModel1, extensionModel2, extensionModel3WithRepeatedName);
stubRegistryKeys(muleContext, EXTENSION1_CONFIG_INSTANCE_NAME, EXTENSION1_OPERATION_NAME, EXTENSION1_NAME);
ConnectionManagerAdapter connectionManagerAdapter = mock(ConnectionManagerAdapter.class);
when(connectionManagerAdapter.getDefaultRetryPolicyTemplate()).thenReturn(new NoRetryPolicyTemplate());
when(muleContext.getRegistry().lookupObject(OBJECT_CONNECTION_MANAGER))
.thenReturn(connectionManagerAdapter);
when(muleContext.getRegistry().get(OBJECT_METADATA_SERVICE)).thenReturn(mock(MuleMetadataService.class));
when(muleContext.getRegistry().get(OBJECT_CONNECTION_MANAGER)).thenReturn(connectionManagerAdapter);
}
private void registerExtensions(ExtensionModel... extensionModels) {
Arrays.stream(extensionModels).forEach(extension -> {
when(extension.getModelProperty(ClassLoaderModelProperty.class)).thenReturn(empty());
extensionsManager.registerExtension(extension);
});
}
@Test
public void getExtensions() {
testEquals(asList(extensionModel1, extensionModel2), extensionsManager.getExtensions());
}
@Test
public void getExtensionsByName() {
Optional<ExtensionModel> extension = extensionsManager.getExtension(EXTENSION1_NAME);
assertThat(extension.isPresent(), is(true));
assertThat(extension.get(), is(sameInstance(extensionModel1)));
}
@Test
public void contextClassLoaderKept() {
assertThat(classLoader, sameInstance(Thread.currentThread().getContextClassLoader()));
}
@Test
public void contextClassLoaderKeptAfterException() {
ExtensionModel extensionModel = mock(ExtensionModel.class);
when(extensionModel.getName()).thenThrow(new RuntimeException());
try {
extensionsManager.registerExtension(extensionModel);
fail("was expecting an exception");
} catch (RuntimeException e) {
assertThat(classLoader, sameInstance(Thread.currentThread().getContextClassLoader()));
}
}
@Test
public void getConfigurationByName() throws Exception {
registerConfigurationProvider();
ConfigurationInstance configurationInstance =
extensionsManager.getConfiguration(EXTENSION1_CONFIG_INSTANCE_NAME, event);
assertThat(configurationInstance.getValue(), is(sameInstance(configInstance)));
}
@Test
public void getConfigurationThroughImplicitConfiguration() throws Exception {
when(muleContext.getRegistry().get(getImplicitConfigurationProviderName(extensionModel1, extension1ConfigurationModel)))
.thenReturn(extension1ConfigurationProvider);
when(extension1ConfigurationModel.getModelProperty(ParameterGroupModelProperty.class)).thenReturn(empty());
registerConfigurationProvider();
Optional<ConfigurationInstance> configInstance =
extensionsManager.getConfiguration(extensionModel1, extension1OperationModel, event);
assertThat(configInstance.isPresent(), is(true));
assertThat(configInstance.get().getValue(), is(sameInstance(this.configInstance)));
}
@Test
public void getOperationExecutorThroughImplicitConfigurationConcurrently() throws Exception {
final int threadCount = 2;
final CountDownLatch joinerLatch = new CountDownLatch(threadCount);
MuleRegistry registry = muleContext.getRegistry();
when(extension1ConfigurationModel.getModelProperty(ParameterGroupModelProperty.class)).thenReturn(empty());
when(registry.lookupObjects(ConfigurationProvider.class)).thenReturn(emptyList());
doAnswer(invocation -> {
when(registry.get(getImplicitConfigurationProviderName(extensionModel1, extension1ConfigurationModel)))
.thenReturn(extension1ConfigurationProvider);
new Thread(() -> extensionsManager.getConfiguration(extensionModel1, extension1OperationModel, event)).start();
joinerLatch.countDown();
return null;
}).when(registry).registerObject(anyString(), anyObject());
Optional<ConfigurationInstance> configurationInstance =
extensionsManager.getConfiguration(extensionModel1, extension1OperationModel, event);
joinerLatch.countDown();
assertThat(configurationInstance.isPresent(), is(true));
assertThat(joinerLatch.await(5, TimeUnit.SECONDS), is(true));
assertThat(configurationInstance.get().getValue(), is(sameInstance(configInstance)));
}
@Test(expected = IllegalStateException.class)
public void getOperationExecutorWithNotImplicitConfig() {
when(muleContext.getRegistry().lookupObjects(ConfigurationProvider.class)).thenReturn(emptyList());
makeExtension1ConfigurationNotImplicit();
extensionsManager.getConfiguration(extensionModel1, extension1OperationModel, event);
}
@Test
public void registerTwoExtensionsWithTheSameNameButDifferentVendor() {
registerExtensions(extensionModel2, extensionModel3WithRepeatedName);
List<ExtensionModel> extensionModels = new ArrayList<>(extensionsManager.getExtensions());
List<String> extensionNameList =
extensionModels.stream().map(ExtensionModel::getName).distinct().collect(Collectors.toList());
List<String> extensionVendorList =
extensionModels.stream().map(ExtensionModel::getVendor).distinct().collect(Collectors.toList());
assertThat(extensionModels.size(), is(2));
assertThat(extensionNameList.size(), is(2));
assertThat(extensionVendorList.size(), is(1));
}
@Test
public void ignoresRegisteringAlreadyRegisteredExtensions() {
final int registeredExtensionsCount = extensionsManager.getExtensions().size();
registerExtensions(extensionModel1, extensionModel1, extensionModel1);
assertThat(extensionsManager.getExtensions(), hasSize(registeredExtensionsCount));
}
@Test
public void enumTransformer() throws Exception {
DefaultExtensionManager extensionsManager = new DefaultExtensionManager();
extensionsManager.setMuleContext(muleContext);
extensionsManager.initialise();
ParameterModel parameter = mock(ParameterModel.class);
when(parameter.getType()).thenReturn(toMetadataType(TimeUnit.class));
ParameterModel parameterOfRepeatedEnumType = mock(ParameterModel.class);
when(parameterOfRepeatedEnumType.getType()).thenReturn(toMetadataType(TimeUnit.class));
mockParameters(extension1ConfigurationModel, parameter, parameterOfRepeatedEnumType);
extensionsManager.registerExtension(extensionModel1);
verify(muleContext.getRegistry()).registerTransformer(any(StringToEnum.class));
}
@Test
public void enumCollectionTransformer() throws Exception {
DefaultExtensionManager extensionsManager = new DefaultExtensionManager();
extensionsManager.setMuleContext(muleContext);
extensionsManager.initialise();
ParameterModel parameter = mock(ParameterModel.class);
when(parameter.getType())
.thenReturn(create(JAVA).arrayType().of(toMetadataType(TimeUnit.class)).build());
mockParameters(extension1ConfigurationModel, parameter);
extensionsManager.registerExtension(extensionModel1);
verify(muleContext.getRegistry()).registerTransformer(any(StringToEnum.class));
}
private void makeExtension1ConfigurationNotImplicit() {
ParameterModel parameterModel1 = mock(ParameterModel.class);
when(parameterModel1.isRequired()).thenReturn(true);
mockParameters(extension1ConfigurationModel, parameterModel1, parameterModel1);
mockConfigurationInstance(extension1ConfigurationModel, configInstance);
}
private void testEquals(Collection<ExtensionModel> expected, Collection<ExtensionModel> obtained) {
assertThat(obtained.size(), is(expected.size()));
Iterator<ExtensionModel> expectedIterator = expected.iterator();
Iterator<ExtensionModel> obtainedIterator = expected.iterator();
while (expectedIterator.hasNext()) {
assertThat(obtainedIterator.hasNext(), is(true));
testEquals(expectedIterator.next(), obtainedIterator.next());
}
}
private void testEquals(ExtensionModel expected, ExtensionModel obtained) {
assertThat(obtained.getName(), equalTo(expected.getName()));
assertThat(obtained.getVersion(), equalTo(expected.getVersion()));
}
private void registerConfigurationProvider() throws RegistrationException {
extensionsManager.registerConfigurationProvider(extension1ConfigurationProvider);
verify(muleContext.getRegistry()).registerObject(extension1ConfigurationProvider.getName(), extension1ConfigurationProvider);
when(muleContext.getRegistry().lookupObjects(ConfigurationProvider.class))
.thenReturn(asList(extension1ConfigurationProvider));
when(muleContext.getRegistry().get(extension1ConfigurationProvider.getName())).thenReturn(extension1ConfigurationProvider);
}
}