org.mule.runtime.config.internal.dsl.model.ConfigurationDependencyResolver 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.Arrays.stream;
import static java.util.Collections.emptyList;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static java.util.stream.Collectors.toList;
import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage;
import static org.mule.runtime.config.internal.dsl.model.DependencyNode.Type.INNER;
import static org.mule.runtime.config.internal.dsl.model.DependencyNode.Type.TOP_LEVEL;
import static org.mule.runtime.internal.dsl.DslConstants.CORE_PREFIX;
import org.mule.runtime.api.component.ComponentIdentifier;
import org.mule.runtime.api.component.location.Location;
import org.mule.runtime.api.util.Reference;
import org.mule.runtime.config.api.dsl.model.ComponentBuildingDefinitionRegistry;
import org.mule.runtime.config.api.dsl.processor.AbstractAttributeDefinitionVisitor;
import org.mule.runtime.config.internal.BeanDependencyResolver;
import org.mule.runtime.config.internal.model.ApplicationModel;
import org.mule.runtime.config.internal.model.ComponentModel;
import org.mule.runtime.dsl.api.component.ComponentBuildingDefinition;
import org.mule.runtime.dsl.api.component.KeyAttributeDefinitionPair;
import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
public class ConfigurationDependencyResolver implements BeanDependencyResolver {
private final ApplicationModel applicationModel;
private final ComponentBuildingDefinitionRegistry componentBuildingDefinitionRegistry;
private List missingElementNames = new ArrayList<>();
/**
* Creates a new instance associated to a complete {@link ApplicationModel}.
*
* @param applicationModel the artifact {@link ApplicationModel}.
* @param componentBuildingDefinitionRegistry the registry to find the
* {@link org.mule.runtime.dsl.api.component.ComponentBuildingDefinition}s associated to each {@link ComponentModel} that
* must be resolved.
*/
public ConfigurationDependencyResolver(ApplicationModel applicationModel,
ComponentBuildingDefinitionRegistry componentBuildingDefinitionRegistry) {
this.applicationModel = applicationModel;
this.componentBuildingDefinitionRegistry = componentBuildingDefinitionRegistry;
}
private Set resolveComponentModelDependencies(ComponentModel componentModel) {
final Set otherRequiredGlobalComponents = resolveComponentDependencies(componentModel);
return findComponentModelsDependencies(otherRequiredGlobalComponents);
}
protected Set resolveComponentDependencies(ComponentModel requestedComponentModel) {
Set otherDependencies = new HashSet<>();
requestedComponentModel.getInnerComponents()
.stream().forEach(childComponent -> otherDependencies.addAll(resolveComponentDependencies(childComponent)));
final Set parametersReferencingDependencies = new HashSet<>();
componentBuildingDefinitionRegistry.getBuildingDefinition(requestedComponentModel.getIdentifier())
.ifPresent(buildingDefinition -> buildingDefinition.getAttributesDefinitions()
.stream().forEach(attributeDefinition -> {
attributeDefinition.accept(new AbstractAttributeDefinitionVisitor() {
@Override
public void onMultipleValues(KeyAttributeDefinitionPair[] definitions) {
stream(definitions)
.forEach(keyAttributeDefinitionPair -> keyAttributeDefinitionPair.getAttributeDefinition().accept(this));
}
@Override
public void onReferenceSimpleParameter(String reference) {
parametersReferencingDependencies.add(reference);
}
});
}));
for (String parametersReferencingDependency : parametersReferencingDependencies) {
if (requestedComponentModel.getParameters().containsKey(parametersReferencingDependency)) {
appendTopLevelDependency(otherDependencies, requestedComponentModel, parametersReferencingDependency);
}
}
// Special cases for flow-ref and configuration
if (isCoreComponent(requestedComponentModel.getIdentifier(), "flow-ref")) {
appendTopLevelDependency(otherDependencies, requestedComponentModel, "name");
} else if (isAggregatorComponent(requestedComponentModel, "aggregatorName")) {
// TODO (MULE-14429): use extensionModel to get the dependencies instead of ComponentBuildingDefinition to solve cases like this (flow-ref)
String name = requestedComponentModel.getParameters().get("aggregatorName");
DependencyNode dependency = new DependencyNode(name, INNER);
if (applicationModel.findNamedElement(name).isPresent()) {
otherDependencies.add(dependency);
} else {
missingElementNames.add(dependency);
}
} else if (isCoreComponent(requestedComponentModel.getIdentifier(), "configuration")) {
appendTopLevelDependency(otherDependencies, requestedComponentModel, "defaultErrorHandler-ref");
}
return otherDependencies;
}
protected Set findComponentModelsDependencies(Set componentModelNames) {
Set componentsToSearchDependencies = new HashSet<>(componentModelNames);
Set foundDependencies = new LinkedHashSet<>();
Set alreadySearchedDependencies = new HashSet<>();
do {
componentsToSearchDependencies.addAll(foundDependencies);
for (DependencyNode dependencyNode : componentsToSearchDependencies) {
if (!alreadySearchedDependencies.contains(dependencyNode)) {
alreadySearchedDependencies.add(dependencyNode);
foundDependencies.addAll(resolveComponentDependencies(findRequiredComponentModel(dependencyNode.getComponentName())));
}
}
foundDependencies.addAll(componentModelNames);
} while (!foundDependencies.containsAll(componentsToSearchDependencies));
return foundDependencies;
}
private void appendTopLevelDependency(Set otherDependencies, ComponentModel requestedComponentModel,
String parametersReferencingDependency) {
DependencyNode dependency =
new DependencyNode(requestedComponentModel.getParameters().get(parametersReferencingDependency), TOP_LEVEL);
if (applicationModel.findTopLevelNamedComponent(dependency.getComponentName()).isPresent()) {
otherDependencies.add(dependency);
} else {
missingElementNames.add(dependency);
}
}
private boolean isCoreComponent(ComponentIdentifier componentIdentifier, String name) {
return componentIdentifier.getNamespace().equals(CORE_PREFIX) && componentIdentifier.getName().equals(name);
}
private boolean isAggregatorComponent(ComponentModel componentModel, String referenceNameParameter) {
return componentModel.getIdentifier().getNamespace().equals("aggregators")
&& componentModel.getParameters().containsKey(referenceNameParameter);
}
private ComponentModel findRequiredComponentModel(String name) {
return applicationModel.findNamedElement(name)
.orElseThrow(() -> new NoSuchComponentModelException(createStaticMessage("No named component with name " + name)));
}
protected ComponentModel findRequiredComponentModel(Location location) {
final Reference foundComponentModelReference = new Reference<>();
Optional globalComponent = applicationModel.findTopLevelNamedComponent(location.getGlobalName());
globalComponent.ifPresent(componentModel -> findComponentWithLocation(componentModel, location)
.ifPresent(foundComponentModel -> foundComponentModelReference.set(foundComponentModel)));
if (foundComponentModelReference.get() == null) {
throw new NoSuchComponentModelException(createStaticMessage("No object found at location " + location.toString()));
}
return foundComponentModelReference.get();
}
private Optional findComponentWithLocation(ComponentModel componentModel, Location location) {
if (componentModel.getComponentLocation().getLocation().equals(location.toString())) {
return of(componentModel);
}
for (ComponentModel childComponent : componentModel.getInnerComponents()) {
Optional foundComponent = findComponentWithLocation(childComponent, location);
if (foundComponent.isPresent()) {
return foundComponent;
}
}
return empty();
}
/**
* @param componentName the name attribute value of the component
* @return the dependencies of the component with component name {@code #componentName}. An empty collection if there is no
* component with such name.
*/
//TODO (MULE-14453: When creating ApplicationModel and ComponentModels inner beans should have a name so they can be later retrieved)
public Collection resolveComponentDependencies(String componentName) {
try {
ComponentModel requiredComponentModel = findRequiredComponentModel(componentName);
return resolveComponentModelDependencies(requiredComponentModel)
.stream()
.filter(dependencyNode -> dependencyNode.isTopLevel())
.map(dependencyNode -> dependencyNode.getComponentName())
.collect(toList());
} catch (NoSuchComponentModelException e) {
return emptyList();
}
}
public ApplicationModel getApplicationModel() {
return applicationModel;
}
@Override
public Collection