001 // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002 // The Molly framework is freely distributable under the terms of an 003 // MIT-style license. For details, see the molly pages web site at: 004 // http://www.mollypages.org/. Use, modify, have fun ! 005 006 package fc.util; 007 008 import java.net.*; 009 import java.security.*; 010 011 public final class ClassUtil 012 { 013 static final String nl = fc.io.IOUtil.LINE_SEP; 014 static final boolean dbg = false; 015 016 /** 017 Returns classloader information about the specified object 018 */ 019 public static String getClassLoaderInfo(final Object obj) 020 { 021 Argcheck.notnull(obj, "specified obj param was null"); 022 return getClassLoaderInfo(obj.getClass()); 023 } 024 025 /** 026 Returns classloader information about the specified class. 027 */ 028 public static String getClassLoaderInfo(final Class c) 029 { 030 Argcheck.notnull(c, "specified class param was null"); 031 032 ClassLoader cl = c.getClassLoader(); 033 034 StringBuffer buf = new StringBuffer(512); 035 036 buf.append("============== Classloader Information =================="); 037 buf.append(nl); 038 039 buf.append("System classloader = "); 040 buf.append(ClassLoader.getSystemClassLoader()); 041 buf.append(nl); 042 043 Thread t = Thread.currentThread(); 044 buf.append("Current thread = ").append(t); 045 buf.append(nl); 046 buf.append("Current thread context classloader = "); 047 buf.append(t.getContextClassLoader()); 048 buf.append(nl); 049 050 buf.append("Class loader hierarchy: "); 051 buf.append(nl); 052 buf.append("[").append(c); 053 buf.append("][package:"); 054 Package p = c.getPackage(); 055 buf.append(p != null ? p.getName() : "N/A"); 056 buf.append("]"); 057 058 buf.append("/ClassID:"); 059 buf.append(System.identityHashCode(cl)); 060 buf.append(" loaded by:"); 061 buf.append(nl); 062 063 String pad = " "; 064 while (cl != null) { 065 buf.append(pad); 066 buf.append(cl); 067 buf.append(nl); 068 cl = cl.getParent(); 069 pad += " "; 070 } 071 buf.append(pad + "null (bootstrap class loader)"); 072 buf.append(nl); 073 074 buf.append("========================================================="); 075 buf.append(nl); 076 return buf.toString(); 077 } 078 079 /* to get the class name from a static method */ 080 private static class CurrentClassGetter extends SecurityManager { 081 public String getClassName() { 082 return getClassContext()[2].getName(); 083 } 084 } 085 086 /** 087 This method can be called from either a instance <b>or</b> a static method. 088 089 Returns the class name in which the calling method was declared. 090 <p> 091 <b>Important note:</b> The returned class name is wherever the method is invoked from. For 092 instance methods, it is the runtime virtual class. For static it is the class name where the method 093 was declared (static methods are not virtual and bound at compile time). 094 095 For example: 096 <blockquote><pre><tt> 097 Class A 098 { 099 static void foo(...) { 100 System.out.print(ClassUtil.getClassName()); 101 } 102 } 103 104 public class B extends A 105 { 106 } 107 108 % java B 109 will print: 110 A [NOT B] 111 </pre></tt></blockquote> 112 */ 113 public static String getClassName() 114 { 115 return new CurrentClassGetter().getClassName(); 116 } 117 118 119 /** 120 Returns the fully qualified name of the class that contained the <tt>main()</tt> method used to 121 invoke the application. 122 123 <b>Note: </b> this method must be called from the same thread (the "main" thread) that started the 124 application. Newly started threads that call this method will instead obtain the class name 125 containing that thread's <tt>run()</tt> method. 126 **/ 127 public static String getMainClassName() 128 { 129 final StackTraceElement[] st = new Throwable().getStackTrace(); 130 //System.out.println("stack trace=" + Arrays.asList(st)); 131 final StackTraceElement mainElement = st[st.length-1]; 132 final String className = mainElement.getClassName(); 133 return className; 134 } 135 136 /** 137 Returns true is the specified class is a system/JDK class. This method should work, but given 138 the vagaries of classes, classloaders, etc, there isn't any guarantee. 139 140 @param caller the object <i>or</i> {@link java.lang.Class} Class corresponding to that object 141 */ 142 public static boolean isSystemClass(final Class c) 143 { 144 /* The param HAS to be Class, cannot be Object 145 146 isSystemClass(Object caller) 147 { 148 Class c = caller.getClass(); 149 ... 150 } 151 152 does NOT work, since Object.class is bound to at runtime (and it will always be a bootstrap class) 153 154 static compile time binding for the win! 155 */ 156 157 //all of the below return null. so this is true for java.* as well as javax.* at least. 158 //System.out.println(String.class.getClassLoader()); 159 //System.out.println(Object.class.getClassLoader()); 160 //System.out.println(java.sql.Connection.class.getClassLoader()); 161 //System.out.println(javax.sql.PooledConnection.class.getClassLoader()); 162 163 //boostrap classloaders are null (written in JDK C). This is an 164 //artifact of the JDK implementation, the spec doesn't mandate it. 165 166 if (c.getClassLoader() == null) { 167 return true; 168 } 169 170 return false; 171 } 172 173 174 /* 175 For regular files: 176 protection domain path: 177 /private/tmp/ 178 resource URL path to loaded class: 179 file:/private/tmp/test.class 180 181 For jar files: 182 protection domain path: 183 /private/tmp/foo.jar 184 resource URL path to loaded class: 185 jar:file:/private/tmp/foo.jar!/test.class 186 187 */ 188 189 190 /** 191 Returns either the directory or jar file from which the specified class was loaded 192 <p> 193 This does not work for system/bootstrap classes and will return <tt>null</tt> for system classes. 194 195 @param caller the {@link java.lang.Class} class to be looked up 196 */ 197 public static String getLoadedFromPath(final Class caller) 198 { 199 //param has to be class, cannot be Object, static compile time binding 200 201 final ProtectionDomain pd = caller.getProtectionDomain(); 202 if (pd == null) { //should not be null for system/boostrap classes but just to be sure 203 if (dbg) System.out.println("ProtectionDomain for " + caller + " was null"); 204 return null; 205 } 206 207 final CodeSource cs = pd.getCodeSource(); 208 if (cs == null) { //is definitely null for system/boostrap classes 209 if (dbg) System.out.println("CodeSource for " + caller + " was null"); 210 return null; 211 } 212 213 final String path = cs.getLocation().getPath(); 214 return path; 215 } 216 217 /** 218 Returns the right most <b>parent</b> directory from which the actual class was loaded. This is the leaf directory where 219 the actual class is located. 220 <p> 221 For classes loaded from other sources (jar, etc), the return value will be null. 222 <p> 223 This also does not work for system/bootstrap classes (they are typically loaded from jars) either (those will 224 return null also) 225 <p> 226 @param caller the {@link java.lang.Class} class to be looked up 227 */ 228 public static String getParentDir(final Class caller) 229 { 230 final String classname = StringUtil.fileName(caller.getName().replace('.', '/')) + ".class"; 231 final URL location = caller.getResource(classname); 232 233 if (location == null) return null; 234 if (dbg) System.out.println("protocol: " + location.getProtocol()); 235 236 String protocol = location.getProtocol(); 237 if (protocol == null) return null; 238 239 if (protocol.equalsIgnoreCase("file")) { 240 return StringUtil.dirName(location.getFile()); 241 } 242 243 return null; 244 } 245 246 247 /** 248 Returns the <b>parent</b> directory from which the actual class was loaded. This is the leaf directory where 249 the actual class is located. 250 <p> 251 For classes loaded from other sources (jar, etc), a URL will be returned. For jar URL's, the 252 URL.getProtocol() method returns "jar", so that can be useful for further processing as needed. 253 <p> 254 @param caller the {@link java.lang.Class} class to be looked up 255 */ 256 public static URL getParentDirAsURL(final Class caller) 257 { 258 final String classname = StringUtil.fileName(caller.getName().replace('.', '/')) + ".class"; 259 final URL location = caller.getResource(classname); 260 final String parent = StringUtil.dirName(location.toString()); 261 if (dbg) System.out.println(parent); 262 URL url = null; 263 try { 264 url = new URL(parent); 265 } 266 catch (Exception e) { 267 System.err.println(fc.io.IOUtil.throwableToString(e)); 268 } 269 return url; 270 } 271 272 public static void main (String args[]) 273 { 274 System.out.println(ClassUtil.getClassLoaderInfo(ClassUtil.class)); 275 276 Class obj = new Object().getClass(); 277 278 System.out.println(ClassUtil.getClassLoaderInfo(obj)); 279 System.out.println("getClassName(): " + getClassName()); 280 System.out.println("getMainClassName(): " + getMainClassName()); 281 282 System.out.println(); 283 284 System.out.println("-------- these should return null --(system classes) ------"); 285 286 test(obj); 287 288 System.out.println("-------- these should return a value (either jar or directory) ------"); 289 test (ClassUtil.class); 290 291 System.out.println("-------- these should return a value (either jar or directory) ------"); 292 test(new org.json.JSONObject().getClass()); 293 } 294 295 private static void test (Class obj) 296 { 297 System.out.println(obj); 298 System.out.println("isSystemClass: " + isSystemClass(obj)); 299 System.out.println("getLoadedFromPath: " + getLoadedFromPath(obj)); 300 System.out.println("getParentDir: " + getParentDir(obj)); 301 System.out.println("getParentDirAsURL: " + getParentDirAsURL(obj)); 302 System.out.println(); 303 } 304 } 305 306 307