Skip to content

Commit

Permalink
Merge pull request #27317 from mkouba/issue-26723
Browse files Browse the repository at this point in the history
Dev UI - bean dependency graph improvements
  • Loading branch information
mkouba authored Aug 17, 2022
2 parents 233f8b8 + 1789c57 commit 9381f2e
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.dev.console.DevConsoleManager;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem;
import io.quarkus.devconsole.spi.DevConsoleTemplateInfoBuildItem;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

public class ArcDevConsoleProcessor {

Expand Down Expand Up @@ -102,8 +106,9 @@ public void transform(TransformationContext transformationContext) {
}

@BuildStep(onlyIf = IsDevelopment.class)
public DevConsoleTemplateInfoBuildItem collectBeanInfo(ValidationPhaseBuildItem validationPhaseBuildItem,
CompletedApplicationClassPredicateBuildItem predicate) {
public void collectBeanInfo(ValidationPhaseBuildItem validationPhaseBuildItem,
CompletedApplicationClassPredicateBuildItem predicate, BuildProducer<DevConsoleTemplateInfoBuildItem> templates,
BuildProducer<DevConsoleRouteBuildItem> routes) {
BeanDeploymentValidator.ValidationContext validationContext = validationPhaseBuildItem.getContext();
DevBeanInfos beanInfos = new DevBeanInfos();
for (BeanInfo bean : validationContext.beans()) {
Expand Down Expand Up @@ -157,9 +162,53 @@ public DevConsoleTemplateInfoBuildItem collectBeanInfo(ValidationPhaseBuildItem
}

beanInfos.sort();
return new DevConsoleTemplateInfoBuildItem("devBeanInfos", beanInfos);
templates.produce(new DevConsoleTemplateInfoBuildItem("devBeanInfos", beanInfos));

routes.produce(new DevConsoleRouteBuildItem("toggleBeanDescription", "POST", new Handler<RoutingContext>() {
@Override
public void handle(RoutingContext context) {
Object val = DevConsoleManager.getGlobal(BEAN_DESCRIPTION);
if (val != null && val.equals("simple")) {
val = "full";
} else {
val = "simple";
}
DevConsoleManager.setGlobal(BEAN_DESCRIPTION, val);
context.response()
.putHeader("location", "beanDependencyGraph?beanId=" + context.request().getParam("beanId"))
.setStatusCode(302).end();
}
}));

routes.produce(new DevConsoleRouteBuildItem("setMaxDependencyLevel", "POST", new Handler<RoutingContext>() {
@Override
public void handle(RoutingContext context) {
context.request().setExpectMultipart(true);
context.request().endHandler(new Handler<Void>() {
@Override
public void handle(Void ignore) {
Integer val = null;
try {
val = Integer.parseInt(context.request().getFormAttribute("maxDepLevel"));
} catch (NumberFormatException ignored) {
}
if (val != null) {
DevConsoleManager.setGlobal(MAX_DEPENDENCY_LEVEL, val);
}
context.response()
.putHeader("location", "beanDependencyGraph?beanId=" + context.request().getParam("beanId"))
.setStatusCode(302).end();
}
});

}
}));
}

static final String BEAN_DESCRIPTION = "io.quarkus.arc.beanDescription";
static final String MAX_DEPENDENCY_LEVEL = "io.quarkus.arc.maxDependencyLevel";
static final int DEFAULT_MAX_DEPENDENCY_LEVEL = 10;

private boolean isAdditionalBeanDefiningAnnotationOn(ClassInfo beanClass,
List<BeanDefiningAnnotationBuildItem> beanDefiningAnnotations) {
for (BeanDefiningAnnotationBuildItem beanDefiningAnnotation : beanDefiningAnnotations) {
Expand All @@ -176,31 +225,31 @@ DependencyGraph buildDependencyGraph(BeanInfo bean, ValidationContext validation
Map<BeanInfo, List<InjectionPointInfo>> directDependents) {
Set<DevBeanInfo> nodes = new HashSet<>();
Set<Link> links = new HashSet<>();
addNodesDependencies(bean, nodes, links, bean, devBeanInfos);
addNodesDependents(bean, nodes, links, bean, allInjectionPoints, declaringToProducers, resolver, devBeanInfos,
addNodesDependencies(0, bean, nodes, links, bean, devBeanInfos);
addNodesDependents(0, bean, nodes, links, bean, allInjectionPoints, declaringToProducers, resolver, devBeanInfos,
directDependents);
return new DependencyGraph(nodes, links);
}

void addNodesDependencies(BeanInfo root, Set<DevBeanInfo> nodes, Set<Link> links, BeanInfo bean,
void addNodesDependencies(int level, BeanInfo root, Set<DevBeanInfo> nodes, Set<Link> links, BeanInfo bean,
DevBeanInfos devBeanInfos) {
if (nodes.add(devBeanInfos.getBean(bean.getIdentifier()))) {
if (bean.isProducerField() || bean.isProducerMethod()) {
links.add(Link.producer(bean.getIdentifier(), bean.getDeclaringBean().getIdentifier()));
addNodesDependencies(root, nodes, links, bean.getDeclaringBean(), devBeanInfos);
links.add(Link.producer(bean.getIdentifier(), bean.getDeclaringBean().getIdentifier(), level));
addNodesDependencies(level + 1, root, nodes, links, bean.getDeclaringBean(), devBeanInfos);
}
for (InjectionPointInfo injectionPoint : bean.getAllInjectionPoints()) {
BeanInfo resolved = injectionPoint.getResolvedBean();
if (resolved != null && !resolved.equals(bean)) {
links.add(Link.dependency(root.equals(bean), bean.getIdentifier(), resolved.getIdentifier()));
links.add(Link.dependency(bean.getIdentifier(), resolved.getIdentifier(), level));
// add transient dependencies
addNodesDependencies(root, nodes, links, injectionPoint.getResolvedBean(), devBeanInfos);
addNodesDependencies(level + 1, root, nodes, links, injectionPoint.getResolvedBean(), devBeanInfos);
}
}
}
}

void addNodesDependents(BeanInfo root, Set<DevBeanInfo> nodes, Set<Link> links, BeanInfo bean,
void addNodesDependents(int level, BeanInfo root, Set<DevBeanInfo> nodes, Set<Link> links, BeanInfo bean,
List<InjectionPointInfo> injectionPoints, Map<BeanInfo, List<BeanInfo>> declaringToProducers, BeanResolver resolver,
DevBeanInfos devBeanInfos, Map<BeanInfo, List<InjectionPointInfo>> directDependents) {
List<InjectionPointInfo> direct = directDependents.get(bean);
Expand Down Expand Up @@ -228,23 +277,23 @@ void addNodesDependents(BeanInfo root, Set<DevBeanInfo> nodes, Set<Link> links,
BeanInfo dependent = ip.getTargetBean().get();
Link link;
if (ip.getResolvedBean() == null) {
link = Link.lookup(dependent.getIdentifier(), bean.getIdentifier());
link = Link.lookup(dependent.getIdentifier(), bean.getIdentifier(), level);
} else {
link = Link.dependent(root.equals(bean), dependent.getIdentifier(), bean.getIdentifier());
link = Link.dependent(dependent.getIdentifier(), bean.getIdentifier(), level);
}
links.add(link);
if (nodes.add(devBeanInfos.getBean(dependent.getIdentifier()))) {
// add transient dependents
addNodesDependents(root, nodes, links, dependent, injectionPoints, declaringToProducers, resolver,
addNodesDependents(level + 1, root, nodes, links, dependent, injectionPoints, declaringToProducers, resolver,
devBeanInfos, directDependents);
}
}

for (BeanInfo producer : declaringToProducers.getOrDefault(bean, Collections.emptyList())) {
links.add(Link.producer(producer.getIdentifier(), bean.getIdentifier()));
links.add(Link.producer(producer.getIdentifier(), bean.getIdentifier(), level));
if (nodes.add(devBeanInfos.getBean(producer.getIdentifier()))) {
// add transient dependents
addNodesDependents(root, nodes, links, producer, injectionPoints, declaringToProducers, resolver,
addNodesDependents(level + 1, root, nodes, links, producer, injectionPoints, declaringToProducers, resolver,
devBeanInfos, directDependents);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,70 @@
package io.quarkus.arc.deployment.devconsole;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

public class DependencyGraph {

public final Set<DevBeanInfo> nodes;
public final Set<Link> links;
public final int maxLevel;

public DependencyGraph(Set<DevBeanInfo> nodes, Set<Link> links) {
this.nodes = nodes;
this.links = links;
this.maxLevel = links.stream().mapToInt(l -> l.level).max().orElse(0);
}

DependencyGraph forLevel(int level) {
// Filter out links first
Set<Link> newLinks = new HashSet<>();
Set<DevBeanInfo> newNodes = new HashSet<>();
Set<String> usedIds = new HashSet<>();
for (Link link : links) {
if (link.level <= level) {
newLinks.add(link);
usedIds.add(link.source);
usedIds.add(link.target);
}
}
// Now keep only nodes for which a link exists...
for (DevBeanInfo node : nodes) {
if (usedIds.contains(node.getId())) {
newNodes.add(node);
}
}
return new DependencyGraph(newNodes, newLinks);
}

public static class Link {

static Link dependent(boolean direct, String source, String target) {
return new Link(source, target, direct ? "directDependent" : "dependency");
static Link dependent(String source, String target, int level) {
return new Link(source, target, level == 0 ? "directDependent" : "dependency", level);
}

static Link dependency(boolean direct, String source, String target) {
return new Link(source, target, direct ? "directDependency" : "dependency");
static Link dependency(String source, String target, int level) {
return new Link(source, target, level == 0 ? "directDependency" : "dependency", level);
}

static Link lookup(String source, String target) {
return new Link(source, target, "lookup");
static Link lookup(String source, String target, int level) {
return new Link(source, target, "lookup", level);
}

static Link producer(String source, String target) {
return new Link(source, target, "producer");
static Link producer(String source, String target, int level) {
return new Link(source, target, "producer", level);
}

public final String source;
public final String target;
public final String type;
public final int level;

public Link(String source, String target, String type) {
public Link(String source, String target, String type, int level) {
this.source = source;
this.target = target;
this.type = type;
this.level = level;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,18 +162,43 @@ public List<String> getInterceptors() {
}

public String getDescription() {
return description(false);
}

public String getSimpleDescription() {
return description(true);
}

private String description(boolean simple) {
String typeInfo = typeInfo(simple);
switch (kind) {
case CLASS:
return providerType.toString();
case FIELD:
return declaringClass.toString() + "#" + memberName;
return typeInfo + "#" + memberName;
case METHOD:
return declaringClass.toString() + "#" + memberName + "()";
return typeInfo + "#" + memberName + "()";
case SYNTHETIC:
return "Synthetic: " + providerType.toString();
return "Synthetic: " + typeInfo;
default:
return typeInfo;
}
}

public String typeInfo(boolean simple) {
String type;
switch (kind) {
case FIELD:
case METHOD:
type = declaringClass.toString();
break;
default:
return providerType.toString();
type = providerType.toString();
break;
}
if (simple) {
int idx = type.lastIndexOf(".");
return idx != -1 && type.length() > 1 ? type.substring(idx + 1) : type;
}
return type;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import java.util.List;
import java.util.Map;

import io.quarkus.dev.console.DevConsoleManager;

public class DevBeanInfos {

private final List<DevBeanInfo> beans;
Expand Down Expand Up @@ -56,6 +58,15 @@ public List<DevDecoratorInfo> getRemovedDecorators() {
return removedDecorators;
}

public String getBeanDescription() {
return DevConsoleManager.getGlobal(ArcDevConsoleProcessor.BEAN_DESCRIPTION);
}

public int getMaxDependencyLevel() {
Integer val = DevConsoleManager.getGlobal(ArcDevConsoleProcessor.MAX_DEPENDENCY_LEVEL);
return val != null ? val : ArcDevConsoleProcessor.DEFAULT_MAX_DEPENDENCY_LEVEL;
}

public DevBeanInfo getBean(String id) {
for (DevBeanInfo bean : beans) {
if (bean.getId().equals(id)) {
Expand All @@ -75,7 +86,12 @@ public DevInterceptorInfo getInterceptor(String id) {
}

public DependencyGraph getDependencyGraph(String beanId) {
return dependencyGraphs.get(beanId);
Integer maxLevel = DevConsoleManager.getGlobal(ArcDevConsoleProcessor.MAX_DEPENDENCY_LEVEL);
if (maxLevel == null) {
maxLevel = ArcDevConsoleProcessor.DEFAULT_MAX_DEPENDENCY_LEVEL;
}
DependencyGraph graph = dependencyGraphs.get(beanId);
return graph.maxLevel <= maxLevel ? graph : graph.forLevel(maxLevel);
}

public int getRemovedComponents() {
Expand Down
Loading

0 comments on commit 9381f2e

Please sign in to comment.