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 006package fc.util; 007 008import java.net.*; 009import java.security.*; 010 011public final class ClassUtil 012{ 013static final String nl = fc.io.IOUtil.LINE_SEP; 014static final boolean dbg = false; 015 016/** 017Returns classloader information about the specified object 018*/ 019public static String getClassLoaderInfo(final Object obj) 020 { 021 Argcheck.notnull(obj, "specified obj param was null"); 022 return getClassLoaderInfo(obj.getClass()); 023 } 024 025/** 026Returns classloader information about the specified class. 027*/ 028public 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 */ 080private static class CurrentClassGetter extends SecurityManager { 081 public String getClassName() { 082 return getClassContext()[2].getName(); 083 } 084 } 085 086/** 087This method can be called from either a instance <b>or</b> a static method. 088 089Returns 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 092instance methods, it is the runtime virtual class. For static it is the class name where the method 093was declared (static methods are not virtual and bound at compile time). 094 095For example: 096<blockquote><pre><tt> 097Class A 098 { 099 static void foo(...) { 100 System.out.print(ClassUtil.getClassName()); 101 } 102 } 103 104public class B extends A 105 { 106 } 107 108% java B 109will print: 110 A (NOT B) 111</pre></tt></blockquote> 112*/ 113public static String getClassName() 114 { 115 return new CurrentClassGetter().getClassName(); 116 } 117 118 119/** 120Returns the fully qualified name of the class that contained the <tt>main()</tt> method used to 121invoke the application. 122 123<b>Note: </b> this method must be called from the same thread (the "main" thread) that started the 124application. Newly started threads that call this method will instead obtain the class name 125containing that thread's <tt>run()</tt> method. 126**/ 127public 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/** 137Returns true is the specified class is a system/JDK class. This method should work, but given 138the 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*/ 142public 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 153 ==============ClassLoaderTest.java== 154 public static void main (String args[]) { 155 new ClassLoaderTest(); 156 } 157 public void printClassLoader(Object obj) { 158 System.out.println(obj); 159 Class c = obj.getClass(); 160 System.out.println(c); 161 System.out.println(c.getClassLoader()); 162 } 163 public ClassLoaderTest() { 164 printClassLoader(ClassLoaderTest.class); 165//====> class ClassLoaderTest => class java.lang.Class => null 166 167 printClassLoader(this); 168//====> ClassLoaderTest@4e25154f => class ClassLoaderTest => sun.misc.Launcher$AppClassLoader@15db974 169 } 170 ============== 171 */ 172 173 //all of the below return null. so this is true for java.* as well as javax.* at least. 174 //System.out.println(String.class.getClassLoader()); 175 //System.out.println(Object.class.getClassLoader()); 176 //System.out.println(java.sql.Connection.class.getClassLoader()); 177 //System.out.println(javax.sql.PooledConnection.class.getClassLoader()); 178 179 //boostrap classloaders are null (written in JDK C). This is an 180 //artifact of the JDK implementation, the spec doesn't mandate it. 181 182 if (c.getClassLoader() == null) { 183 return true; 184 } 185 186 return false; 187 } 188 189 190/* 191For regular files: 192 protection domain path: 193 /private/tmp/ 194 resource URL path to loaded class: 195 file:/private/tmp/test.class 196 197For jar files: 198 protection domain path: 199 /private/tmp/foo.jar 200 resource URL path to loaded class: 201 jar:file:/private/tmp/foo.jar!/test.class 202 203*/ 204 205 206/** 207Returns either the directory or jar file from which the specified class was loaded 208<p> 209This does not work for system/bootstrap classes and will return <tt>null</tt> for system classes. 210 211@param caller the {@link java.lang.Class} class to be looked up 212*/ 213public static String getLoadedFromPath(final Class caller) 214 { 215 //param has to be class, cannot be Object, static compile time binding 216 217 final ProtectionDomain pd = caller.getProtectionDomain(); 218 if (pd == null) { //should not be null for system/boostrap classes but just to be sure 219 if (dbg) System.out.println("ProtectionDomain for " + caller + " was null"); 220 return null; 221 } 222 223 final CodeSource cs = pd.getCodeSource(); 224 if (cs == null) { //is definitely null for system/boostrap classes 225 if (dbg) System.out.println("CodeSource for " + caller + " was null"); 226 return null; 227 } 228 229 final String path = cs.getLocation().getPath(); 230 return path; 231 } 232 233/** 234Returns the right most <b>parent</b> directory from which the actual class was loaded. This is the leaf directory where 235the actual class is located. 236<p> 237For classes loaded from other sources (jar, etc), the return value will be null. 238<p> 239This also does not work for system/bootstrap classes (they are typically loaded from jars) either (those will 240return null also) 241<p> 242@param caller the {@link java.lang.Class} class to be looked up 243*/ 244public static String getParentDir(final Class caller) 245 { 246 final String classname = StringUtil.fileName(caller.getName().replace('.', '/')) + ".class"; 247 final URL location = caller.getResource(classname); 248 249 if (location == null) return null; 250 if (dbg) System.out.println("protocol: " + location.getProtocol()); 251 252 String protocol = location.getProtocol(); 253 if (protocol == null) return null; 254 255 if (protocol.equalsIgnoreCase("file")) { 256 return StringUtil.dirName(location.getFile()); 257 } 258 259 return null; 260 } 261 262 263/** 264Returns the <b>parent</b> directory from which the actual class was loaded. This is the leaf directory where 265the actual class is located. 266<p> 267For classes loaded from other sources (jar, etc), a URL will be returned. For jar URL's, the 268URL.getProtocol() method returns "jar", so that can be useful for further processing as needed. 269<p> 270@param caller the {@link java.lang.Class} class to be looked up 271*/ 272public static URL getParentDirAsURL(final Class caller) 273 { 274 final String classname = StringUtil.fileName(caller.getName().replace('.', '/')) + ".class"; 275 final URL location = caller.getResource(classname); 276 final String parent = StringUtil.dirName(location.toString()); 277 if (dbg) System.out.println(parent); 278 URL url = null; 279 try { 280 url = new URL(parent); 281 } 282 catch (Exception e) { 283 System.err.println(fc.io.IOUtil.throwableToString(e)); 284 } 285 return url; 286 } 287 288public static void main (String args[]) 289 { 290 System.out.println(ClassUtil.getClassLoaderInfo(ClassUtil.class)); 291 292 Class obj = new Object().getClass(); 293 294 System.out.println(ClassUtil.getClassLoaderInfo(obj)); 295 System.out.println("getClassName(): " + getClassName()); 296 System.out.println("getMainClassName(): " + getMainClassName()); 297 298 System.out.println(); 299 300 System.out.println("-------- these should return null --(system classes) ------"); 301 302 test(obj); 303 304 System.out.println("-------- these should return a value (either jar or directory) ------"); 305 test (ClassUtil.class); 306 307 System.out.println("-------- these should return a value (either jar or directory) ------"); 308 test(new org.json.JSONObject().getClass()); 309 } 310 311private static void test (Class obj) 312 { 313 System.out.println(obj); 314 System.out.println("isSystemClass: " + isSystemClass(obj)); 315 System.out.println("getLoadedFromPath: " + getLoadedFromPath(obj)); 316 System.out.println("getParentDir: " + getParentDir(obj)); 317 System.out.println("getParentDirAsURL: " + getParentDirAsURL(obj)); 318 System.out.println(); 319 } 320} 321 322 323