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 }