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