org.mule.runtime.config.internal.dsl.model.ExtensionModelHelper Maven / Gradle / Ivy
/*
* 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.config.internal.dsl.model;
import static java.util.Collections.singletonList;
import static java.util.Optional.empty;
import static org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType.ERROR_HANDLER;
import static org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType.FLOW;
import static org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType.OPERATION;
import static org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType.ROUTER;
import static org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType.SCOPE;
import static org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType.SOURCE;
import static org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType.UNKNOWN;
import static org.mule.runtime.api.util.NameUtils.COMPONENT_NAME_SEPARATOR;
import static org.mule.runtime.api.util.NameUtils.toCamelCase;
import static org.mule.runtime.config.internal.dsl.model.extension.xml.MacroExpansionModuleModel.ORIGINAL_IDENTIFIER;
import org.mule.runtime.api.component.ComponentIdentifier;
import org.mule.runtime.api.component.TypedComponentIdentifier;
import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.runtime.api.meta.model.ComponentModelVisitor;
import org.mule.runtime.api.meta.model.ExtensionModel;
import org.mule.runtime.api.meta.model.construct.ConstructModel;
import org.mule.runtime.api.meta.model.construct.HasConstructModels;
import org.mule.runtime.api.meta.model.nested.NestableElementModel;
import org.mule.runtime.api.meta.model.nested.NestableElementModelVisitor;
import org.mule.runtime.api.meta.model.nested.NestedChainModel;
import org.mule.runtime.api.meta.model.nested.NestedComponentModel;
import org.mule.runtime.api.meta.model.nested.NestedRouteModel;
import org.mule.runtime.api.meta.model.operation.HasOperationModels;
import org.mule.runtime.api.meta.model.operation.OperationModel;
import org.mule.runtime.api.meta.model.source.HasSourceModels;
import org.mule.runtime.api.meta.model.source.SourceModel;
import org.mule.runtime.api.util.Reference;
import org.mule.runtime.config.api.dsl.model.DslElementModel;
import org.mule.runtime.config.internal.model.ComponentModel;
import org.mule.runtime.extension.api.stereotype.MuleStereotypes;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
/**
* Helper class to work with a set of {@link ExtensionModel}s
*
* Contains a cache for searches within the extension models so we avoid processing each extension model twice.
*
* It's recommended that the application only has one instance of this class to avoid processing the extension models several
* times.
*
* since 4.0
*/
public class ExtensionModelHelper {
private final Set extensionsModels;
private Cache> extensionComponentModelByComponentIdentifier =
CacheBuilder.newBuilder().build();
/**
* @param extensionModels the set of {@link ExtensionModel}s to work with. Usually this is the set of models configured within a
* mule artifact.
*/
public ExtensionModelHelper(Set extensionModels) {
this.extensionsModels = extensionModels;
}
/**
* Find a {@link DslElementModel} for a given {@link ComponentModel}
*
* @param componentModel the component model from the configuration.
* @return the {@link DslElementModel} associated with the configuration or an {@link Optional#empty()} if there isn't one.
*/
public TypedComponentIdentifier.ComponentType findComponentType(ComponentModel componentModel) {
Optional extensionComponentModelOptional =
findComponentModel(componentModel.getCustomAttributes().containsKey(ORIGINAL_IDENTIFIER)
? (ComponentIdentifier) componentModel.getCustomAttributes().get(ORIGINAL_IDENTIFIER)
: componentModel.getIdentifier());
return extensionComponentModelOptional.map(extensionComponentModel -> {
Reference componentTypeReference = new Reference<>();
extensionComponentModel.accept(new ComponentModelVisitor() {
@Override
public void visit(OperationModel model) {
componentTypeReference.set(OPERATION);
}
@Override
public void visit(SourceModel model) {
componentTypeReference.set(SOURCE);
}
@Override
public void visit(ConstructModel model) {
if (model.getStereotype().equals(MuleStereotypes.ERROR_HANDLER)) {
componentTypeReference.set(ERROR_HANDLER);
return;
}
if (model.getStereotype().equals(MuleStereotypes.FLOW)) {
componentTypeReference.set(FLOW);
return;
}
NestedComponentVisitor nestedComponentVisitor = new NestedComponentVisitor(componentTypeReference);
for (NestableElementModel nestableElementModel : model.getNestedComponents()) {
nestableElementModel.accept(nestedComponentVisitor);
if (componentTypeReference.get() != null) {
return;
}
}
}
});
return componentTypeReference.get() == null ? UNKNOWN : componentTypeReference.get();
}).orElse(UNKNOWN);
}
/**
* Finds a {@link org.mule.runtime.api.meta.model.ComponentModel} within the provided set of {@link ExtensionModel}s by a
* {@link ComponentIdentifier}.
*
* @param componentIdentifier the identifier to use for the search.
* @return the found {@link org.mule.runtime.api.meta.model.ComponentModel} or {@link Optional#empty()} if it couldn't be found.
*/
public Optional findComponentModel(ComponentIdentifier componentIdentifier) {
try {
return extensionComponentModelByComponentIdentifier.get(componentIdentifier, () -> {
String componentName = toCamelCase(componentIdentifier.getName(), COMPONENT_NAME_SEPARATOR);
for (ExtensionModel extensionModel : extensionsModels) {
if (extensionModel.getXmlDslModel().getPrefix().equals(componentIdentifier.getNamespace())) {
List operationModelsProviders = ImmutableList.builder()
.add(extensionModel).addAll(extensionModel.getConfigurationModels()).build();
List sourceModelsProviders = ImmutableList.builder()
.add(extensionModel).addAll(extensionModel.getConfigurationModels()).build();
List constructModelsProviders = singletonList(extensionModel);
Optional componentModel =
resolveModel(operationModelsProviders, sourceModelsProviders, constructModelsProviders, componentName);
//TODO MULE-13894 remove this once unified extensionModel names to use camelCase (see smart connectors and crafted declared extesion models)
if (!componentModel.isPresent()) {
componentModel = resolveModel(operationModelsProviders, sourceModelsProviders, constructModelsProviders,
componentIdentifier.getName());
}
return componentModel;
}
}
return empty();
});
} catch (ExecutionException e) {
throw new MuleRuntimeException(e);
}
}
private Optional resolveModel(List operationModelsProviders,
List sourceModelsProviders,
List constructModelsProviders,
String componentName) {
for (HasOperationModels operationModelsProvider : operationModelsProviders) {
Optional operationModel = operationModelsProvider.getOperationModel(componentName);
if (operationModel.isPresent()) {
return operationModel;
}
}
for (HasSourceModels sourceModelsProvider : sourceModelsProviders) {
Optional sourceModel = sourceModelsProvider.getSourceModel(componentName);
if (sourceModel.isPresent()) {
return sourceModel;
}
}
for (HasConstructModels constructModelsProvider : constructModelsProviders) {
Optional constructModel = constructModelsProvider.getConstructModel(componentName);
if (constructModel.isPresent()) {
return constructModel;
}
}
return empty();
}
/**
* Visitor of {@link ConstructModel} that determines it
* {@link org.mule.runtime.api.component.TypedComponentIdentifier.ComponentType}
*/
static class NestedComponentVisitor implements NestableElementModelVisitor {
private Reference reference;
public NestedComponentVisitor(Reference reference) {
this.reference = reference;
}
@Override
public void visit(NestedComponentModel component) {}
@Override
public void visit(NestedChainModel component) {
reference.set(SCOPE);
}
@Override
public void visit(NestedRouteModel component) {
reference.set(ROUTER);
}
}
}