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