001    /*--------------------------------------------------------------------------+
002    $Id: ClassLoaderGraphCreator.java 26268 2010-02-18 10:44:30Z juergens $
003    |                                                                          |
004    | Copyright 2005-2010 Technische Universitaet Muenchen                     |
005    |                                                                          |
006    | Licensed under the Apache License, Version 2.0 (the "License");          |
007    | you may not use this file except in compliance with the License.         |
008    | You may obtain a copy of the License at                                  |
009    |                                                                          |
010    |    http://www.apache.org/licenses/LICENSE-2.0                            |
011    |                                                                          |
012    | Unless required by applicable law or agreed to in writing, software      |
013    | distributed under the License is distributed on an "AS IS" BASIS,        |
014    | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015    | See the License for the specific language governing permissions and      |
016    | limitations under the License.                                           |
017    +--------------------------------------------------------------------------*/
018    package edu.tum.cs.commons.reflect;
019    
020    import static edu.tum.cs.commons.string.StringUtils.CR;
021    
022    import java.util.ArrayList;
023    import java.util.List;
024    
025    import edu.tum.cs.commons.assertion.CCSMPre;
026    import edu.tum.cs.commons.collections.IdentityHashSet;
027    import edu.tum.cs.commons.collections.UniqueIdManager;
028    import edu.tum.cs.commons.color.ECCSMColor;
029    import edu.tum.cs.commons.string.StringUtils;
030    
031    /**
032     * Create a DOT graph that contains the provided object, their defining classes,
033     * the classes' class loaders and the parent class loaders up to the bootstrap
034     * class loader. This is very useful for debugging class loader-related bugs.
035     * 
036     * @author deissenb
037     * @author $Author: juergens $
038     * @version $Rev: 26268 $
039     * @levd.rating GREEN Hash: 33DD5960012AF0E7504D06872EC9697A
040     */
041    public class ClassLoaderGraphCreator {
042    
043            /** The header for DOT files. */
044            public final static String HEADER = "digraph G {" + CR
045                            + "  edge [  fontname = \"Helvetica\"," + CR
046                            + "          color = \"#639CCE\", fontsize = 8 ];" + CR
047                            + "  node [  shape = polygon," + CR + "          sides = 4," + CR
048                            + "          color = \"#639CCE\"," + CR
049                            + "          fontname = \"Helvetica\"," + CR
050                            + "          fontsize    = 9," + CR + "          height=0.25];"
051                            + CR;
052    
053            /** Manager to create unique node ids. */
054            private final UniqueIdManager<Object> idManager = new UniqueIdManager<Object>();
055    
056            /** Objects included in the graph. */
057            private final IdentityHashSet<Object> objects = new IdentityHashSet<Object>();
058    
059            /** Classes included in the graph. */
060            private final IdentityHashSet<Class<?>> classes = new IdentityHashSet<Class<?>>();
061    
062            /** Class loaders. */
063            private final IdentityHashSet<ClassLoader> classLoaders = new IdentityHashSet<ClassLoader>();
064    
065            /**
066             * Create new graph creator.
067             * 
068             * @param objects
069             *            the objects provided may be arbitrary objects or {@link Class}
070             *            -objects.
071             */
072            public ClassLoaderGraphCreator(Object... objects) {
073                    for (Object object : objects) {
074                            if (object instanceof Class<?>) {
075                                    addClass((Class<?>) object);
076                            } else {
077                                    addObject(object);
078                            }
079                    }
080            }
081    
082            /** Add a object. */
083            @SuppressWarnings("null")
084            public void addObject(Object object) {
085                    CCSMPre.isFalse(object == null, "Object may not be null.");
086                    objects.add(object);
087                    addClass(object.getClass());
088            }
089    
090            /** Add a class. */
091            public void addClass(Class<?> clazz) {
092                    CCSMPre.isFalse(clazz == null, "Class may not be null.");
093                    classes.add(clazz);
094                    classLoaders.addAll(getClassLoaders(clazz));
095            }
096    
097            /**
098             * Determine class loader hierarchy of a class. This includes the
099             * <code>null</code> value to describe the bootstrap loader.
100             */
101            private static List<ClassLoader> getClassLoaders(Class<?> clazz) {
102                    ArrayList<ClassLoader> loaders = new ArrayList<ClassLoader>();
103    
104                    ClassLoader loader = clazz.getClassLoader();
105    
106                    while (loader != null) {
107                            loaders.add(loader);
108                            loader = loader.getParent();
109                    }
110    
111                    loaders.add(null);
112    
113                    return loaders;
114            }
115    
116            /** Create graph. */
117            public String createClassLoaderGraph() {
118                    StringBuilder builder = new StringBuilder();
119                    builder.append(HEADER + CR);
120                    builder.append(createVertices());
121                    builder.append(createEdges());
122                    builder.append("}" + CR);
123    
124                    return builder.toString();
125            }
126    
127            /** Create edge. */
128            private String createEdges() {
129                    StringBuilder result = new StringBuilder();
130    
131                    for (Object object : objects) {
132                            appendEdge(result, object, object.getClass());
133                    }
134    
135                    for (Class<?> clazz : classes) {
136                            appendEdge(result, clazz, clazz.getClassLoader());
137                    }
138    
139                    for (ClassLoader loader : classLoaders) {
140                            if (loader != null) {
141                                    appendEdge(result, loader, loader.getParent());
142                            }
143                    }
144    
145                    return result.toString();
146            }
147    
148            /** Append edge if it was not added before. */
149            private void appendEdge(StringBuilder builder, Object start, Object end) {
150                    String edge = makeId(start) + " -> " + makeId(end) + ";" + CR;
151                    builder.append(edge);
152            }
153    
154            /** Creates vertices. */
155            private String createVertices() {
156                    StringBuilder result = new StringBuilder();
157    
158                    for (Object object : objects) {
159                            result.append(createVertex(object, ECCSMColor.GREEN));
160                    }
161    
162                    for (Class<?> clazz : classes) {
163                            result.append(createVertex(clazz, ECCSMColor.RED));
164                    }
165    
166                    for (ClassLoader loader : classLoaders) {
167                            result.append(createVertex(loader, ECCSMColor.BLUE));
168                    }
169    
170                    return result.toString();
171            }
172    
173            /** Creates vertex. */
174            private String createVertex(Object node, ECCSMColor color) {
175                    StringBuilder result = new StringBuilder();
176                    result.append(makeId(node));
177                    result.append(" [label=\"" + makeLabel(node) + "\", ");
178                    result.append(" color=\"" + color.getHTMLColorCode() + "\"");
179                    result.append("];" + CR);
180                    return result.toString();
181            }
182    
183            /** Creates a label for a node. */
184            private String makeLabel(Object object) {
185                    String result;
186                    if (object == null) {
187                            result = "Bootstrap Loader";
188                    } else if (object instanceof Class<?>) {
189                            // A class object
190                            result = ((Class<?>) object).getName();
191                    } else if (object instanceof ClassLoader) {
192                            // A class loader object
193                            result = object.toString();
194                    } else {
195                            // A "normal" object
196                            String toString = object.toString();
197    
198                            // Deal with empty toStrings
199                            if (StringUtils.isEmpty(toString)) {
200                                    toString = "instance of " + object.getClass().getName();
201                            }
202                            result = toString;
203                    }
204    
205                    result = StringUtils.replaceLineBreaks(result, " ");
206                    result = result.replace('"', '\'');
207    
208                    return result;
209            }
210    
211            /** Determines unique id for node. */
212            private String makeId(Object object) {
213                    return "id" + idManager.obtainId(object);
214            }
215    }