DEV Community

Query Filter
Query Filter

Posted on

bridge81

package com.yourpackage;

import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.SmartLifecycle;
import java.util.HashSet;
import java.util.Set;

public class SpringVisualizer implements ApplicationContextAware, SmartLifecycle {

    private ConfigurableApplicationContext startContext;
    private boolean isRunning = false;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.startContext = (ConfigurableApplicationContext) applicationContext;
    }

    @Override
    public void start() {
        System.out.println("\n=== GENERATING ESCAPED PROPERTY GRAPH ===");
        generateSafeGraph();
        this.isRunning = true;
    }

    private void generateSafeGraph() {
        StringBuilder dot = new StringBuilder("digraph SafeBeanMap {\n");
        // We use 'Mrecord' for rounded corners and better text alignment
        dot.append("  rankdir=LR; node [shape=Mrecord, style=filled, fillcolor=\"#E1F5FE\", fontname=\"Arial\", fontsize=9];\n");

        Set<String> processed = new HashSet<>();
        ApplicationContext current = this.startContext;

        while (current != null) {
            ConfigurableListableBeanFactory factory = ((ConfigurableApplicationContext) current).getBeanFactory();
            for (String name : factory.getBeanDefinitionNames()) {
                if (processed.add(name)) {
                    BeanDefinition def = factory.getBeanDefinition(name);

                    // Header: Bean Name
                    StringBuilder label = new StringBuilder();
                    label.append("{ ").append(clean(name)); 

                    // Body: Properties
                    PropertyValue[] pvs = def.getPropertyValues().getPropertyValues();
                    if (pvs.length > 0) {
                        label.append(" | ");
                        for (PropertyValue pv : pvs) {
                            String val = String.valueOf(pv.getValue());
                            // Limit length to prevent massive boxes
                            if (val.length() > 40) val = val.substring(0, 37) + "...";
                            label.append(clean(pv.getName())).append("=").append(clean(val)).append("\\l");
                        }
                    }
                    label.append(" }");

                    // Node Color Logic
                    String color = "#E1F5FE";
                    if (name.toLowerCase().contains("comet")) color = "#FFF9C4";
                    if (name.toLowerCase().contains("cpls")) color = "#C8E6C9";

                    dot.append(String.format("  \"%s\" [label=\"%s\", fillcolor=\"%s\"];\n", clean(name), label.toString(), color));

                    // Edges
                    for (String dep : factory.getDependenciesForBean(name)) {
                        dot.append(String.format("  \"%s\" -> \"%s\";\n", clean(name), clean(dep)));
                    }
                }
            }
            current = current.getParent();
        }
        dot.append("}\n");

        System.out.println("----- COPY START -----");
        System.out.println(dot.toString());
        System.out.println("----- COPY END -----");
    }

    /**
     * Escapes characters that break Graphviz Record shapes
     */
    private String clean(String input) {
        if (input == null) return "null";
        return input.replace("\"", "'")
                    .replace("{", "\\{")
                    .replace("}", "\\}")
                    .replace("|", "\\|")
                    .replace("<", "\\<")
                    .replace(">", "\\>")
                    .replace("\n", " ")
                    .replace("\r", " ");
    }

    @Override public int getPhase() { return Integer.MAX_VALUE; }
    @Override public boolean isAutoStartup() { return true; }
    @Override public void stop() { this.isRunning = false; }
    @Override public boolean isRunning() { return this.isRunning; }
    @Override public void stop(Runnable callback) { callback.run(); }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)