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 }