diff --git a/extensions/validation/src/test/resources/basic-validations.xml b/extensions/validation/src/test/resources/basic-validations.xml index ba887d7e0b9..14d5ecc11e7 100644 --- a/extensions/validation/src/test/resources/basic-validations.xml +++ b/extensions/validation/src/test/resources/basic-validations.xml @@ -11,58 +11,55 @@ - - - + - + - + - + - + - + - + - + - + - + - + - + - - + + @@ -79,11 +76,11 @@ - + - + diff --git a/extensions/validation/src/test/resources/custom-validator.xml b/extensions/validation/src/test/resources/custom-validator.xml index 1a50f6c8782..d333ee15263 100644 --- a/extensions/validation/src/test/resources/custom-validator.xml +++ b/extensions/validation/src/test/resources/custom-validator.xml @@ -5,11 +5,8 @@ xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd http://www.mulesoft.org/schema/mule/validation http://www.mulesoft.org/schema/mule/validation/current/mule-validation.xsd"> - - - - + diff --git a/extensions/validation/src/test/resources/number-validations.xml b/extensions/validation/src/test/resources/number-validations.xml index d111133a5e0..b5802df0237 100644 --- a/extensions/validation/src/test/resources/number-validations.xml +++ b/extensions/validation/src/test/resources/number-validations.xml @@ -5,10 +5,8 @@ xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd http://www.mulesoft.org/schema/mule/validation http://www.mulesoft.org/schema/mule/validation/current/mule-validation.xsd"> - - - + diff --git a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/capability/xml/schema/SchemaBuilder.java b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/capability/xml/schema/SchemaBuilder.java index f3109a4128f..e7fe49aece4 100644 --- a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/capability/xml/schema/SchemaBuilder.java +++ b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/capability/xml/schema/SchemaBuilder.java @@ -632,7 +632,7 @@ private void registerOperationType(String name, Operation operation) complexContentExtension.setBase(base); complexContent.setExtension(complexContentExtension); - Attribute configAttr = createAttribute(ATTRIBUTE_NAME_CONFIG, ATTRIBUTE_DESCRIPTION_CONFIG, false, SUBSTITUTABLE_NAME); + Attribute configAttr = createAttribute(ATTRIBUTE_NAME_CONFIG, ATTRIBUTE_DESCRIPTION_CONFIG, true, SUBSTITUTABLE_NAME); complexContentExtension.getAttributeOrAttributeGroup().add(configAttr); final ExplicitGroup all = new ExplicitGroup(); diff --git a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/BaseResolverFactoryBean.java b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/BaseResolverFactoryBean.java deleted file mode 100644 index 21e49cd1266..00000000000 --- a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/BaseResolverFactoryBean.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.module.extension.internal.config; - -import org.mule.module.extension.internal.runtime.resolver.ConfigurationValueResolver; -import org.mule.module.extension.internal.runtime.resolver.ValueResolver; - -import org.springframework.beans.factory.FactoryBean; - -abstract class BaseResolverFactoryBean implements FactoryBean -{ - protected final String name; - protected ElementDescriptor element; - protected T valueResolver; - - BaseResolverFactoryBean(String name, ElementDescriptor element) - { - this.name = name; - this.element = element; - } - - protected abstract T createValueResolver(); - - /** - * Returns a {@link ConfigurationValueResolver} - */ - @Override - public T getObject() throws Exception - { - return valueResolver; - } - - /** - * @return {@value true} - */ - @Override - public boolean isSingleton() - { - return true; - } -} diff --git a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationBeanDefinitionParser.java b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationBeanDefinitionParser.java index 88d7446c2f0..fe212d44e6c 100644 --- a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationBeanDefinitionParser.java +++ b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationBeanDefinitionParser.java @@ -33,7 +33,7 @@ final class ConfigurationBeanDefinitionParser extends BaseExtensionBeanDefinitio ConfigurationBeanDefinitionParser(Configuration configuration) { - super(ConfigurationFactoryBean.class); + super(ConfigurationInstanceProviderFactoryBean.class); this.configuration = configuration; } diff --git a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationFactoryBean.java b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationFactoryBean.java deleted file mode 100644 index 8343a8fe2e6..00000000000 --- a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationFactoryBean.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.module.extension.internal.config; - -import static org.mule.module.extension.internal.config.XmlExtensionParserUtils.getResolverSet; -import org.mule.api.MuleContext; -import org.mule.extension.introspection.Configuration; -import org.mule.module.extension.internal.runtime.resolver.ConfigurationValueResolver; - -import org.springframework.beans.factory.FactoryBean; - -/** - * A {@link FactoryBean} which returns a {@link ConfigurationValueResolver} that provides the actual instances - * that implement a given {@link Configuration}. Subsequent invokations to {@link #getObject()} method - * returns always the same {@link ConfigurationValueResolver}. - * - * @since 3.7.0 - */ -final class ConfigurationFactoryBean extends BaseResolverFactoryBean -{ - - private final Configuration configuration; - private final MuleContext muleContext; - - ConfigurationFactoryBean(String name, - Configuration configuration, - ElementDescriptor element, - MuleContext muleContext) - { - super(name, element); - this.configuration = configuration; - this.muleContext = muleContext; - valueResolver = createValueResolver(); - } - - @Override - protected ConfigurationValueResolver createValueResolver() - { - return new ConfigurationValueResolver(name, - configuration, - getResolverSet(element, configuration.getParameters()), - muleContext); - } - - /** - * @return {@link ConfigurationValueResolver} - */ - @Override - public Class getObjectType() - { - return ConfigurationValueResolver.class; - } -} diff --git a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationInstanceProviderFactoryBean.java b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationInstanceProviderFactoryBean.java new file mode 100644 index 00000000000..4d2221b496e --- /dev/null +++ b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/ConfigurationInstanceProviderFactoryBean.java @@ -0,0 +1,94 @@ +/* + * 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.module.extension.internal.config; + +import static org.mule.MessageExchangePattern.REQUEST_RESPONSE; +import static org.mule.module.extension.internal.config.XmlExtensionParserUtils.getResolverSet; +import org.mule.DefaultMuleEvent; +import org.mule.DefaultMuleMessage; +import org.mule.api.MuleContext; +import org.mule.api.MuleEvent; +import org.mule.api.MuleRuntimeException; +import org.mule.api.construct.FlowConstruct; +import org.mule.extension.introspection.Configuration; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.module.extension.internal.runtime.ConfigurationObjectBuilder; +import org.mule.module.extension.internal.runtime.DynamicConfigurationInstanceProvider; +import org.mule.module.extension.internal.runtime.StaticConfigurationInstanceProvider; +import org.mule.module.extension.internal.runtime.resolver.ResolverSet; + +import org.springframework.beans.factory.FactoryBean; + +/** + * A {@link FactoryBean} which returns a {@link ConfigurationInstanceProvider} that provides the actual instances + * that implement a given {@link Configuration}. Subsequent invokations to {@link #getObject()} method + * returns always the same {@link ConfigurationInstanceProvider}. + * + * @since 3.7.0 + */ +final class ConfigurationInstanceProviderFactoryBean implements FactoryBean> +{ + + private final ConfigurationInstanceProvider configurationInstanceProvider; + + ConfigurationInstanceProviderFactoryBean(String name, + Configuration configuration, + ElementDescriptor element, + MuleContext muleContext) + { + ResolverSet resolverSet = getResolverSet(element, configuration.getParameters()); + ConfigurationObjectBuilder configurationObjectBuilder = new ConfigurationObjectBuilder(configuration, resolverSet); + + if (resolverSet.isDynamic()) + { + configurationInstanceProvider = new DynamicConfigurationInstanceProvider(name, configuration, configurationObjectBuilder, resolverSet); + } + else + { + Object configurationInstance = instantiateStaticConfiguration(muleContext, configurationObjectBuilder); + configurationInstanceProvider = new StaticConfigurationInstanceProvider<>(name, configuration, configurationInstance); + } + } + + private Object instantiateStaticConfiguration(MuleContext muleContext, ConfigurationObjectBuilder configurationObjectBuilder) + { + try + { + return configurationObjectBuilder.build(getInitialiserEvent(muleContext)); + } + catch (Exception e) + { + throw new MuleRuntimeException(e); + } + } + + private MuleEvent getInitialiserEvent(MuleContext muleContext) + { + return new DefaultMuleEvent(new DefaultMuleMessage(null, muleContext), REQUEST_RESPONSE, (FlowConstruct) null); + } + + @Override + public ConfigurationInstanceProvider getObject() throws Exception + { + return configurationInstanceProvider; + } + + /** + * @return {@link ConfigurationInstanceProvider} + */ + @Override + public Class getObjectType() + { + return ConfigurationInstanceProvider.class; + } + + @Override + public boolean isSingleton() + { + return true; + } +} diff --git a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationBeanDefinitionParser.java b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationBeanDefinitionParser.java index 8dc07339df8..93f541ce979 100644 --- a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationBeanDefinitionParser.java +++ b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationBeanDefinitionParser.java @@ -29,7 +29,6 @@ import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.support.ManagedMap; @@ -111,10 +110,10 @@ private void parseConfigRef(Element element, BeanDefinitionBuilder builder) String configRef = element.getAttribute(ATTRIBUTE_NAME_CONFIG); if (StringUtils.isBlank(configRef)) { - throw new IllegalArgumentException("All operations must provide a config-ref element"); + configRef = null; } - builder.addConstructorArgValue(new RuntimeBeanReference(configRef)); + builder.addConstructorArgValue(configRef); } diff --git a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationFactoryBean.java b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationFactoryBean.java index ad0d611c4e7..8b7ba1de5a8 100644 --- a/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationFactoryBean.java +++ b/modules/extensions-spring-support/src/main/java/org/mule/module/extension/internal/config/OperationFactoryBean.java @@ -15,7 +15,6 @@ import org.mule.extension.introspection.Operation; import org.mule.module.extension.internal.runtime.processor.OperationMessageProcessor; import org.mule.module.extension.internal.runtime.resolver.ResolverSet; -import org.mule.module.extension.internal.runtime.resolver.ValueResolver; import org.mule.util.ObjectNameHelper; import java.util.List; @@ -31,18 +30,18 @@ public class OperationFactoryBean implements FactoryBean { - private final ValueResolver configurationValueResolver; + private final String configurationInstanceProviderName; private final Operation operation; private final ElementDescriptor element; private final Map> nestedOperations; - public OperationFactoryBean(ValueResolver configurationValueResolver, + public OperationFactoryBean(String configurationInstanceProviderName, Operation operation, ElementDescriptor element, Map> nestedOperations, MuleContext muleContext) { - this.configurationValueResolver = configurationValueResolver; + this.configurationInstanceProviderName = configurationInstanceProviderName; this.operation = operation; this.element = element; this.nestedOperations = nestedOperations; @@ -55,7 +54,7 @@ public OperationFactoryBean(ValueResolver configurationValueResolver, public OperationMessageProcessor getObject() throws Exception { ResolverSet resolverSet = getResolverSet(element, operation.getParameters(), nestedOperations); - return new OperationMessageProcessor(configurationValueResolver, operation, resolverSet); + return new OperationMessageProcessor(operation, configurationInstanceProviderName, resolverSet); } /** diff --git a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ConfigParserTestCase.java b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ConfigParserTestCase.java index 1cc7cea867b..f80403b9874 100644 --- a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ConfigParserTestCase.java +++ b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ConfigParserTestCase.java @@ -17,11 +17,11 @@ import static org.junit.runners.Parameterized.Parameter; import static org.junit.runners.Parameterized.Parameters; import org.mule.api.MuleEvent; -import org.mule.module.extension.KnockeableDoor; import org.mule.module.extension.HealthStatus; import org.mule.module.extension.HeisenbergExtension; +import org.mule.module.extension.KnockeableDoor; import org.mule.module.extension.Ricin; -import org.mule.module.extension.internal.runtime.resolver.ValueResolver; +import org.mule.module.extension.internal.util.ExtensionsTestUtils; import org.mule.tck.junit4.ExtensionsFunctionalTestCase; import java.math.BigDecimal; @@ -102,10 +102,9 @@ public void config() throws Exception @Test public void sameInstanceForEquivalentEvent() throws Exception { - ValueResolver heisenbergResolver = muleContext.getRegistry().lookupObject(testConfig); MuleEvent event = getHeisenbergEvent(); - HeisenbergExtension heisenberg = heisenbergResolver.resolve(event); - assertThat(heisenberg, is(sameInstance(heisenbergResolver.resolve(event)))); + HeisenbergExtension heisenberg = lookupHeisenberg(testConfig, event); + assertThat(heisenberg, is(sameInstance(lookupHeisenberg(testConfig, event)))); } @Test @@ -138,8 +137,12 @@ public void dependenciesInjected() throws Exception private HeisenbergExtension lookupHeisenberg(String key) throws Exception { - ValueResolver heisenbergResolver = muleContext.getRegistry().lookupObject(key); - return (HeisenbergExtension) heisenbergResolver.resolve(getHeisenbergEvent()); + return lookupHeisenberg(key, getHeisenbergEvent()); + } + + private HeisenbergExtension lookupHeisenberg(String key, MuleEvent event) throws Exception + { + return ExtensionsTestUtils.getConfigurationInstance(key, event); } private MuleEvent getHeisenbergEvent() throws Exception diff --git a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ExtensionsAsInjectedDependenciesTestCase.java b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ExtensionsAsInjectedDependenciesTestCase.java index 900766d0ffb..16f47806ed5 100644 --- a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ExtensionsAsInjectedDependenciesTestCase.java +++ b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ExtensionsAsInjectedDependenciesTestCase.java @@ -9,10 +9,10 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.sameInstance; import static org.junit.Assert.assertThat; -import org.mule.VoidMuleEvent; import org.mule.api.MuleEvent; +import org.mule.extension.runtime.ConfigurationInstanceProvider; import org.mule.module.extension.HeisenbergExtension; -import org.mule.module.extension.internal.runtime.resolver.ValueResolver; +import org.mule.module.extension.internal.util.ExtensionsTestUtils; import org.mule.tck.junit4.ExtensionsFunctionalTestCase; import javax.inject.Inject; @@ -23,6 +23,9 @@ public class ExtensionsAsInjectedDependenciesTestCase extends ExtensionsFunctionalTestCase { + private static final String STATIC_HEISENBERG = "staticHeisenberg"; + private static final String DYNAMIC_AGE_HEISENBERG = "dynamicAgeHeisenberg"; + private Dependent dependent; @Override @@ -48,41 +51,46 @@ protected Class[] getAnnotatedExtensionClasses() @Test public void staticHeisenbergWasInjected() throws Exception { - ValueResolver staticHeisenberg = dependent.getStaticHeisenberg(); - assertThat(staticHeisenberg, is(sameInstance(muleContext.getRegistry().get("staticHeisenberg")))); - HeisenbergExtension heisenberg = staticHeisenberg.resolve(VoidMuleEvent.getInstance()); + assertCorrectProviderInjected(STATIC_HEISENBERG, dependent.getStaticHeisenberg()); + HeisenbergExtension heisenberg = ExtensionsTestUtils.getConfigurationInstance(STATIC_HEISENBERG, getTestEvent("")); assertThat(heisenberg.getPersonalInfo().getAge(), is(50)); } @Test public void dynamicHeisenbergWasInjected() throws Exception { - ValueResolver dynamicHeisenberg = dependent.getDynamicAgeHeisenberg(); + assertCorrectProviderInjected(DYNAMIC_AGE_HEISENBERG, dependent.getDynamicAgeHeisenberg()); + final int age = 52; MuleEvent event = getTestEvent(""); event.setFlowVariable("age", age); - HeisenbergExtension heisenberg = dynamicHeisenberg.resolve(event); + HeisenbergExtension heisenberg = ExtensionsTestUtils.getConfigurationInstance(DYNAMIC_AGE_HEISENBERG, event); assertThat(heisenberg.getPersonalInfo().getAge(), is(age)); } + private void assertCorrectProviderInjected(String key, ConfigurationInstanceProvider expected) + { + assertThat(expected, is(sameInstance(muleContext.getRegistry().get(key)))); + } + public static class Dependent { @Inject - @Named("staticHeisenberg") - private ValueResolver staticHeisenberg; + @Named(STATIC_HEISENBERG) + private ConfigurationInstanceProvider staticHeisenberg; @Inject - @Named("dynamicAgeHeisenberg") - private ValueResolver dynamicAgeHeisenberg; + @Named(DYNAMIC_AGE_HEISENBERG) + private ConfigurationInstanceProvider dynamicAgeHeisenberg; - public ValueResolver getStaticHeisenberg() + public ConfigurationInstanceProvider getStaticHeisenberg() { return staticHeisenberg; } - public ValueResolver getDynamicAgeHeisenberg() + public ConfigurationInstanceProvider getDynamicAgeHeisenberg() { return dynamicAgeHeisenberg; } diff --git a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/HeisenbergDefaultConfigTestCase.java b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/HeisenbergDefaultConfigTestCase.java new file mode 100644 index 00000000000..45d630d97f3 --- /dev/null +++ b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/HeisenbergDefaultConfigTestCase.java @@ -0,0 +1,56 @@ +/* + * 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.module.extension.internal; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mule.module.extension.HeisenbergExtension.EXTENSION_NAME; +import org.mule.extension.ExtensionManager; +import org.mule.extension.introspection.Extension; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.tck.junit4.ExtensionsFunctionalTestCase; + +import org.hamcrest.core.IsInstanceOf; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class HeisenbergDefaultConfigTestCase extends ExtensionsFunctionalTestCase +{ + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Override + protected String getConfigFile() + { + return "heisenberg-default-config.xml"; + } + + @Test + public void usesDefaultConfig() throws Exception + { + assertThat(runFlow("sayMyName").getMessage().getPayloadAsString(), is("Heisenberg")); + } + + @Test + public void twoConfigsAndNoConfigRef() throws Exception + { + ExtensionManager extensionManager = muleContext.getExtensionManager(); + Extension extension = extensionManager.getExtensions().iterator().next(); + assertThat(extension.getName(), is(EXTENSION_NAME)); + + ConfigurationInstanceProvider configurationInstanceProvider = mock(ConfigurationInstanceProvider.class); + when(configurationInstanceProvider.getConfiguration()).thenReturn(extension.getConfigurations().get(0)); + extensionManager.registerConfigurationInstanceProvider("secondConfig", configurationInstanceProvider); + + expectedException.expectCause(IsInstanceOf.instanceOf(IllegalStateException.class)); + runFlow("sayMyName"); + } +} diff --git a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ImplicitConfigTestCase.java b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ImplicitConfigTestCase.java new file mode 100644 index 00000000000..5cc412d6364 --- /dev/null +++ b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/ImplicitConfigTestCase.java @@ -0,0 +1,140 @@ +/* + * 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.module.extension.internal; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.junit.Assert.assertThat; +import org.mule.api.MuleContext; +import org.mule.api.MuleEvent; +import org.mule.api.MuleException; +import org.mule.api.context.MuleContextAware; +import org.mule.api.lifecycle.Initialisable; +import org.mule.api.lifecycle.InitialisationException; +import org.mule.api.lifecycle.Startable; +import org.mule.extension.annotations.Extension; +import org.mule.extension.annotations.Operation; +import org.mule.extension.annotations.Operations; +import org.mule.extension.annotations.Parameter; +import org.mule.extension.annotations.capability.Xml; +import org.mule.extension.annotations.param.Optional; +import org.mule.tck.junit4.ExtensionsFunctionalTestCase; + +import org.junit.Test; + +public class ImplicitConfigTestCase extends ExtensionsFunctionalTestCase +{ + + @Override + protected Class[] getAnnotatedExtensionClasses() + { + return new Class[] {ImplicitConfigExtension.class}; + } + + @Override + protected String getConfigFile() + { + return "implicit-config.xml"; + } + + @Test + public void getImplicitConfig() throws Exception + { + final Integer defaultValue = 42; + MuleEvent event = getTestEvent(""); + event.setFlowVariable("optionalWithDefault", defaultValue); + + ImplicitConfigExtension config = (ImplicitConfigExtension) runFlow("implicitConfig", event).getMessage().getPayload(); + assertThat(config, is(notNullValue())); + assertThat(config.getMuleContext(), is(sameInstance(muleContext))); + assertThat(config.getInitialise(), is(1)); + assertThat(config.getStart(), is(1)); + assertThat(config.getOptionalNoDefault(), is(nullValue())); + assertThat(config.getOptionalWithDefault(), is(defaultValue)); + } + + @Extension(name = "implicit", version = "1.0") + @Operations({ImplicitOperations.class}) + @Xml(schemaLocation = "http://www.mulesoft.org/schema/mule/implicit", namespace = "implicit", schemaVersion = "1.0") + public static class ImplicitConfigExtension implements Initialisable, Startable, MuleContextAware + { + + private MuleContext muleContext; + private int initialise = 0; + private int start = 0; + + @Parameter + @Optional + private String optionalNoDefault; + + @Parameter + @Optional(defaultValue = "#[flowVars['optionalWithDefault']]") + private Integer optionalWithDefault; + + @Override + public void initialise() throws InitialisationException + { + initialise++; + } + + @Override + public void setMuleContext(MuleContext context) + { + muleContext = context; + } + + @Override + public void start() throws MuleException + { + start++; + } + + public MuleContext getMuleContext() + { + return muleContext; + } + + public int getInitialise() + { + return initialise; + } + + public int getStart() + { + return start; + } + + public String getOptionalNoDefault() + { + return optionalNoDefault; + } + + public Integer getOptionalWithDefault() + { + return optionalWithDefault; + } + } + + public static class ImplicitOperations + { + + private final ImplicitConfigExtension config; + + public ImplicitOperations(ImplicitConfigExtension config) + { + this.config = config; + } + + @Operation + public ImplicitConfigExtension getConfig() + { + return config; + } + } +} diff --git a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/OperationExecutionTestCase.java b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/OperationExecutionTestCase.java index 33a18890972..a11ede34e11 100644 --- a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/OperationExecutionTestCase.java +++ b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/OperationExecutionTestCase.java @@ -15,7 +15,7 @@ import org.mule.api.MuleException; import org.mule.extension.ExtensionManager; import org.mule.module.extension.HeisenbergExtension; -import org.mule.module.extension.internal.runtime.resolver.ValueResolver; +import org.mule.module.extension.internal.util.ExtensionsTestUtils; import org.mule.tck.junit4.ExtensionsFunctionalTestCase; import org.junit.Test; @@ -180,7 +180,6 @@ private void doTestExpressionEnemy(Object enemyIndex) throws Exception private HeisenbergExtension getConfig(String name) throws Exception { - ValueResolver config = muleContext.getRegistry().lookupObject(name); - return config.resolve(getTestEvent(EMPTY)); + return ExtensionsTestUtils.getConfigurationInstance(name, getTestEvent("")); } } diff --git a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/StatefulOperationTestCase.java b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/StatefulOperationTestCase.java index 8ad2b708847..4b08a10fb9d 100644 --- a/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/StatefulOperationTestCase.java +++ b/modules/extensions-spring-support/src/test/java/org/mule/module/extension/internal/StatefulOperationTestCase.java @@ -11,7 +11,7 @@ import static org.junit.Assert.assertThat; import org.mule.api.MuleEvent; import org.mule.module.extension.HeisenbergExtension; -import org.mule.module.extension.internal.runtime.resolver.ValueResolver; +import org.mule.module.extension.internal.util.ExtensionsTestUtils; import org.mule.tck.junit4.ExtensionsFunctionalTestCase; import java.math.BigDecimal; @@ -53,11 +53,10 @@ public void statefulOperation() throws Exception private void assertRemainingMoney(String name, long expectedAmount) throws Exception { - ValueResolver configResolver = muleContext.getRegistry().get("heisenberg"); MuleEvent event = getTestEvent(""); event.setFlowVariable("myName", name); - HeisenbergExtension heisenbergExtension = configResolver.resolve(event); + HeisenbergExtension heisenbergExtension = ExtensionsTestUtils.getConfigurationInstance("heisenberg", event); assertThat(heisenbergExtension.getMoney(), equalTo(BigDecimal.valueOf(expectedAmount))); } diff --git a/modules/extensions-spring-support/src/test/resources/heisenberg-default-config.xml b/modules/extensions-spring-support/src/test/resources/heisenberg-default-config.xml new file mode 100644 index 00000000000..e6756df96b2 --- /dev/null +++ b/modules/extensions-spring-support/src/test/resources/heisenberg-default-config.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/extensions-spring-support/src/test/resources/heisenberg-operation-config.xml b/modules/extensions-spring-support/src/test/resources/heisenberg-operation-config.xml index eb90b700023..660e7124378 100644 --- a/modules/extensions-spring-support/src/test/resources/heisenberg-operation-config.xml +++ b/modules/extensions-spring-support/src/test/resources/heisenberg-operation-config.xml @@ -35,74 +35,74 @@ - + - + - + - + - + - + - + - + - - + - - - + + + - + - + - + - + - + - + \ No newline at end of file diff --git a/modules/extensions-spring-support/src/test/resources/heisenberg.xsd b/modules/extensions-spring-support/src/test/resources/heisenberg.xsd index 5797eea8238..49d1797d1cb 100644 --- a/modules/extensions-spring-support/src/test/resources/heisenberg.xsd +++ b/modules/extensions-spring-support/src/test/resources/heisenberg.xsd @@ -127,7 +127,7 @@ - + Specify which configuration to use for this invocation. @@ -142,7 +142,7 @@ - + Specify which configuration to use for this invocation. @@ -154,7 +154,7 @@ - + Specify which configuration to use for this invocation. @@ -167,7 +167,7 @@ - + Specify which configuration to use for this invocation. @@ -179,7 +179,7 @@ - + Specify which configuration to use for this invocation. @@ -191,7 +191,7 @@ - + Specify which configuration to use for this invocation. @@ -203,7 +203,7 @@ - + Specify which configuration to use for this invocation. @@ -223,7 +223,7 @@ - + Specify which configuration to use for this invocation. @@ -243,7 +243,7 @@ - + Specify which configuration to use for this invocation. @@ -256,7 +256,7 @@ - + Specify which configuration to use for this invocation. @@ -270,7 +270,7 @@ - + Specify which configuration to use for this invocation. @@ -283,7 +283,7 @@ - + Specify which configuration to use for this invocation. diff --git a/modules/extensions-spring-support/src/test/resources/implicit-config.xml b/modules/extensions-spring-support/src/test/resources/implicit-config.xml new file mode 100644 index 00000000000..8b752383d65 --- /dev/null +++ b/modules/extensions-spring-support/src/test/resources/implicit-config.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/extensions-support/pom.xml b/modules/extensions-support/pom.xml index ace078649cf..5acf3db63b2 100644 --- a/modules/extensions-support/pom.xml +++ b/modules/extensions-support/pom.xml @@ -22,7 +22,7 @@ mule-extensions-api - org.mule.extensions + org.mule mule-extensions-annotations diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/AnnotationsBasedDescriber.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/AnnotationsBasedDescriber.java index 2ae1d3b1f3b..be0f05fe546 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/AnnotationsBasedDescriber.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/AnnotationsBasedDescriber.java @@ -7,7 +7,6 @@ package org.mule.module.extension.internal.introspection; import static org.apache.commons.lang.StringUtils.EMPTY; -import static org.mule.module.extension.internal.introspection.MuleExtensionAnnotationParser.getDefaultValue; import static org.mule.module.extension.internal.introspection.MuleExtensionAnnotationParser.getExtension; import static org.mule.module.extension.internal.introspection.MuleExtensionAnnotationParser.getMemberName; import static org.mule.module.extension.internal.introspection.MuleExtensionAnnotationParser.getParameterName; @@ -15,6 +14,7 @@ import static org.mule.module.extension.internal.util.IntrospectionUtils.getOperationMethods; import static org.mule.module.extension.internal.util.IntrospectionUtils.getParameterFields; import static org.mule.module.extension.internal.util.IntrospectionUtils.getParameterGroupFields; +import static org.mule.module.extension.internal.util.MuleExtensionUtils.getDefaultValue; import static org.mule.util.Preconditions.checkArgument; import org.mule.api.registry.SPIServiceRegistry; import org.mule.extension.annotations.Configuration; @@ -187,7 +187,7 @@ private Set declareSingleParameters(Class extensionType, } else { - parameterConstruct = with.optionalParameter(parameterName).defaultingTo(getDefaultValue(optional, dataType)); + parameterConstruct = with.optionalParameter(parameterName).defaultingTo(getDefaultValue(optional)); } parameterConstruct.ofType(dataType); diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/MuleExtensionAnnotationParser.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/MuleExtensionAnnotationParser.java index 892e5047fa5..cdfff3a7cd7 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/MuleExtensionAnnotationParser.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/introspection/MuleExtensionAnnotationParser.java @@ -8,6 +8,7 @@ import static org.mule.module.extension.internal.util.CapabilityUtils.getSingleCapability; import static org.mule.module.extension.internal.util.IntrospectionUtils.getFieldDataType; +import static org.mule.module.extension.internal.util.MuleExtensionUtils.getDefaultValue; import static org.mule.util.Preconditions.checkState; import org.mule.api.MuleEvent; import org.mule.api.MuleMessage; @@ -17,7 +18,6 @@ import org.mule.extension.annotations.param.Optional; import org.mule.extension.annotations.param.Payload; import org.mule.extension.introspection.Capable; -import org.mule.extension.introspection.DataQualifier; import org.mule.extension.introspection.DataType; import org.mule.extension.introspection.declaration.CapableDeclaration; import org.mule.module.extension.internal.capability.metadata.MemberNameCapability; @@ -158,7 +158,7 @@ private static ParameterDescriptor doParseParameter(String paramName, DataType p if (optional != null) { parameter.setRequired(false); - parameter.setDefaultValue(getDefaultValue(optional, dataType)); + parameter.setDefaultValue(getDefaultValue(optional)); } else { @@ -181,33 +181,6 @@ private static ParameterDescriptor doParseParameter(String paramName, DataType p return parameter; } - protected static Object getDefaultValue(Optional optional, DataType dataType) - { - if (optional == null) - { - return null; - } - - String defaultValue = optional.defaultValue(); - if (DataQualifier.STRING.equals(dataType.getQualifier())) - { - return defaultValue; - } - else - { - return StringUtils.isEmpty(defaultValue) ? null : defaultValue; - } - } - - private static void checkParametrizable(DataType type) - { - if (IMPLICIT_ARGUMENT_TYPES.contains(type.getRawType())) - { - throw new IllegalArgumentException( - String.format("Type %s is not allowed as an operation parameter. Use dependency injection instead", type.getRawType().getName())); - } - } - public static String[] getParamNames(Method method) { String[] paramNames; diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ConfigurationsStateTracker.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ConfigurationStateTracker.java similarity index 67% rename from modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ConfigurationsStateTracker.java rename to modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ConfigurationStateTracker.java index 28f767abf39..2d037b12271 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ConfigurationsStateTracker.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ConfigurationStateTracker.java @@ -10,13 +10,15 @@ import org.mule.extension.introspection.Configuration; import org.mule.extension.introspection.Extension; import org.mule.extension.introspection.Operation; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.OperationContext; import org.mule.extension.runtime.OperationExecutor; import org.mule.util.CollectionUtils; -import com.google.common.collect.ImmutableMultimap; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; -import com.google.common.collect.Multimaps; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.collections.Predicate; @@ -27,30 +29,40 @@ * * @since 3.7.0 */ -final class ConfigurationsStateTracker +final class ConfigurationStateTracker { - private final Multimap> configurationInstances = - Multimaps.synchronizedSetMultimap(LinkedHashMultimap.>create()); + private final Map> configurationInstances = new ConcurrentHashMap<>(); + private final Map configurationInstanceProviders = new ConcurrentHashMap<>(); + + void registerInstanceProvider(String providerName, ConfigurationInstanceProvider configurationInstanceProvider) + { + configurationInstanceProviders.put(providerName, configurationInstanceProvider); + } - void registerInstance(Configuration configuration, final String instanceName, final C configurationInstance) + void registerInstance(String instanceName, C configurationInstance) { - configurationInstances.put(configuration, new ConfigurationInstanceWrapper<>(instanceName, configurationInstance)); + configurationInstances.put(instanceName, new ConfigurationInstanceWrapper<>(instanceName, configurationInstance)); + } + + Map getConfigurationInstanceProviders() + { + return ImmutableMap.copyOf(configurationInstanceProviders); } /** * Returns an {@link OperationExecutor} that was previously registered * through {@link #registerOperationExecutor(Operation, Object, OperationExecutor)} * - * @param operation a {@link Operation} model * @param configurationInstance a previously registered configuration instance + * @param operationContext a {@link OperationContext} * @param the type of the configuration instance * @return a {@link OperationExecutor} */ - OperationExecutor getOperationExecutor(Operation operation, C configurationInstance) + OperationExecutor getOperationExecutor(C configurationInstance, OperationContext operationContext) { ConfigurationInstanceWrapper wrapper = locateConfigurationInstanceWrapper(configurationInstance); - return wrapper.getOperationExecutor(operation); + return wrapper.getOperationExecutor(operationContext.getOperation()); } /** @@ -68,11 +80,6 @@ void registerOperationExecutor(Operation operation, C configurationInstance, wrapper.registerOperationExecutor(operation, executor); } - Multimap> getConfigurationInstances() - { - return ImmutableMultimap.copyOf(configurationInstances); - } - private ConfigurationInstanceWrapper locateConfigurationInstanceWrapper(final C configurationInstance) { ConfigurationInstanceWrapper wrapper = (ConfigurationInstanceWrapper) CollectionUtils.find(configurationInstances.values(), new Predicate() diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/DefaultExtensionManager.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/DefaultExtensionManager.java index d0a92e6b6a8..6b483e73686 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/DefaultExtensionManager.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/DefaultExtensionManager.java @@ -6,6 +6,7 @@ */ package org.mule.module.extension.internal.manager; +import static org.mule.module.extension.internal.util.MuleExtensionUtils.asOperationContextAdapter; import static org.mule.util.Preconditions.checkArgument; import org.mule.api.MuleContext; import org.mule.api.MuleException; @@ -18,30 +19,40 @@ import org.mule.api.registry.ServiceRegistry; import org.mule.common.MuleVersion; import org.mule.config.i18n.MessageFactory; -import org.mule.extension.ExtensionManager; import org.mule.extension.introspection.Configuration; import org.mule.extension.introspection.Extension; import org.mule.extension.introspection.Operation; +import org.mule.extension.introspection.Parameter; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.ConfigurationInstanceRegistrationCallback; +import org.mule.extension.runtime.OperationContext; import org.mule.extension.runtime.OperationExecutor; import org.mule.module.extension.internal.introspection.DefaultExtensionFactory; import org.mule.module.extension.internal.introspection.ExtensionDiscoverer; +import org.mule.module.extension.internal.runtime.ConfigurationObjectBuilder; import org.mule.module.extension.internal.runtime.DelegatingOperationExecutor; +import org.mule.module.extension.internal.runtime.StaticConfigurationInstanceProvider; +import org.mule.module.extension.internal.runtime.resolver.EvaluateAndTransformValueResolver; +import org.mule.module.extension.internal.runtime.resolver.ResolverSet; +import org.mule.module.extension.internal.runtime.resolver.StaticValueResolver; +import org.mule.module.extension.internal.runtime.resolver.ValueResolver; import org.mule.util.ObjectNameHelper; import com.google.common.collect.ImmutableList; import java.util.List; +import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Default implementation of {@link ExtensionManager} + * Default implementation of {@link ExtensionManagerAdapter} * * @since 3.7.0 */ -public final class DefaultExtensionManager implements ExtensionManager, MuleContextAware, Initialisable +public final class DefaultExtensionManager implements ExtensionManagerAdapter, MuleContextAware, Initialisable { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExtensionManager.class); @@ -49,24 +60,23 @@ public final class DefaultExtensionManager implements ExtensionManager, MuleCont private final ExtensionRegister register = new ExtensionRegister(); private final ServiceRegistry serviceRegistry = new SPIServiceRegistry(); - private boolean initialised = false; private MuleContext muleContext; private ObjectNameHelper objectNameHelper; private ExtensionDiscoverer extensionDiscoverer = new DefaultExtensionDiscoverer(new DefaultExtensionFactory(serviceRegistry), serviceRegistry); + /** + * Searches the mule registry for instances of {@link ConfigurationInstanceProvider} + * and registers them through the {@link #registerConfigurationInstanceProvider(String, ConfigurationInstanceProvider)} + * method + */ @Override public void initialise() throws InitialisationException { - for (Extension extension : getExtensions()) + for (Map.Entry instanceProviderEntry : muleContext.getRegistry().lookupByType(ConfigurationInstanceProvider.class).entrySet()) { - ExtensionStateTracker extensionState = register.getExtensionState(extension); - for (ConfigurationInstanceWrapper instanceWrapper : extensionState.getConfigurationInstances().values()) - { - putInRegistryAndApplyLifecycle(instanceWrapper.getName(), instanceWrapper.getConfigurationInstance()); - } + ConfigurationInstanceProvider instanceProvider = instanceProviderEntry.getValue(); + registerConfigurationInstanceProvider(instanceProviderEntry.getKey(), instanceProvider); } - - initialised = true; } @Override @@ -114,26 +124,56 @@ public boolean registerExtension(Extension extension) * {@inheritDoc} */ @Override - public void registerConfigurationInstance(Configuration configuration, String configurationInstanceName, C configurationInstance) + public void registerConfigurationInstanceProvider(String providerName, ConfigurationInstanceProvider configurationInstanceProvider) { + Configuration configuration = configurationInstanceProvider.getConfiguration(); ExtensionStateTracker extensionStateTracker = register.getExtensionState(configuration); - extensionStateTracker.registerConfigurationInstance(configurationInstanceName, configuration, configurationInstance); + extensionStateTracker.registerConfigurationInstanceProvider(configuration, providerName, configurationInstanceProvider); + } - if (initialised) + /** + * {@inheritDoc} + */ + @Override + public C getConfigurationInstance(final ConfigurationInstanceProvider configurationInstanceProvider, OperationContext operationContext) + { + return configurationInstanceProvider.get(operationContext, new ConfigurationInstanceRegistrationCallback() { - putInRegistryAndApplyLifecycle(configurationInstanceName, configurationInstance); - } + @Override + public void registerNewConfigurationInstance(ConfigurationInstanceProvider configurationInstanceProvider, C configurationInstance) + { + registerConfigurationInstance(configurationInstanceProvider.getConfiguration(), + configurationInstanceProvider.getName(), + configurationInstance); + } + }); } - private void putInRegistryAndApplyLifecycle(String configurationInstanceName, C configurationInstance) + /** + * {@inheritDoc} + */ + @Override + public OperationExecutor getOperationExecutor(OperationContext operationContext) { - try + Extension extension = register.getExtension(operationContext.getOperation()); + Map providers = register.getConfigurationInstanceProviders(extension); + + int matches = providers.size(); + + if (matches == 1) { - muleContext.getRegistry().registerObject(objectNameHelper.getUniqueName(configurationInstanceName), configurationInstance); + ConfigurationInstanceProvider provider = providers.values().iterator().next(); + return getOperationExecutor(provider, operationContext); } - catch (MuleException e) + else if (matches > 1) { - throw new MuleRuntimeException(e); + throw new IllegalStateException(String.format("No config-ref was specified for operation %s of extension %s, but %d are registered. Please specify which to use", + operationContext.getOperation().getName(), extension.getName(), matches)); + } + else + { + attemptToCreateImplicitConfigurationInstance(extension, operationContext); + return getOperationExecutor(operationContext); } } @@ -141,21 +181,10 @@ private void putInRegistryAndApplyLifecycle(String configurationInstanceName * {@inheritDoc} */ @Override - public OperationExecutor getOperationExecutor(Operation operation, C configurationInstance) + public OperationExecutor getOperationExecutor(final String configurationInstanceProviderName, OperationContext operationContext) { - ExtensionStateTracker extensionStateTracker = register.getExtensionState(operation); - OperationExecutor executor; - - synchronized (configurationInstance) - { - executor = extensionStateTracker.getOperationExecutor(operation, configurationInstance); - if (executor == null) - { - executor = createOperationExecutor(operation, configurationInstance); - extensionStateTracker.registerOperationExecutor(operation, configurationInstance, executor); - } - } - return executor; + ConfigurationInstanceProvider configurationInstanceProvider = getConfigurationInstanceProvider(configurationInstanceProviderName); + return getOperationExecutor(configurationInstanceProvider, operationContext); } /** @@ -177,6 +206,137 @@ public Set getExtensionsCapableOf(Class capabilityType) return register.getExtensionsCapableOf(capabilityType); } + private void registerConfigurationInstance(Configuration configuration, String configurationInstanceName, C configurationInstance) + { + ExtensionStateTracker extensionStateTracker = register.getExtensionState(configuration); + extensionStateTracker.registerConfigurationInstance(configuration, configurationInstanceName, configurationInstance); + + putInRegistryAndApplyLifecycle(configurationInstanceName, configurationInstance); + } + + private void putInRegistryAndApplyLifecycle(String key, Object object) + { + try + { + muleContext.getRegistry().registerObject(objectNameHelper.getUniqueName(key), object); + } + catch (MuleException e) + { + throw new MuleRuntimeException(e); + } + } + + private OperationExecutor getOperationExecutor(ConfigurationInstanceProvider configurationInstanceProvider, OperationContext operationContext) + { + Object configurationInstance = getConfigurationInstance(configurationInstanceProvider, operationContext); + OperationExecutor executor; + + synchronized (configurationInstance) + { + ExtensionStateTracker extensionStateTracker = register.getExtensionState(configurationInstanceProvider.getConfiguration()); + executor = extensionStateTracker.getOperationExecutor(configurationInstanceProvider.getConfiguration(), configurationInstance, operationContext); + if (executor == null) + { + executor = createOperationExecutor(configurationInstance, operationContext); + extensionStateTracker.registerOperationExecutor(configurationInstanceProvider.getConfiguration(), + operationContext.getOperation(), + configurationInstance, + executor); + } + } + + return executor; + } + + private ConfigurationInstanceProvider getConfigurationInstanceProvider(String configurationInstanceProviderName) + { + ConfigurationInstanceProvider configurationInstanceProvider = register.getConfigurationInstanceProviders().get(configurationInstanceProviderName); + if (configurationInstanceProvider == null) + { + throw new IllegalArgumentException("There's no registered ConfigurationInstanceProvider under name" + configurationInstanceProviderName); + } + return configurationInstanceProvider; + } + + private Object attemptToCreateImplicitConfigurationInstance(Extension extension, OperationContext operationContext) + { + Configuration implicitConfiguration = getImplicitConfiguration(extension); + + if (implicitConfiguration == null) + { + throw new IllegalStateException(String.format("Could not find a config for extension %s and none can be created automatically. Please define one", extension.getName())); + } + + ConfigurationObjectBuilder configurationObjectBuilder = new ConfigurationObjectBuilder(implicitConfiguration, buildImplicitConfigurationResolverSet(implicitConfiguration)); + + Object configurationInstance; + try + { + configurationInstance = configurationObjectBuilder.build(asOperationContextAdapter(operationContext).getEvent()); + } + catch (MuleException e) + { + throw new MuleRuntimeException(e); + } + + final String instanceName = objectNameHelper.getUniqueName(String.format("%s-%s", extension.getName(), implicitConfiguration.getName())); + registerConfigurationInstanceProvider(instanceName, + new StaticConfigurationInstanceProvider<>(instanceName, implicitConfiguration, configurationInstance)); + + return configurationInstance; + } + + private ResolverSet buildImplicitConfigurationResolverSet(Configuration configuration) + { + ResolverSet resolverSet = new ResolverSet(); + for (Parameter parameter : configuration.getParameters()) + { + Object defaultValue = parameter.getDefaultValue(); + if (defaultValue != null) + { + ValueResolver valueResolver; + if (defaultValue instanceof String && muleContext.getExpressionManager().isExpression((String) defaultValue)) + { + valueResolver = new EvaluateAndTransformValueResolver<>((String) defaultValue, parameter.getType()); + } + else + { + valueResolver = new StaticValueResolver<>(defaultValue); + } + + resolverSet.add(parameter, valueResolver); + } + } + + return resolverSet; + } + + private Configuration getImplicitConfiguration(Extension extension) + { + for (Configuration configuration : extension.getConfigurations()) + { + if (canBeUsedImplicitly(configuration)) + { + return configuration; + } + } + + return null; + } + + private boolean canBeUsedImplicitly(Configuration configuration) + { + for (Parameter parameter : configuration.getParameters()) + { + if (parameter.isRequired() && parameter.getDefaultValue() == null) + { + return false; + } + } + + return true; + } + private boolean maybeUpdateExtension(Extension extension, String extensionName) { Extension actual = register.getExtension(extensionName); @@ -236,8 +396,9 @@ public void setMuleContext(MuleContext muleContext) objectNameHelper = new ObjectNameHelper(muleContext); } - private OperationExecutor createOperationExecutor(Operation operation, C configurationInstance) + private OperationExecutor createOperationExecutor(C configurationInstance, OperationContext operationContext) { + Operation operation = operationContext.getOperation(); OperationExecutor executor; executor = operation.getExecutor(configurationInstance); if (executor instanceof DelegatingOperationExecutor) diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionManagerAdapter.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionManagerAdapter.java new file mode 100644 index 00000000000..3ba3801533a --- /dev/null +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionManagerAdapter.java @@ -0,0 +1,39 @@ +/* + * 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.module.extension.internal.manager; + +import org.mule.extension.ExtensionManager; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.OperationContext; + +/** + * An adapter interface which expands the contact + * of {@link ExtensionManager} with functionality + * that is internal to this implementation + * of the extensions API and that extensions + * themselves shouldn't be able to access + * + * @since 3.7.0 + */ +public interface ExtensionManagerAdapter extends ExtensionManager +{ + + /** + * Returns a configuration instance obtained through the given + * {@code configurationInstanceProvider} and for the + * provided {@code operationContext}. This method will fail + * if {@code configurationInstanceProvider} hasn't previously been + * registered through the {@link #registerConfigurationInstanceProvider(String, ConfigurationInstanceProvider)} + * method + * + * @param configurationInstanceProvider a registered {@link ConfigurationInstanceProvider} + * @param operationContext a {@link OperationContext} + * @param the type of the configuration instance to be returned + * @return a configuration instance + */ + C getConfigurationInstance(ConfigurationInstanceProvider configurationInstanceProvider, OperationContext operationContext); +} diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionRegister.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionRegister.java index be478633a90..8730bf9f5ba 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionRegister.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionRegister.java @@ -10,11 +10,13 @@ import org.mule.extension.introspection.Configuration; import org.mule.extension.introspection.Extension; import org.mule.extension.introspection.Operation; +import org.mule.extension.runtime.ConfigurationInstanceProvider; import org.mule.util.CollectionUtils; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.util.Map; @@ -157,6 +159,28 @@ ExtensionStateTracker getExtensionState(Configuration configuration) return getExtensionState(getExtension(configuration)); } + Map getConfigurationInstanceProviders() + { + ImmutableMap.Builder providers = ImmutableMap.builder(); + for (Extension extension : extensions.values()) + { + providers.putAll(getConfigurationInstanceProviders(extension)); + } + + return providers.build(); + } + + Map getConfigurationInstanceProviders(Extension extension) + { + ImmutableMap.Builder providers = ImmutableMap.builder(); + for (Configuration configuration : extension.getConfigurations()) + { + providers.putAll(getExtensionState(configuration).getConfigurationInstanceProviders(configuration)); + } + + return providers.build(); + } + /** * @param operation a {@link Operation} owned by a registered {@link Extension} * @return the {@link ExtensionStateTracker} object related to the given {@code operation} diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionStateTracker.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionStateTracker.java index 50e20295ebb..ae7eb7539bb 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionStateTracker.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/manager/ExtensionStateTracker.java @@ -9,9 +9,16 @@ import org.mule.extension.introspection.Configuration; import org.mule.extension.introspection.Extension; import org.mule.extension.introspection.Operation; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.OperationContext; import org.mule.extension.runtime.OperationExecutor; -import com.google.common.collect.Multimap; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; + +import java.util.Map; /** * Holds state regarding the use that the platform is doing of a @@ -22,52 +29,74 @@ final class ExtensionStateTracker { - private final ConfigurationsStateTracker configurationsState = new ConfigurationsStateTracker(); + private final LoadingCache configurationsStates = CacheBuilder.newBuilder() + .build(new CacheLoader() + { + @Override + public ConfigurationStateTracker load(Configuration configuration) throws Exception + { + return new ConfigurationStateTracker(); + } + }); + + void registerConfigurationInstanceProvider(Configuration configuration, + String providerName, + ConfigurationInstanceProvider configurationInstanceProvider) + { + getStateTracker(configuration).registerInstanceProvider(providerName, configurationInstanceProvider); + } + + Map getConfigurationInstanceProviders(Configuration configuration) + { + return ImmutableMap.copyOf(getStateTracker(configuration).getConfigurationInstanceProviders()); + } /** * Registers a {@code configurationInstance} which is a realization of a {@link Configuration} * model defined by {@code configuration} * - * @param instanceName the name of the instance * @param configuration a {@link Configuration} + * @param instanceName the name of the instance * @param configurationInstance an instance which is compliant with the {@code configuration} model * @param the type of the configuration instance */ - void registerConfigurationInstance(String instanceName, Configuration configuration, C configurationInstance) - { - configurationsState.registerInstance(configuration, instanceName, configurationInstance); - } - - Multimap> getConfigurationInstances() + void registerConfigurationInstance(Configuration configuration, String instanceName, C configurationInstance) { - return configurationsState.getConfigurationInstances(); + getStateTracker(configuration).registerInstance(instanceName, configurationInstance); } /** * Returns an {@link OperationExecutor} that was previously registered - * through {@link #registerOperationExecutor(Operation, Object, OperationExecutor)} + * through {@link #registerOperationExecutor(Configuration, Operation, Object, OperationExecutor)} * - * @param operation a {@link Operation} model + * @param configuration a {@link Configuration} * @param configurationInstance a previously registered configuration instance + * @param operationContext a {@link OperationContext} * @param the type of the configuration instance * @return a {@link OperationExecutor} */ - OperationExecutor getOperationExecutor(Operation operation, C configurationInstance) + OperationExecutor getOperationExecutor(Configuration configuration, C configurationInstance, OperationContext operationContext) { - return configurationsState.getOperationExecutor(operation, configurationInstance); + return getStateTracker(configuration).getOperationExecutor(configurationInstance, operationContext); } /** * Registers a {@link OperationExecutor} for the {@code operation}|{@code configurationInstance} * pair. * + * @param configuration a {@link Configuration} * @param operation a {@link Operation} model * @param configurationInstance a previously registered configuration instance * @param executor a {@link OperationExecutor} * @param the type of the configuration instance */ - void registerOperationExecutor(Operation operation, C configurationInstance, OperationExecutor executor) + void registerOperationExecutor(Configuration configuration, Operation operation, C configurationInstance, OperationExecutor executor) + { + getStateTracker(configuration).registerOperationExecutor(operation, configurationInstance, executor); + } + + private ConfigurationStateTracker getStateTracker(Configuration configuration) { - configurationsState.registerOperationExecutor(operation, configurationInstance, executor); + return configurationsStates.getUnchecked(configuration); } } diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/DefaultOperationContext.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/DefaultOperationContext.java index ad8fa495127..c96440ee598 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/DefaultOperationContext.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/DefaultOperationContext.java @@ -7,41 +7,45 @@ package org.mule.module.extension.internal.runtime; import org.mule.api.MuleEvent; +import org.mule.extension.introspection.Operation; import org.mule.extension.introspection.Parameter; -import org.mule.extension.runtime.OperationContext; import org.mule.module.extension.internal.runtime.resolver.ResolverSetResult; import java.util.HashMap; import java.util.Map; /** - * Default implementation of {@link OperationContext} which + * Default implementation of {@link OperationContextAdapter} which * adds additional information which is relevant to this implementation * of the extensions-api, even though it's not part of the API itself * * @since 3.7.0 */ -public final class DefaultOperationContext implements OperationContext +public class DefaultOperationContext implements OperationContextAdapter { + private final Operation operation; private final Map parameters; private final Map parametersByName; - - /** - * The current {@link MuleEvent} - */ private final MuleEvent event; - public DefaultOperationContext(ResolverSetResult parameters, MuleEvent event) + public DefaultOperationContext(Operation operation, ResolverSetResult parameters, MuleEvent event) { + this.operation = operation; + this.event = event; + this.parameters = parameters.asMap(); parametersByName = new HashMap<>(this.parameters.size()); for (Map.Entry parameter : this.parameters.entrySet()) { parametersByName.put(parameter.getKey().getName(), parameter.getValue()); } + } - this.event = event; + @Override + public Operation getOperation() + { + return operation; } @Override @@ -62,6 +66,7 @@ public Object getParameterValue(String parameterName) return parametersByName.get(parameterName); } + @Override public MuleEvent getEvent() { return event; diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/DynamicConfigurationInstanceProvider.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/DynamicConfigurationInstanceProvider.java new file mode 100644 index 00000000000..6064c02e500 --- /dev/null +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/DynamicConfigurationInstanceProvider.java @@ -0,0 +1,151 @@ +/* + * 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.module.extension.internal.runtime; + +import static org.mule.module.extension.internal.util.MuleExtensionUtils.asOperationContextAdapter; +import org.mule.api.MuleEvent; +import org.mule.api.MuleRuntimeException; +import org.mule.extension.introspection.Configuration; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.ConfigurationInstanceRegistrationCallback; +import org.mule.extension.runtime.OperationContext; +import org.mule.module.extension.internal.runtime.resolver.ResolverSet; +import org.mule.module.extension.internal.runtime.resolver.ResolverSetResult; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +/** + * A {@link ConfigurationInstanceProvider} which continuously evaluates the same + * {@link ResolverSet} and then uses the resulting {@link ResolverSetResult} + * to build an instance of a given type. + *

