/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.core.internal.metadata; import static com.google.common.collect.ImmutableMap.copyOf; import static java.lang.String.format; import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage; import static org.mule.runtime.api.metadata.resolving.FailureCode.COMPONENT_NOT_FOUND; import static org.mule.runtime.api.metadata.resolving.FailureCode.NO_DYNAMIC_METADATA_AVAILABLE; import static org.mule.runtime.api.metadata.resolving.MetadataFailure.Builder.newFailure; import static org.mule.runtime.api.metadata.resolving.MetadataResult.failure; import org.mule.runtime.api.component.location.Location; import org.mule.runtime.api.exception.MuleRuntimeException; import org.mule.runtime.api.lifecycle.Initialisable; import org.mule.runtime.api.lifecycle.InitialisationException; import org.mule.runtime.api.meta.model.ComponentModel; import org.mule.runtime.api.meta.model.operation.OperationModel; import org.mule.runtime.api.meta.model.source.SourceModel; import org.mule.runtime.api.metadata.EntityMetadataProvider; import org.mule.runtime.api.metadata.MetadataCache; import org.mule.runtime.api.metadata.MetadataKey; import org.mule.runtime.api.metadata.MetadataKeyProvider; import org.mule.runtime.api.metadata.MetadataKeysContainer; import org.mule.runtime.api.metadata.MetadataProvider; import org.mule.runtime.api.metadata.MetadataResolvingException; import org.mule.runtime.api.metadata.MetadataService; import org.mule.runtime.api.metadata.descriptor.ComponentMetadataDescriptor; import org.mule.runtime.api.metadata.descriptor.TypeMetadataDescriptor; import org.mule.runtime.api.metadata.resolving.MetadataResult; import org.mule.runtime.core.api.MuleContext; import org.mule.runtime.core.api.config.ConfigurationInstanceNotification; import org.mule.runtime.core.api.context.notification.CustomNotificationListener; import org.mule.runtime.core.api.locator.ConfigurationComponentLocator; import org.mule.runtime.core.context.notification.NotificationException; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.util.Map; import java.util.concurrent.ExecutionException; import javax.inject.Inject; /** * Default implementation of the {@link MetadataService}, which provides access to the Metadata of any Component in the * application, using it's {@link Location}. Requires the injection of the {@link MuleContext}, to be able to lookup the component * inside the Mule App flows using the given {@link Location} * * @since 4.0 */ public class MuleMetadataService implements MetadataService, Initialisable { private static final String COMPONENT_NOT_METADATA_PROVIDER = "Component [%s] is not a MetadataProvider or MetadataEntityProvider, no information available"; private static final String COMPONENT_NOT_METADATA_KEY_PROVIDER = "Component [%s] is not a MetadataKeyProvider, no information available"; private static final String EXCEPTION_RESOLVING_COMPONENT_METADATA = "An exception occurred while resolving metadata for component '%s'"; private static final String EXCEPTION_RESOLVING_METADATA_KEYS = "An exception occurred while resolving Component MetadataKeys"; @Inject private MuleContext muleContext; @Inject private ConfigurationComponentLocator componentLocator; private final LoadingCache<String, MetadataCache> caches; public MuleMetadataService() { caches = CacheBuilder.newBuilder().build(new CacheLoader<String, MetadataCache>() { @Override public MetadataCache load(String id) throws Exception { return new DefaultMetadataCache(); } }); } /** * Initialize this instance by registering a {@link CustomNotificationListener} * * @throws InitialisationException */ @Override public void initialise() throws InitialisationException { try { muleContext.registerListener((CustomNotificationListener<ConfigurationInstanceNotification>) notification -> { try { if (notification.getAction() == ConfigurationInstanceNotification.CONFIGURATION_STOPPED) { String name = ((ConfigurationInstanceNotification) notification).getConfigurationInstance().getName(); disposeCache(name); } } catch (Exception e) { throw new RuntimeException("Error while looking for the MetadataManager in the registry", e); } }); } catch (NotificationException e) { throw new InitialisationException(createStaticMessage("Could not register ConfigurationInstanceListener"), e, this); } } /** * {@inheritDoc} */ @Override public MetadataResult<MetadataKeysContainer> getMetadataKeys(Location location) { return exceptionHandledMetadataFetch(() -> findMetadataKeyProvider(location).getMetadataKeys(), EXCEPTION_RESOLVING_METADATA_KEYS); } /** * {@inheritDoc} */ @Override public MetadataResult<ComponentMetadataDescriptor<OperationModel>> getOperationMetadata(Location location, MetadataKey key) { return getComponentMetadataWithKey(location, key); } /** * {@inheritDoc} */ @Override public MetadataResult<ComponentMetadataDescriptor<OperationModel>> getOperationMetadata(Location location) { return getComponentMetadata(location); } /** * {@inheritDoc} */ @Override public MetadataResult<ComponentMetadataDescriptor<SourceModel>> getSourceMetadata(Location location, MetadataKey key) { return getComponentMetadataWithKey(location, key); } /** * {@inheritDoc} */ @Override public MetadataResult<ComponentMetadataDescriptor<SourceModel>> getSourceMetadata(Location location) { return getComponentMetadata(location); } /** * {@inheritDoc} */ @Override public MetadataResult<TypeMetadataDescriptor> getEntityMetadata(Location location, MetadataKey key) { return exceptionHandledMetadataFetch(() -> findEntityMetadataProvider(location).getEntityMetadata(key), format(EXCEPTION_RESOLVING_COMPONENT_METADATA, location)); } /** * {@inheritDoc} */ @Override public MetadataResult<MetadataKeysContainer> getEntityKeys(Location location) { return exceptionHandledMetadataFetch(() -> findEntityMetadataProvider(location).getEntityKeys(), format(EXCEPTION_RESOLVING_COMPONENT_METADATA, location)); } /** * {@inheritDoc} */ @Override public void disposeCache(String id) { caches.invalidate(id); } public MetadataCache getMetadataCache(String id) { try { return caches.get(id); } catch (ExecutionException e) { throw new MuleRuntimeException(createStaticMessage("Could not get the cache with id:" + id), e); } } public Map<String, ? extends MetadataCache> getMetadataCaches() { return copyOf(caches.asMap()); } private <T> MetadataResult<T> exceptionHandledMetadataFetch(MetadataDelegate<T> producer, String failureMessage) { try { return producer.get(); } catch (InvalidComponentIdException e) { return failure(newFailure(e).withFailureCode(e.getFailureCode()).onComponent()); } catch (Exception e) { return failure(newFailure(e).onComponent()); } } private MetadataKeyProvider findMetadataKeyProvider(Location location) throws InvalidComponentIdException { try { return (MetadataKeyProvider) findComponent(location); } catch (ClassCastException e) { throw new InvalidComponentIdException(createStaticMessage(format(COMPONENT_NOT_METADATA_KEY_PROVIDER, location)), NO_DYNAMIC_METADATA_AVAILABLE); } } private Object findComponent(Location location) throws InvalidComponentIdException { return componentLocator.find(location) .orElseThrow(() -> new InvalidComponentIdException(createStaticMessage("No object found with location " + location), COMPONENT_NOT_FOUND)); } private <T extends ComponentModel> MetadataProvider<T> findMetadataProvider(Location location) throws InvalidComponentIdException { try { return (MetadataProvider<T>) findComponent(location); } catch (ClassCastException e) { throw new InvalidComponentIdException(createStaticMessage(format(COMPONENT_NOT_METADATA_PROVIDER, location)), NO_DYNAMIC_METADATA_AVAILABLE); } } private EntityMetadataProvider findEntityMetadataProvider(Location location) throws InvalidComponentIdException { try { return (EntityMetadataProvider) findComponent(location); } catch (ClassCastException e) { throw new InvalidComponentIdException(createStaticMessage(format(COMPONENT_NOT_METADATA_PROVIDER, location)), NO_DYNAMIC_METADATA_AVAILABLE); } } private <T extends ComponentModel> MetadataResult<ComponentMetadataDescriptor<T>> getComponentMetadata(Location location) { return exceptionHandledMetadataFetch(() -> ((MetadataProvider<T>) findMetadataProvider(location)) .getMetadata(), format(EXCEPTION_RESOLVING_COMPONENT_METADATA, location)); } private <T extends ComponentModel> MetadataResult<ComponentMetadataDescriptor<T>> getComponentMetadataWithKey(Location location, MetadataKey key) { return exceptionHandledMetadataFetch(() -> ((MetadataProvider<T>) findMetadataProvider(location)) .getMetadata(key), format(EXCEPTION_RESOLVING_COMPONENT_METADATA, location)); } @FunctionalInterface private interface MetadataDelegate<T> { MetadataResult<T> get() throws MetadataResolvingException, InvalidComponentIdException; } }