001: /*
002: * BBN TECHNOLOGIES CORP. PROPRIETARY
003: * Data contained in this document is proprietary to BBN TECHNOLOGIES CORP.
004: * (BBN) or others from whom BBN has acquired such data and shall not be
005: * copied, used or disclosed, in whole or in part, by Northrop Grumman
006: * Space & Mission Systems Corp. (Northrop Grumman) and The Boeing
007: * Company (Boeing) or any other non-US Government entity, for any
008: * purpose other than Boeing's performance of its obligations to the
009: * United States Government under Prime Contract No. DAAE07-03-9-F001
010: * without the prior express written permission of BBN.
011: *
012: * EXPORT CONTROL WARNING
013: * This document contains technical data whose export is restricted by
014: * the Arms Export Control Act (Title 22, U.S.C. Section 2751 et. seq.),
015: * and the International Traffic in Arms Regulations (ITAR) or Executive
016: * order 12470 of the United States of America. Violation of these export
017: * laws is subject to severe criminal penalties.
018: *
019: * GOVERNMENT PURPOSE RIGHTS (US Government Only)
020: * Contract No.: DAAE07-03-9-F001 (Boeing Prime Contract)
021: * Subcontract No.: 51300JAW3S (BBN subcontract under Northrop Grumman)
022: * Contractor Name: BBN Technologies Corp. under subcontract
023: * to Northrop Grumman Space & Mission Systems Corp.
024: * Contractor Address: 10 Moulton Street, Cambridge MA 02138 USA
025: * Expiration Date: None (Perpetual)
026: *
027: * The Government is granted Government Purpose Rights to this Data or
028: * Software. The Government rights to use, modify, reproduce, release,
029: * perform, display or disclose these technical data is subject to the
030: * restriction as stated in Agreement DAAE07-03-9-F001 between the Boeing
031: * Company and the Government. No restrictions apply after the
032: * expiration date shown above. Any reproduction of the technical data
033: * or portions thereof marked with this legend must also reproduce the
034: * markings.
035: *
036: * Copyright (c) 2006, BBN Technologies Corp.. All Rights Reserved
037: */
038:
039: package org.cougaar.bootstrap;
040:
041: import java.io.FileWriter;
042: import java.io.IOException;
043: import java.io.OutputStream;
044: import java.io.OutputStreamWriter;
045: import java.io.PrintStream;
046: import java.io.PrintWriter;
047: import java.io.Writer;
048: import java.net.URL;
049: import java.net.URLStreamHandlerFactory;
050: import java.util.HashMap;
051: import java.util.Map;
052: import sun.misc.Resource;
053:
054: /**
055: * Classloader that logs accessed classes, similar to
056: * "java -verbose:class", but also notes XURLClassLoader details
057: * such as jar paths.
058: * <p>
059: * Enable by setting:<pre>
060: * -Dorg.cougaar.bootstrap.classloader.class=org.cougaar.bootstrap.LoggingClassLoader
061: * -Dorg.cougaar.bootstrap.classloader.log=FILENAME
062: * </pre>
063: *
064: * @property org.cougaar.bootstrap.classloader.log=FILE
065: * log file for verbose class loading. If the name starts with
066: * ">>", then file append is used, otherwise overwrite is
067: * used. The file name "-" represents standard output. Embedded
068: * non-nested substrings of "{..}" are expanded with the java
069: * system property between the markers, such as:<pre>
070: * "/{os.name}/x{user.name}.log" -> "/Linux/xBob.log".</pre>
071: */
072: public class LoggingClassLoader extends XURLClassLoader {
073:
074: /** logging support */
075: private static final String LOG_PROP_NAME = "org.cougaar.bootstrap.classloader.log";
076: private PrintWriter logStream;
077:
078: private Map logPaths;
079:
080: public LoggingClassLoader(URL[] urls, ClassLoader parent) {
081: super (urls, parent);
082: logInit();
083: }
084:
085: public LoggingClassLoader(URL[] urls) {
086: super (urls);
087: logInit();
088: }
089:
090: public LoggingClassLoader(URL[] urls, ClassLoader parent,
091: URLStreamHandlerFactory factory) {
092: super (urls, parent, factory);
093: logInit();
094: }
095:
096: protected void addURL(URL url) {
097: super .addURL(url);
098: logAddURL(url);
099: }
100:
101: protected Class defineClass(String name, Resource res)
102: throws IOException {
103: Class ret = super .defineClass(name, res);
104: logDefineClass(res.getCodeSourceURL(), name);
105: return ret;
106: }
107:
108: private void logInit() {
109: // open the optional logging stream
110: String logName = resolveProperty(LOG_PROP_NAME);
111: if (logName.length() == 0) {
112: return;
113: }
114: boolean append = false;
115: try {
116: boolean autoFlush = false;
117: if (logName.startsWith(">>")) {
118: append = true;
119: logName = logName.substring(2);
120: }
121: Writer w;
122: if (logName.equals("-")) {
123: autoFlush = true;
124: w = new OutputStreamWriter(System.out);
125: } else {
126: w = new FileWriter(logName, append);
127: }
128: logStream = new PrintWriter(w, autoFlush);
129: } catch (Exception e) {
130: System.err
131: .println("Warning: "
132: + "Unable to open classloader log file \""
133: + logName
134: + "\" (-D"
135: + LOG_PROP_NAME
136: + "="
137: + SystemProperties
138: .getProperty(LOG_PROP_NAME) + ")");
139: return;
140: }
141: // add shutdown hook to flush stream
142: try {
143: Runtime.getRuntime().addShutdownHook(
144: new ShutdownFlusher(logStream));
145: } catch (Exception e) {
146: // security exception?
147: }
148: // log the initial URLs
149: URL[] urls = getURLs();
150: int n = (urls != null ? urls.length : 0);
151: logPaths = new HashMap(2 * n);
152: logStream.println("# " + (append ? "Append" : "New")
153: + " classloader log" + "\n# time: "
154: + System.currentTimeMillis() + "\n# format: jar class");
155: for (int i = 0; i < n; i++) {
156: logAddURL(urls[i]);
157: }
158: }
159:
160: /** Log an added classpath URL */
161: private void logAddURL(URL url) {
162: if (logStream == null) {
163: return;
164: }
165: if (url == null)
166: return;
167: // Extract the jar name (e.g. "file:host/b/c.jar" -> "c")
168: String jarName = "unknown";
169: String path = url.getPath();
170: if (logPaths.containsKey(url))
171: return;
172: if (path.endsWith(".jar") || path.endsWith(".zip")) {
173: int lastSep = path.lastIndexOf('/');
174: if (lastSep > 0) {
175: jarName = path
176: .substring(lastSep + 1, path.length() - 4);
177: }
178: }
179: // Assign a short unique name for this url
180: //
181: // We could optimize this by keeping a
182: // reverse (jar -> url) map, but for now we'll just
183: // scan the (url -> jar) map.
184: String givenName = jarName;
185: int tryCounter = 0;
186: while (logPaths.containsValue(givenName)) {
187: givenName = jarName + "#" + (++tryCounter);
188: }
189: logPaths.put(url, givenName);
190: // Log (jar, url)
191: logStream.println("# URL " + givenName + " " + url);
192: }
193:
194: private void logDefineClass(URL url, String name) {
195: if (logStream == null) {
196: return;
197: }
198: String givenName = (String) logPaths.get(url);
199: // Log (jar, class)
200: logStream.println(givenName + " " + name);
201: }
202:
203: /**
204: * Expand the system property with the given name.
205: * <p>
206: * For example:<pre>
207: * "/{os.name}/x{user.name}.log" -> "/Linux/xroot.log".
208: * </pre>
209: */
210: private static String resolveProperty(String s) {
211: String orig = SystemProperties.getProperty(s);
212: if (orig == null) {
213: return "";
214: }
215: int startIdx = orig.indexOf('{');
216: if (startIdx < 0) {
217: return orig;
218: }
219: String ret = orig.substring(0, startIdx);
220: while (true) {
221: int endIdx = orig.indexOf('}', (startIdx + 1));
222: if (endIdx < 0) {
223: throw new RuntimeException("Missing \"}\"");
224: }
225: String propName = orig.substring(startIdx + 1, endIdx);
226: String propValue = SystemProperties.getProperty(propName);
227: if (propValue != null) {
228: ret += propValue;
229: } else {
230: // silently ignore?
231: }
232: startIdx = orig.indexOf('{', (endIdx + 1));
233: if (startIdx < 0) {
234: ret += orig.substring(endIdx + 1);
235: break;
236: }
237: ret += orig.substring(endIdx + 1, startIdx);
238: }
239: return ret;
240: }
241:
242: // ugly Thread subclass; why not runnable?
243: private static class ShutdownFlusher extends Thread {
244: private PrintWriter logStream;
245:
246: public ShutdownFlusher(PrintWriter logStream) {
247: this .logStream = logStream;
248: if (logStream == null) {
249: throw new IllegalArgumentException("null stream");
250: }
251: }
252:
253: public void run() {
254: try {
255: logStream.flush();
256: } catch (Exception e) {
257: // ignore!
258: }
259: }
260: }
261: }
|