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