+ * Although each invocation to {@link #get(OperationContext, ConfigurationInstanceRegistrationCallback)} + * is guaranteed to end up in an invocation to {@link #resolverSet#resolve(MuleEvent)}, the resulting + * {@link ResolverSetResult} might not end up generating a new instance. This is so because + * {@link ResolverSetResult} instances are put in a cache to + * guarantee that equivalent evaluations of the {@code resolverSet} return the same + * instance. + * + * @since 3.7.0 + */ +public final class DynamicConfigurationInstanceProvider implements ConfigurationInstanceProvider +{ + + private final String name; + private final Configuration configuration; + private final ConfigurationObjectBuilder configurationObjectBuilder; + private final ResolverSet resolverSet; + + private LoadingCache cache; + + /** + * Creates a new instance + * + * @param name the name of the config definition + * @param configuration the {@link Configuration} model + * @param configurationObjectBuilder the introspection model of the objects this resolver produces + * @param resolverSet the {@link ResolverSet} that's going to be evaluated + */ + public DynamicConfigurationInstanceProvider(String name, + Configuration configuration, + ConfigurationObjectBuilder configurationObjectBuilder, + ResolverSet resolverSet) + { + this.name = name; + this.configuration = configuration; + this.configurationObjectBuilder = configurationObjectBuilder; + this.resolverSet = resolverSet; + buildCache(); + } + + private void buildCache() + { + cache = CacheBuilder.newBuilder().build(new CacheLoader() + { + @Override + public T load(CacheKey key) throws Exception + { + T configurationInstance = (T) configurationObjectBuilder.build(key.resolverSetResult); + key.registrationCallback.registerNewConfigurationInstance(DynamicConfigurationInstanceProvider.this, + configurationInstance); + + return configurationInstance; + } + }); + } + + /** + * Evaluates {@link #resolverSet} using the given {@code event} and returns + * an instance produced with the result. For equivalent {@link ResolverSetResult}s + * it will return the same instance, for as long as the {@code expirationInterval} and + * {@code expirationTimeUnit} were specified in the constructor + * + * @param operationContext a {@link OperationContext} + * @param registrationCallback a {@link ConfigurationInstanceRegistrationCallback} to be invoked when a new instance is produced + * @return the resolved value + */ + @Override + public T get(OperationContext operationContext, ConfigurationInstanceRegistrationCallback registrationCallback) + { + try + { + ResolverSetResult result = resolverSet.resolve(asOperationContextAdapter(operationContext).getEvent()); + return cache.getUnchecked(new CacheKey(result, registrationCallback)); + } + catch (Exception e) + { + throw new MuleRuntimeException(e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Configuration getConfiguration() + { + return configuration; + } + + @Override + public String getName() + { + return name; + } + + private static class CacheKey + { + + private final ResolverSetResult resolverSetResult; + private final ConfigurationInstanceRegistrationCallback registrationCallback; + + private CacheKey(ResolverSetResult resolverSetResult, ConfigurationInstanceRegistrationCallback registrationCallback) + { + this.resolverSetResult = resolverSetResult; + this.registrationCallback = registrationCallback; + } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof CacheKey) + { + return resolverSetResult.equals(((CacheKey) obj).resolverSetResult); + } + + return false; + } + + @Override + public int hashCode() + { + return resolverSetResult.hashCode(); + } + } +} diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/OperationContextAdapter.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/OperationContextAdapter.java new file mode 100644 index 00000000000..9f2b14eda83 --- /dev/null +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/OperationContextAdapter.java @@ -0,0 +1,29 @@ +/* + * 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.module.extension.internal.runtime; + +import org.mule.api.MuleEvent; +import org.mule.extension.runtime.OperationContext; + +/** + * Adapter interface which expands the contract of + * {@link OperationContext} which functionality that is + * internal to this implementation of the extensions + * api and shouldn't be accessible for the extensions + * themselves + * + * @since 3.7.0 + */ +public interface OperationContextAdapter extends OperationContext +{ + + /** + * Returns the {@link MuleEvent} on which + * an operation is to be executed + */ + MuleEvent getEvent(); +} diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/StaticConfigurationInstanceProvider.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/StaticConfigurationInstanceProvider.java new file mode 100644 index 00000000000..81f7adb1d64 --- /dev/null +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/StaticConfigurationInstanceProvider.java @@ -0,0 +1,96 @@ +/* + * 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.module.extension.internal.runtime; + +import org.mule.api.MuleException; +import org.mule.api.MuleRuntimeException; +import org.mule.extension.introspection.Configuration; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.ConfigurationInstanceRegistrationCallback; +import org.mule.extension.runtime.OperationContext; + +public final class StaticConfigurationInstanceProvider implements ConfigurationInstanceProvider +{ + + private final String name; + private final Configuration configuration; + private final T configurationInstance; + private ProviderDelegate providerDelegate; + + public StaticConfigurationInstanceProvider(String name, Configuration configuration, T configurationInstance) + { + this.name = name; + this.configuration = configuration; + this.configurationInstance = configurationInstance; + providerDelegate = new FirstTimeProviderDelegate<>(); + } + + @Override + public T get(OperationContext operationContext, ConfigurationInstanceRegistrationCallback registrationCallback) + { + try + { + return providerDelegate.provide(operationContext, registrationCallback); + } + catch (MuleException e) + { + throw new MuleRuntimeException(e); + } + } + + @Override + public Configuration getConfiguration() + { + return configuration; + } + + @Override + public String getName() + { + return name; + } + + private interface ProviderDelegate + { + + T provide(OperationContext operationContext, ConfigurationInstanceRegistrationCallback registrationCallback) throws MuleException; + } + + private class FirstTimeProviderDelegate implements ProviderDelegate + { + + @Override + public synchronized T provide(OperationContext operationContext, + ConfigurationInstanceRegistrationCallback registrationCallback) throws MuleException + { + if (providerDelegate == this) + { + registrationCallback.registerNewConfigurationInstance(StaticConfigurationInstanceProvider.this, configurationInstance); + providerDelegate = new FixedProviderDelegate<>(configurationInstance); + } + + return (T) configurationInstance; + } + } + + private class FixedProviderDelegate implements ProviderDelegate + { + + private T configurationInstance; + + private FixedProviderDelegate(T configurationInstance) + { + this.configurationInstance = configurationInstance; + } + + @Override + public T provide(OperationContext operationContext, ConfigurationInstanceRegistrationCallback registrationCallback) throws MuleException + { + return configurationInstance; + } + } +} diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/VoidReturnDelegate.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/VoidReturnDelegate.java index cd685f01a2e..5e861b583db 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/VoidReturnDelegate.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/VoidReturnDelegate.java @@ -12,9 +12,9 @@ /** * An implementation of {@link ReturnDelegate} intended * for operations which return {@link Void} and that - * were executed with a {@link DefaultOperationContext} + * were executed with a {@link OperationContextAdapter} *

- * It returns the {@link MuleEvent} that {@link DefaultOperationContext} + * It returns the {@link MuleEvent} that {@link OperationContextAdapter} * provides. Notices that this class will fail if used with any other type * of {@link OperationContext} *

@@ -34,11 +34,11 @@ private VoidReturnDelegate() /** * {@inheritDoc} - * @return {@link DefaultOperationContext#getEvent()} + * @return {@link OperationContextAdapter#getEvent()} */ @Override public Object asReturnValue(Object value, OperationContext operationContext) { - return ((DefaultOperationContext) operationContext).getEvent(); + return ((OperationContextAdapter) operationContext).getEvent(); } } diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/processor/OperationMessageProcessor.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/processor/OperationMessageProcessor.java index b1ef2992a40..4805631d9f6 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/processor/OperationMessageProcessor.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/processor/OperationMessageProcessor.java @@ -18,12 +18,14 @@ import org.mule.extension.runtime.OperationContext; import org.mule.extension.runtime.OperationExecutor; import org.mule.module.extension.internal.runtime.DefaultOperationContext; +import org.mule.module.extension.internal.runtime.OperationContextAdapter; import org.mule.module.extension.internal.runtime.resolver.ResolverSet; import org.mule.module.extension.internal.runtime.resolver.ResolverSetResult; -import org.mule.module.extension.internal.runtime.resolver.ValueResolver; import java.util.concurrent.Future; +import org.apache.commons.lang.StringUtils; + /** * A {@link MessageProcessor} capable of executing extension operations. * It obtains a configuration, evaluate all the operation parameters @@ -35,26 +37,32 @@ public final class OperationMessageProcessor implements MessageProcessor, MuleContextAware { - private final ValueResolver configuration; + private final String configurationInstanceProviderName; private final Operation operation; private final ResolverSet resolverSet; private MuleContext muleContext; - public OperationMessageProcessor(ValueResolver configuration, Operation operation, ResolverSet resolverSet) + public OperationMessageProcessor(Operation operation, + String configurationInstanceProviderName, + ResolverSet resolverSet) { - this.configuration = configuration; this.operation = operation; + this.configurationInstanceProviderName = configurationInstanceProviderName; this.resolverSet = resolverSet; } + private OperationContext createOperationContext(MuleEvent event) throws MuleException { + ResolverSetResult parameters = resolverSet.resolve(event); + return new DefaultOperationContext(operation, parameters, event); + } + @Override public MuleEvent process(MuleEvent event) throws MuleException { - Object configInstance = configuration.resolve(event); - ResolverSetResult parameters = resolverSet.resolve(event); + OperationContext operationContext = createOperationContext(event); - Future future = executeOperation(event, configInstance, parameters); + Future future = executeOperation(operationContext); Object result = extractResult(event, future); if (result instanceof MuleEvent) @@ -87,18 +95,21 @@ private Object extractResult(MuleEvent event, Future future) throws Mule } } - private Future executeOperation(MuleEvent event, Object configInstance, ResolverSetResult parameters) throws MuleException + private Future executeOperation(OperationContext operationContext) throws MuleException { - OperationExecutor executor = muleContext.getExtensionManager().getOperationExecutor(operation, configInstance); - OperationContext context = new DefaultOperationContext(parameters, event); + OperationExecutor executor = StringUtils.isBlank(configurationInstanceProviderName) + ? muleContext.getExtensionManager().getOperationExecutor(operationContext) + : muleContext.getExtensionManager().getOperationExecutor(configurationInstanceProviderName, operationContext); try { - return executor.execute(context); + return executor.execute(operationContext); } catch (Exception e) { - throw handledException(String.format("Operation %s threw exception", operation.getName()), event, e); + throw handledException(String.format("Operation %s threw exception", operation.getName()), + ((OperationContextAdapter) operationContext).getEvent(), + e); } } diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/ConfigurationValueResolver.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/ConfigurationValueResolver.java deleted file mode 100644 index a494eadde19..00000000000 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/ConfigurationValueResolver.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.module.extension.internal.runtime.resolver; - -import static org.mule.MessageExchangePattern.REQUEST_RESPONSE; -import org.mule.DefaultMuleEvent; -import org.mule.DefaultMuleMessage; -import org.mule.api.MuleContext; -import org.mule.api.MuleEvent; -import org.mule.api.MuleException; -import org.mule.api.MuleRuntimeException; -import org.mule.api.NamedObject; -import org.mule.api.construct.FlowConstruct; -import org.mule.extension.ExtensionManager; -import org.mule.extension.introspection.Configuration; -import org.mule.module.extension.internal.runtime.ConfigurationObjectBuilder; - -/** - * A {@link ValueResolver} for returning instances that implement - * a {@link Configuration}. Those instances are created through the - * {@link Configuration#getInstantiator()} component. - * It supports both static and dynamic configurations (understanding by static - * that non of its parameters have expressions, and dynamic that at least one of them does). - *

- * In the case of static configurations, it will always return the same instance, in the case of - * dynamic, it will evaluate those expressions and only return the same instance for equivalent - * instances of {@link ResolverSetResult}. Those instances will be cached and discarded - * after one minute of inactivity. - *

- * A {@link ResolverSet} is used for evaluating the attributes and creating new instances. - * It also implements {@link NamedObject} since configurations are named and unique from a user's - * point of view. Notice however that the named object is this resolver and in the case of - * dynamic configurations instances are not likely to be unique - *

- * The generated instance will be registered with the {@code extensionManager} - * through {@link ExtensionManager#registerConfigurationInstance(Configuration, String, Object)} - * - * @since 3.7.0 - */ -public final class ConfigurationValueResolver implements ValueResolver, NamedObject -{ - - private final String name; - private final ValueResolver delegate; - - public ConfigurationValueResolver(String name, - Configuration configuration, - ResolverSet resolverSet, - MuleContext muleContext) - { - this.name = name; - - ConfigurationObjectBuilder configurationObjectBuilder = new ConfigurationObjectBuilder(configuration, resolverSet); - - if (resolverSet.isDynamic()) - { - delegate = new DynamicConfigurationValueResolver(name, configuration, configurationObjectBuilder, resolverSet, muleContext); - } - else - { - try - { - Object config = configurationObjectBuilder.build(getInitialiserEvent(muleContext)); - muleContext.getExtensionManager().registerConfigurationInstance(configuration, name, config); - delegate = new StaticValueResolver<>(config); - } - catch (MuleException e) - { - throw new MuleRuntimeException(e); - } - } - } - - /** - * Returns an instance associated with the given {@code event} - * - * @param event a {@link MuleEvent} - * @return a configuration instance - * @throws {@link MuleException} - */ - @Override - public Object resolve(MuleEvent event) throws MuleException - { - return delegate.resolve(event); - } - - /** - * Whether the generated configurations are dynamic or not - */ - @Override - public boolean isDynamic() - { - return delegate.isDynamic(); - } - - - private MuleEvent getInitialiserEvent(MuleContext muleContext) - { - return new DefaultMuleEvent(new DefaultMuleMessage(null, muleContext), REQUEST_RESPONSE, (FlowConstruct) null); - } - - @Override - public String getName() - { - return name; - } -} \ No newline at end of file diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationValueResolver.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationValueResolver.java deleted file mode 100644 index 57fd74bda6c..00000000000 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationValueResolver.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.module.extension.internal.runtime.resolver; - -import org.mule.api.MuleContext; -import org.mule.api.MuleEvent; -import org.mule.api.MuleException; -import org.mule.extension.ExtensionManager; -import org.mule.extension.introspection.Configuration; -import org.mule.module.extension.internal.runtime.ConfigurationObjectBuilder; - -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.CacheLoader; -import com.google.common.cache.LoadingCache; - -/** - * A {@link ValueResolver} which continuously evaluates the same - * {@link ResolverSet} and then uses the resulting {@link ResolverSetResult} - * to build an instance of a given type. - *

- * Although each invocation to {@link #resolve(MuleEvent)} is guaranteed to end up - * in an invocation to {@link #resolverSet#resolve(MuleEvent)}, the resulting - * {@link ResolverSetResult} might not end up generating a new instance. This is so because - * {@link ResolverSetResult} instances are put in a cache to - * guarantee that equivalent evaluations of the {@code resolverSet} return the same - * instance. That cache will automatically expire entries that are not used for - * an interval configured using {@code expirationInterval} - * and {@code expirationTimeUnit}. - *

- * The generated instances will be registered with the {@code extensionManager} - * through {@link ExtensionManager#registerConfigurationInstance(Configuration, String, Object)} - * - * @since 3.7.0 - */ -public class DynamicConfigurationValueResolver implements ValueResolver -{ - - private final String name; - private final Configuration configuration; - private final ConfigurationObjectBuilder configurationObjectBuilder; - private final ResolverSet resolverSet; - private final ExtensionManager extensionManager; - - private LoadingCache cache; - - /** - * Creates a new instance - * - * @param name the name of the config definition - * @param configuration the {@link Configuration} model - * @param configurationObjectBuilder the introspection model of the objects this resolver produces - * @param resolverSet the {@link ResolverSet} that's going to be evaluated - * @param muleContext the current {@link MuleContext} - */ - public DynamicConfigurationValueResolver(String name, - Configuration configuration, - ConfigurationObjectBuilder configurationObjectBuilder, - ResolverSet resolverSet, - MuleContext muleContext) - { - this.name = name; - this.configuration = configuration; - this.configurationObjectBuilder = configurationObjectBuilder; - this.resolverSet = resolverSet; - extensionManager = muleContext.getExtensionManager(); - buildCache(); - } - - private void buildCache() - { - cache = CacheBuilder.newBuilder().build(new CacheLoader() - { - @Override - public Object load(ResolverSetResult key) throws Exception - { - Object config = configurationObjectBuilder.build(key); - extensionManager.registerConfigurationInstance(configuration, name, config); - - return config; - } - }); - } - - /** - * Evaluates {@link #resolverSet} using the given {@code event} and returns - * an instance produced with the result. For equivalent {@link ResolverSetResult}s - * it will return the same instance, for as long as the {@code expirationInterval} and - * {@code expirationTimeUnit} were specified in the constructor - * - * @param event a {@link MuleEvent} - * @return the resolved value - */ - @Override - public Object resolve(MuleEvent event) throws MuleException - { - ResolverSetResult result = resolverSet.resolve(event); - return cache.getUnchecked(result); - } - - /** - * Whether or not {@link #resolverSet} is dynamic - */ - @Override - public boolean isDynamic() - { - return resolverSet.isDynamic(); - } -} diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/EventArgumentResolver.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/EventArgumentResolver.java index 2c50c47df29..cd149d265ef 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/EventArgumentResolver.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/EventArgumentResolver.java @@ -8,7 +8,7 @@ import org.mule.api.MuleEvent; import org.mule.extension.runtime.OperationContext; -import org.mule.module.extension.internal.runtime.DefaultOperationContext; +import org.mule.module.extension.internal.runtime.OperationContextAdapter; /** * An implementation of {@link ArgumentResolver} which @@ -16,7 +16,7 @@ * {@link OperationContext}. * * Notice that for this to work, the {@link OperationContext} - * has to be an instance of {@link DefaultOperationContext} + * has to be an instance of {@link OperationContextAdapter} * * @since 3.7.0 */ @@ -29,6 +29,6 @@ public class EventArgumentResolver implements ArgumentResolver @Override public MuleEvent resolve(OperationContext operationContext) { - return ((DefaultOperationContext) operationContext).getEvent(); + return ((OperationContextAdapter) operationContext).getEvent(); } } diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/MessageArgumentResolver.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/MessageArgumentResolver.java index 598e5e487b4..6346b64dfb1 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/MessageArgumentResolver.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/runtime/resolver/MessageArgumentResolver.java @@ -8,7 +8,7 @@ import org.mule.api.MuleMessage; import org.mule.extension.runtime.OperationContext; -import org.mule.module.extension.internal.runtime.DefaultOperationContext; +import org.mule.module.extension.internal.runtime.OperationContextAdapter; /** * An implementation of {@link ArgumentResolver} which @@ -16,7 +16,7 @@ * {@link OperationContext}. * * Notice that for this to work, the {@link OperationContext} - * has to be an instance of {@link DefaultOperationContext} + * has to be an instance of {@link OperationContextAdapter} * * @since 3.7.0 */ @@ -26,6 +26,6 @@ public class MessageArgumentResolver implements ArgumentResolver @Override public MuleMessage resolve(OperationContext operationContext) { - return ((DefaultOperationContext) operationContext).getEvent().getMessage(); + return ((OperationContextAdapter) operationContext).getEvent().getMessage(); } } diff --git a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/util/MuleExtensionUtils.java b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/util/MuleExtensionUtils.java index 9ac95993ce6..1daecc2632c 100644 --- a/modules/extensions-support/src/main/java/org/mule/module/extension/internal/util/MuleExtensionUtils.java +++ b/modules/extensions-support/src/main/java/org/mule/module/extension/internal/util/MuleExtensionUtils.java @@ -6,10 +6,24 @@ */ package org.mule.module.extension.internal.util; +import static org.mule.MessageExchangePattern.REQUEST_RESPONSE; import static org.mule.util.Preconditions.checkArgument; +import org.mule.DefaultMuleEvent; +import org.mule.DefaultMuleMessage; +import org.mule.api.MuleContext; +import org.mule.api.MuleEvent; +import org.mule.api.construct.FlowConstruct; +import org.mule.extension.annotations.param.Optional; import org.mule.extension.introspection.Configuration; import org.mule.extension.introspection.Described; import org.mule.extension.introspection.Operation; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.OperationContext; +import org.mule.module.extension.internal.runtime.ConfigurationObjectBuilder; +import org.mule.module.extension.internal.runtime.DynamicConfigurationInstanceProvider; +import org.mule.module.extension.internal.runtime.OperationContextAdapter; +import org.mule.module.extension.internal.runtime.StaticConfigurationInstanceProvider; +import org.mule.module.extension.internal.runtime.resolver.ResolverSet; import org.mule.module.extension.internal.runtime.resolver.ValueResolver; import org.mule.util.TemplateParser; @@ -163,6 +177,47 @@ public static List alphaSortDescribedList(List list) return list; } + public static ConfigurationInstanceProvider createConfigurationInstanceProvider(String name, + Configuration configuration, + ResolverSet resolverSet, + MuleContext muleContext) throws Exception + { + ConfigurationObjectBuilder configurationObjectBuilder = new ConfigurationObjectBuilder(configuration, resolverSet); + + if (resolverSet.isDynamic()) + { + return new DynamicConfigurationInstanceProvider<>(name, configuration, configurationObjectBuilder, resolverSet); + } + else + { + MuleEvent event = new DefaultMuleEvent(new DefaultMuleMessage(null, muleContext), REQUEST_RESPONSE, (FlowConstruct) null); + return new StaticConfigurationInstanceProvider<>(name, configuration, (T) configurationObjectBuilder.build(event)); + } + } + + public static OperationContextAdapter asOperationContextAdapter(OperationContext operationContext) + { + checkArgument(operationContext != null, "operationContext cannot be null"); + if (!(operationContext instanceof OperationContextAdapter)) + { + throw new IllegalArgumentException(String.format("operationContext was expected to be an instance of %s but got %s instead", + OperationContextAdapter.class.getName(), operationContext.getClass().getName())); + } + + return (OperationContextAdapter) operationContext; + } + + public static String getDefaultValue(Optional optional) + { + if (optional == null) + { + return null; + } + + String defaultValue = optional.defaultValue(); + return Optional.NULL.equals(defaultValue) ? null : defaultValue; + } + private static class DescribedComparator implements Comparator { diff --git a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/manager/DefaultExtensionManagerTestCase.java b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/manager/DefaultExtensionManagerTestCase.java index 3852c0bf997..9e6c8fc716a 100644 --- a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/manager/DefaultExtensionManagerTestCase.java +++ b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/manager/DefaultExtensionManagerTestCase.java @@ -7,24 +7,32 @@ package org.mule.module.extension.internal.manager; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; 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.anyString; import static org.mockito.Matchers.same; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mule.module.extension.internal.util.ExtensionsTestUtils.assertRegisteredWithUniqueMadeKey; +import static org.mule.api.lifecycle.LifecycleUtils.initialiseIfNeeded; import org.mule.api.MuleContext; +import org.mule.extension.ExtensionManager; import org.mule.extension.introspection.Configuration; import org.mule.extension.introspection.Extension; import org.mule.extension.introspection.Operation; -import org.mule.extension.runtime.OperationExecutor; +import org.mule.extension.introspection.Parameter; import org.mule.extension.introspection.capability.XmlCapability; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.ConfigurationInstanceRegistrationCallback; +import org.mule.extension.runtime.OperationContext; +import org.mule.extension.runtime.OperationExecutor; import org.mule.module.extension.internal.introspection.ExtensionDiscoverer; import org.mule.module.extension.internal.runtime.DelegatingOperationExecutor; import org.mule.module.extension.internal.util.ExtensionsTestUtils; @@ -33,9 +41,12 @@ import com.google.common.collect.ImmutableList; +import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; import org.junit.Before; @@ -51,7 +62,7 @@ public class DefaultExtensionManagerTestCase extends AbstractMuleTestCase { - private DefaultExtensionManager extensionsManager; + private ExtensionManager extensionsManager; private static final String EXTENSION1_NAME = "extension1"; private static final String EXTENSION1_CONFIG_NAME = "extension1Config"; @@ -75,32 +86,72 @@ public class DefaultExtensionManagerTestCase extends AbstractMuleTestCase @Mock(answer = RETURNS_DEEP_STUBS) private MuleContext muleContext; - @Mock + @Mock(answer = RETURNS_DEEP_STUBS) private Configuration extension1Configuration; @Mock private Operation extension1Operation; + @Mock + private OperationContext extension1OperationContext; + + @Mock + private ConfigurationInstanceProvider extension1ConfigurationInstanceProvider; + + @Mock + private DelegatingOperationExecutor executor; + private ClassLoader classLoader; + private final Object configInstance = new Object(); + private final Object executorDelegate = new Object(); + @Before public void before() { - extensionsManager = new DefaultExtensionManager(); + DefaultExtensionManager extensionsManager = new DefaultExtensionManager(); extensionsManager.setExtensionsDiscoverer(discoverer); extensionsManager.setMuleContext(muleContext); + this.extensionsManager = extensionsManager; when(extension1.getName()).thenReturn(EXTENSION1_NAME); + when(extension1.getConfigurations()).thenReturn(Arrays.asList(extension1Configuration)); when(extension2.getName()).thenReturn(EXTENSION2_NAME); when(extension1.getVersion()).thenReturn(EXTENSION1_VERSION); when(extension2.getVersion()).thenReturn(EXTENSION2_VERSION); when(extension1Configuration.getName()).thenReturn(EXTENSION1_CONFIG_NAME); + when(extension1Configuration.getInstantiator().newInstance()).thenReturn(configInstance); + when(extension1Configuration.getInstantiator().getObjectType()).thenReturn((Class) configInstance.getClass()); + when(extension1.getConfiguration(EXTENSION1_CONFIG_NAME)).thenReturn(extension1Configuration); when(extension1.getOperation(EXTENSION1_OPERATION_NAME)).thenReturn(extension1Operation); when(extension1Operation.getName()).thenReturn(EXTENSION1_OPERATION_NAME); + when(extension1OperationContext.getOperation()).thenReturn(extension1Operation); + + when(extension1ConfigurationInstanceProvider.getConfiguration()).thenReturn(extension1Configuration); + when(extension1ConfigurationInstanceProvider.getName()).thenReturn(EXTENSION1_CONFIG_INSTANCE_NAME); + when(extension1ConfigurationInstanceProvider.get(same(extension1OperationContext), any(ConfigurationInstanceRegistrationCallback.class))).thenAnswer(new Answer() + { + private boolean firstTime = true; + + @Override + public Object answer(InvocationOnMock invocation) throws Throwable + { + if (firstTime) + { + ConfigurationInstanceRegistrationCallback callback = (ConfigurationInstanceRegistrationCallback) invocation.getArguments()[1]; + callback.registerNewConfigurationInstance(extension1ConfigurationInstanceProvider, configInstance); + firstTime = false; + } + return configInstance; + } + }); + + when(executor.getExecutorDelegate()).thenReturn(executorDelegate); + when(extension1Operation.getExecutor(configInstance)).thenReturn(executor); classLoader = getClass().getClassLoader(); @@ -113,7 +164,7 @@ public List answer(InvocationOnMock invocation) throws Throwable } }); - ExtensionsTestUtils.stubRegistryKey(muleContext, EXTENSION1_CONFIG_INSTANCE_NAME, EXTENSION1_OPERATION_NAME); + ExtensionsTestUtils.stubRegistryKeys(muleContext, EXTENSION1_CONFIG_INSTANCE_NAME, EXTENSION1_OPERATION_NAME, EXTENSION1_NAME); } @Test @@ -250,45 +301,96 @@ public void contextClassLoaderKeptAfterException() } @Test - public void registerConfigurationInstance() throws Exception + public void registerConfigurationInstanceProvider() throws Exception { discover(); - Object configurationInstance = new Object(); - extensionsManager.registerConfigurationInstance(extension1Configuration, EXTENSION1_CONFIG_INSTANCE_NAME, configurationInstance); - extensionsManager.initialise(); - assertRegisteredWithUniqueMadeKey(muleContext, EXTENSION1_CONFIG_INSTANCE_NAME, configurationInstance); + extensionsManager.registerConfigurationInstanceProvider(EXTENSION1_CONFIG_INSTANCE_NAME, extension1ConfigurationInstanceProvider); + initialiseIfNeeded(extensionsManager); + + assertThat(extensionsManager.getOperationExecutor(EXTENSION1_CONFIG_INSTANCE_NAME, extension1OperationContext), is(notNullValue())); + ExtensionsTestUtils.assertRegisteredWithUniqueMadeKey(muleContext, EXTENSION1_CONFIG_INSTANCE_NAME, configInstance); } @Test public void getOperationExecutor() throws Exception { discover(); + extensionsManager.registerConfigurationInstanceProvider(EXTENSION1_CONFIG_INSTANCE_NAME, extension1ConfigurationInstanceProvider); - final Object configInstance = new Object(); - extensionsManager.registerConfigurationInstance(extension1Configuration, EXTENSION1_CONFIG_INSTANCE_NAME, configInstance); - - final Object executorDelegate = new Object(); - DelegatingOperationExecutor executor = mock(DelegatingOperationExecutor.class); - when(executor.getExecutorDelegate()).thenReturn(executorDelegate); - - when(extension1Operation.getExecutor(configInstance)).thenReturn(executor); - OperationExecutor managedExecutor = extensionsManager.getOperationExecutor(extension1Operation, configInstance); + OperationExecutor managedExecutor = extensionsManager.getOperationExecutor(EXTENSION1_CONFIG_INSTANCE_NAME, extension1OperationContext); assertThat(managedExecutor, is(sameInstance((OperationExecutor) executor))); // ask for the same executor again and check that it's still the same instance - managedExecutor = extensionsManager.getOperationExecutor(extension1Operation, configInstance); + managedExecutor = extensionsManager.getOperationExecutor(EXTENSION1_CONFIG_INSTANCE_NAME, extension1OperationContext); assertThat(managedExecutor, is(sameInstance((OperationExecutor) executor))); verify(muleContext.getRegistry()).registerObject(anyString(), same(executorDelegate)); - verify(extension1Operation.getExecutor(configInstance)); + verify(extension1Operation).getExecutor(configInstance); + } + + @Test(expected = IllegalArgumentException.class) + public void getOperationExecutorForUnregisteredConfigurationInstanceProvider() throws Exception + { + discover(); + OperationContext operationContext = mock(OperationContext.class); + when(operationContext.getOperation()).thenReturn(extension1Operation); + ConfigurationInstanceProvider configurationInstanceProvider = mock(ConfigurationInstanceProvider.class); + when(configurationInstanceProvider.getConfiguration()).thenReturn(extension1Configuration); + + extensionsManager.getOperationExecutor(EXTENSION1_CONFIG_INSTANCE_NAME, operationContext); + } + + @Test + public void initialise() throws Exception + { + discover(); + Map providers = new HashMap<>(); + providers.put(EXTENSION1_CONFIG_INSTANCE_NAME, extension1ConfigurationInstanceProvider); + when(muleContext.getRegistry().lookupByType(ConfigurationInstanceProvider.class)).thenReturn(providers); + + initialiseIfNeeded(extensionsManager); + } + + @Test + public void getConfigurationInstance() throws Exception + { + assertThat(extensionsManager, is(instanceOf(ExtensionManagerAdapter.class))); + ExtensionManagerAdapter extensionsManager = (ExtensionManagerAdapter) this.extensionsManager; + + discover(); + extensionsManager.registerConfigurationInstanceProvider(EXTENSION1_CONFIG_INSTANCE_NAME, extension1ConfigurationInstanceProvider); + Object configurationInstance = extensionsManager.getConfigurationInstance(extension1ConfigurationInstanceProvider, extension1OperationContext); + assertThat(configurationInstance, is(sameInstance(configInstance))); + } + + @Test + public void getOperationExecutorThroughDefaultConfig() + { + discover(); + extensionsManager.registerConfigurationInstanceProvider(EXTENSION1_CONFIG_INSTANCE_NAME, extension1ConfigurationInstanceProvider); + + OperationExecutor managedExecutor = extensionsManager.getOperationExecutor(extension1OperationContext); + assertThat(managedExecutor, is(sameInstance((OperationExecutor) executor))); + verify(extension1Operation).getExecutor(configInstance); } @Test(expected = IllegalStateException.class) - public void getOperationExecutorForUnregisteredConfigurationInstance() throws Exception + public void getOperationExecutorWithNotImplicitConfig() { + makeExtension1ConfigurationNotImplicit(); discover(); - extensionsManager.getOperationExecutor(extension1Operation, new Object()); + + extensionsManager.getOperationExecutor(extension1OperationContext); + } + + private void makeExtension1ConfigurationNotImplicit() + { + Parameter parameter1 = mock(Parameter.class); + when(parameter1.isRequired()).thenReturn(true); + + when(extension1Configuration.getParameters()).thenReturn(Arrays.asList(parameter1, parameter1)); + when(extension1Configuration.getInstantiator().newInstance()).thenReturn(configInstance); } private List getTestExtensions() diff --git a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/ReflectiveMethodOperationExecutorTestCase.java b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/ReflectiveMethodOperationExecutorTestCase.java index 149b4621b20..926ab9e4332 100644 --- a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/ReflectiveMethodOperationExecutorTestCase.java +++ b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/ReflectiveMethodOperationExecutorTestCase.java @@ -16,6 +16,7 @@ import static org.mule.module.extension.HealthStatus.DEAD; import static org.mule.module.extension.HeisenbergExtension.HEISENBERG; import org.mule.api.MuleEvent; +import org.mule.extension.introspection.Operation; import org.mule.extension.runtime.OperationContext; import org.mule.extension.introspection.Parameter; import org.mule.module.extension.HeisenbergExtension; @@ -48,6 +49,9 @@ public class ReflectiveMethodOperationExecutorTestCase extends AbstractMuleTestC @Mock private ResolverSetResult parameters; + @Mock + private Operation operation; + private Map parameterValues = new HashMap<>(); private ReflectiveMethodOperationExecutor executor; @@ -60,7 +64,7 @@ public class ReflectiveMethodOperationExecutorTestCase extends AbstractMuleTestC public void before() { initHeisenberg(); - operationContext = new DefaultOperationContext(parameters, muleEvent); + operationContext = new DefaultOperationContext(operation, parameters, muleEvent); when(operationContext.getParameters()).thenReturn(parameterValues); } diff --git a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/VoidReturnDelegateTestCase.java b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/VoidReturnDelegateTestCase.java index 918318d58a0..85b30f23ed0 100644 --- a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/VoidReturnDelegateTestCase.java +++ b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/VoidReturnDelegateTestCase.java @@ -11,6 +11,8 @@ import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import org.mule.api.MuleEvent; +import org.mule.extension.introspection.Operation; +import org.mule.extension.runtime.OperationContext; import org.mule.module.extension.internal.runtime.resolver.ResolverSetResult; import org.mule.tck.junit4.AbstractMuleTestCase; import org.mule.tck.size.SmallTest; @@ -25,7 +27,7 @@ public class VoidReturnDelegateTestCase extends AbstractMuleTestCase public void returnsMuleEvent() { MuleEvent event = mock(MuleEvent.class); - DefaultOperationContext operationContext = new DefaultOperationContext(mock(ResolverSetResult.class), event); + OperationContext operationContext = new DefaultOperationContext(mock(Operation.class), mock(ResolverSetResult.class), event); Object returnValue = VoidReturnDelegate.INSTANCE.asReturnValue(new Object(), operationContext); assertThat(event, is(sameInstance(returnValue))); diff --git a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/AbstractConfigurationInstanceProviderTestCase.java b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/AbstractConfigurationInstanceProviderTestCase.java new file mode 100644 index 00000000000..cd5076ec263 --- /dev/null +++ b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/AbstractConfigurationInstanceProviderTestCase.java @@ -0,0 +1,48 @@ +/* + * 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.module.extension.internal.runtime.resolver; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.verify; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.ConfigurationInstanceRegistrationCallback; +import org.mule.module.extension.internal.runtime.DefaultOperationContext; +import org.mule.tck.junit4.AbstractMuleTestCase; + +import org.mockito.Mock; + +abstract class AbstractConfigurationInstanceProviderTestCase extends AbstractMuleTestCase +{ + @Mock + protected DefaultOperationContext operationContext; + + @Mock + protected ConfigurationInstanceRegistrationCallback configurationInstanceRegistrationCallback; + + protected ConfigurationInstanceProvider instanceProvider; + + + protected void assertConfigInstanceRegistered(ConfigurationInstanceProvider configurationInstanceProvider, T configurationInstance) + { + verify(configurationInstanceRegistrationCallback).registerNewConfigurationInstance(configurationInstanceProvider, configurationInstance); + } + + protected void assertSameInstancesResolved() throws Exception + { + final int count = 10; + Object config = instanceProvider.get(operationContext, configurationInstanceRegistrationCallback); + + for (int i = 1; i < count; i++) + { + assertThat(instanceProvider.get(operationContext, configurationInstanceRegistrationCallback), is(sameInstance(config))); + } + + assertConfigInstanceRegistered(instanceProvider, config); + } +} diff --git a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/ConfigurationValueResolverTestCase.java b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/ConfigurationValueResolverTestCase.java index 5e5971eb9f9..2aef5a8c640 100644 --- a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/ConfigurationValueResolverTestCase.java +++ b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/ConfigurationValueResolverTestCase.java @@ -6,16 +6,8 @@ */ package org.mule.module.extension.internal.runtime.resolver; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; -import static org.hamcrest.Matchers.sameInstance; -import static org.junit.Assert.assertThat; import static org.mockito.Answers.RETURNS_DEEP_STUBS; -import static org.mockito.Matchers.same; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mule.module.extension.internal.util.ExtensionsTestUtils.getParameter; import org.mule.api.MuleContext; @@ -25,7 +17,7 @@ import org.mule.extension.introspection.Parameter; import org.mule.module.extension.HeisenbergExtension; import org.mule.module.extension.internal.util.ExtensionsTestUtils; -import org.mule.tck.junit4.AbstractMuleTestCase; +import org.mule.module.extension.internal.util.MuleExtensionUtils; import org.mule.tck.size.SmallTest; import java.util.HashMap; @@ -34,7 +26,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.runners.MockitoJUnitRunner; @@ -42,7 +33,7 @@ @SmallTest @RunWith(MockitoJUnitRunner.class) -public class ConfigurationValueResolverTestCase extends AbstractMuleTestCase +public class ConfigurationValueResolverTestCase extends AbstractConfigurationInstanceProviderTestCase { private static final String CONFIG_NAME = "myConfig"; @@ -68,13 +59,11 @@ public class ConfigurationValueResolverTestCase extends AbstractMuleTestCase @Mock private ExtensionManager extensionManager; - private ConfigurationValueResolver resolver; - @Before public void before() throws Exception { when(muleContext.getExtensionManager()).thenReturn(extensionManager); - ExtensionsTestUtils.stubRegistryKey(muleContext, CONFIG_NAME); + ExtensionsTestUtils.stubRegistryKeys(muleContext, CONFIG_NAME); when(configuration.getInstantiator().getObjectType()).thenReturn(MODULE_CLASS); when(configuration.getInstantiator().newInstance()).thenAnswer(new Answer() @@ -87,105 +76,21 @@ public Object answer(InvocationOnMock invocation) throws Throwable }); when(configuration.getCapabilities(any(Class.class))).thenReturn(null); + when(operationContext.getEvent()).thenReturn(event); + Map parameters = new HashMap<>(); parameters.put(getParameter("myName", String.class), new StaticValueResolver(MY_NAME)); parameters.put(getParameter("age", Integer.class), new StaticValueResolver(AGE)); when(resolverSet.getResolvers()).thenReturn(parameters); - } + when(resolverSet.isDynamic()).thenReturn(false); - @Test - public void resolveStaticConfig() throws Exception - { - resolver = getStaticConfigResolver(); - assertSameInstancesResolved(); - assertConfigInstanceRegistered(resolver.resolve(event)); + instanceProvider = MuleExtensionUtils.createConfigurationInstanceProvider(CONFIG_NAME, configuration, resolverSet, muleContext); } @Test - public void resolveDynamicConfigWithEquivalentEvent() throws Exception + public void resolveStaticConfig() throws Exception { - resolver = getDynamicConfigResolver(); assertSameInstancesResolved(); } - @Test - public void resolveDynamicConfigWithDifferentEvent() throws Exception - { - resolver = getDynamicConfigResolver(); - Object config1 = resolver.resolve(event); - - when(resolverSet.resolve(event)).thenReturn(mock(ResolverSetResult.class)); - Object config2 = resolver.resolve(event); - - assertThat(config1, is(not(sameInstance(config2)))); - assertConfigInstanceRegistered(config1); - assertConfigInstanceRegistered(config2); - } - - private void assertConfigInstanceRegistered(Object instance) - { - ArgumentCaptor key = ArgumentCaptor.forClass(String.class); - verify(extensionManager).registerConfigurationInstance(same(configuration), key.capture(), same(instance)); - assertThat(key.getValue(), containsString(CONFIG_NAME)); - } - - private void assertSameInstancesResolved() throws Exception - { - final int count = 10; - Object config = resolver.resolve(event); - - for (int i = 1; i < count; i++) - { - assertThat(resolver.resolve(event), is(sameInstance(config))); - } - - assertConfigInstanceRegistered(config); - } - - @Test - public void staticConfigIsNotDynamic() throws Exception - { - resolver = getStaticConfigResolver(); - assertThat(resolver.isDynamic(), is(false)); - } - - @Test - public void dynamicConfigIsDynamic() throws Exception - { - resolver = getDynamicConfigResolver(); - assertThat(resolver.isDynamic(), is(true)); - } - - @Test - public void staticConfigName() throws Exception - { - resolver = getStaticConfigResolver(); - assertThat(resolver.getName(), is(CONFIG_NAME)); - } - - @Test - public void dynamicConfigName() throws Exception - { - resolver = getDynamicConfigResolver(); - assertThat(resolver.getName(), is(CONFIG_NAME)); - } - - private ConfigurationValueResolver getStaticConfigResolver() throws Exception - { - when(resolverSet.isDynamic()).thenReturn(false); - return getConfigResolver(); - } - - private ConfigurationValueResolver getDynamicConfigResolver() throws Exception - { - when(resolverSet.isDynamic()).thenReturn(true); - when(resolverSet.resolve(event)).thenReturn(mock(ResolverSetResult.class)); - - return getConfigResolver(); - } - - private ConfigurationValueResolver getConfigResolver() throws Exception - { - return new ConfigurationValueResolver(CONFIG_NAME, configuration, resolverSet, muleContext); - } } diff --git a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationValueResolverTestCase.java b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationInstanceProviderTestCase.java similarity index 64% rename from modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationValueResolverTestCase.java rename to modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationInstanceProviderTestCase.java index be42a3ab320..4e09b37f64d 100644 --- a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationValueResolverTestCase.java +++ b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/runtime/resolver/DynamicConfigurationInstanceProviderTestCase.java @@ -22,10 +22,11 @@ import org.mule.extension.introspection.Configuration; import org.mule.module.extension.HeisenbergExtension; import org.mule.module.extension.internal.runtime.ConfigurationObjectBuilder; +import org.mule.module.extension.internal.runtime.DynamicConfigurationInstanceProvider; import org.mule.module.extension.internal.util.ExtensionsTestUtils; -import org.mule.tck.junit4.AbstractMuleTestCase; import org.mule.tck.size.SmallTest; +import org.hamcrest.Matchers; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -37,7 +38,7 @@ @SmallTest @RunWith(MockitoJUnitRunner.class) -public class DynamicConfigurationValueResolverTestCase extends AbstractMuleTestCase +public class DynamicConfigurationInstanceProviderTestCase extends AbstractConfigurationInstanceProviderTestCase { private static final Class MODULE_CLASS = HeisenbergExtension.class; @@ -61,14 +62,12 @@ public class DynamicConfigurationValueResolverTestCase extends AbstractMuleTestC @Mock private ExtensionManager extensionManager; - private DynamicConfigurationValueResolver resolver; - private ConfigurationObjectBuilder configurationObjectBuilder; @Before public void before() throws Exception { - ExtensionsTestUtils.stubRegistryKey(muleContext, CONFIGURATION_NAME); + ExtensionsTestUtils.stubRegistryKeys(muleContext, CONFIGURATION_NAME); when(configuration.getInstantiator().getObjectType()).thenReturn(MODULE_CLASS); when(configuration.getInstantiator().newInstance()).thenAnswer(new Answer() { @@ -83,18 +82,20 @@ public Object answer(InvocationOnMock invocation) throws Throwable when(resolverSet.resolve(event)).thenReturn(resolverSetResult); when(muleContext.getExtensionManager()).thenReturn(extensionManager); + when(operationContext.getEvent()).thenReturn(event); + configurationObjectBuilder = new ConfigurationObjectBuilder(configuration, resolverSet); - resolver = new DynamicConfigurationValueResolver(CONFIGURATION_NAME, configuration, configurationObjectBuilder, resolverSet, muleContext); + instanceProvider = new DynamicConfigurationInstanceProvider(CONFIGURATION_NAME, configuration, configurationObjectBuilder, resolverSet); } @Test public void resolveCached() throws Exception { final int count = 10; - HeisenbergExtension config = (HeisenbergExtension) resolver.resolve(event); + HeisenbergExtension config = (HeisenbergExtension) instanceProvider.get(operationContext, configurationInstanceRegistrationCallback); for (int i = 1; i < count; i++) { - assertThat((HeisenbergExtension) resolver.resolve(event), is(sameInstance(config))); + assertThat((HeisenbergExtension) instanceProvider.get(operationContext, configurationInstanceRegistrationCallback), is(sameInstance(config))); } verify(resolverSet, times(count)).resolve(event); @@ -103,12 +104,31 @@ public void resolveCached() throws Exception @Test public void resolveDifferentInstances() throws Exception { - HeisenbergExtension instance1 = (HeisenbergExtension) resolver.resolve(event); + HeisenbergExtension instance1 = (HeisenbergExtension) instanceProvider.get(operationContext, configurationInstanceRegistrationCallback); ResolverSetResult alternateResult = mock(ResolverSetResult.class, Mockito.RETURNS_DEEP_STUBS); when(resolverSet.resolve(event)).thenReturn(alternateResult); - HeisenbergExtension instance2 = (HeisenbergExtension) resolver.resolve(event); + HeisenbergExtension instance2 = (HeisenbergExtension) instanceProvider.get(operationContext, configurationInstanceRegistrationCallback); assertThat(instance2, is(not(sameInstance(instance1)))); } + + @Test + public void resolveDynamicConfigWithEquivalentEvent() throws Exception + { + assertSameInstancesResolved(); + } + + @Test + public void resolveDynamicConfigWithDifferentEvent() throws Exception + { + Object config1 = instanceProvider.get(operationContext, configurationInstanceRegistrationCallback); + + when(resolverSet.resolve(event)).thenReturn(mock(ResolverSetResult.class)); + Object config2 = instanceProvider.get(operationContext, configurationInstanceRegistrationCallback); + + assertThat(config1, is(not(Matchers.sameInstance(config2)))); + assertConfigInstanceRegistered(instanceProvider, config1); + assertConfigInstanceRegistered(instanceProvider, config2); + } } diff --git a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/util/ExtensionsTestUtils.java b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/util/ExtensionsTestUtils.java index 936bed2f0aa..c5d7394e0c2 100644 --- a/modules/extensions-support/src/test/java/org/mule/module/extension/internal/util/ExtensionsTestUtils.java +++ b/modules/extensions-support/src/test/java/org/mule/module/extension/internal/util/ExtensionsTestUtils.java @@ -18,7 +18,13 @@ import org.mule.api.MuleContext; import org.mule.api.MuleEvent; import org.mule.extension.introspection.DataType; +import org.mule.extension.introspection.Operation; import org.mule.extension.introspection.Parameter; +import org.mule.extension.runtime.ConfigurationInstanceProvider; +import org.mule.extension.runtime.OperationContext; +import org.mule.module.extension.internal.manager.ExtensionManagerAdapter; +import org.mule.module.extension.internal.runtime.DefaultOperationContext; +import org.mule.module.extension.internal.runtime.resolver.ResolverSetResult; import org.mule.module.extension.internal.runtime.resolver.ValueResolver; import org.apache.commons.lang.ArrayUtils; @@ -58,7 +64,7 @@ public static Parameter getParameter(String name, Class type) return parameter; } - public static void stubRegistryKey(MuleContext muleContext, final String... keys) + public static void stubRegistryKeys(MuleContext muleContext, final String... keys) { when(muleContext.getRegistry().get(anyString())).thenAnswer(new Answer() { @@ -88,4 +94,16 @@ public static void assertRegisteredWithUniqueMadeKey(MuleContext muleContext, St verify(muleContext.getRegistry()).registerObject(captor.capture(), same(object)); assertThat(captor.getValue(), containsString(key)); } + + public static C getConfigurationInstance(String key, MuleEvent muleEvent) throws Exception + { + ConfigurationInstanceProvider configurationInstanceProvider = muleEvent.getMuleContext().getRegistry().lookupObject(key); + ExtensionManagerAdapter extensionManager = (ExtensionManagerAdapter) muleEvent.getMuleContext().getExtensionManager(); + return extensionManager.getConfigurationInstance(configurationInstanceProvider, getOperationContext(muleEvent)); + } + + private static OperationContext getOperationContext(MuleEvent event) throws Exception + { + return new DefaultOperationContext(mock(Operation.class), mock(ResolverSetResult.class), event); + } } diff --git a/modules/json/src/test/resources/validate-schema-with-validation-module-config.xml b/modules/json/src/test/resources/validate-schema-with-validation-module-config.xml index 1ad887142ff..770292781f7 100644 --- a/modules/json/src/test/resources/validate-schema-with-validation-module-config.xml +++ b/modules/json/src/test/resources/validate-schema-with-validation-module-config.xml @@ -7,10 +7,8 @@ http://www.mulesoft.org/schema/mule/validation http://www.mulesoft.org/schema/mule/validation/current/mule-validation.xsd http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd"> - - - + diff --git a/pom.xml b/pom.xml index 501e838d606..95565da118e 100644 --- a/pom.xml +++ b/pom.xml @@ -1350,7 +1350,7 @@ ${muleExtensionsApiVersion} - org.mule.extensions + org.mule mule-extensions-annotations ${muleExtensionsApiVersion}