001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Mikhail A. Markov
021: * @version $Revision: 1.1.2.4 $
022: */package org.apache.harmony.rmi.common;
023:
024: import java.io.OutputStream;
025: import java.io.PrintStream;
026: import java.security.AccessController;
027: import java.security.PrivilegedAction;
028: import java.util.logging.Handler;
029: import java.util.logging.Level;
030: import java.util.logging.Logger;
031: import java.util.logging.LogRecord;
032: import java.util.logging.SimpleFormatter;
033: import java.util.logging.StreamHandler;
034:
035: /**
036: * Class containing all RMI logging functionality.
037: *
038: * @author Mikhail A. Markov
039: * @version $Revision: 1.1.2.4 $
040: */
041: public class RMILog implements RMIProperties {
042:
043: /**
044: * RMI logging level corresponding to Level.OFF value.
045: */
046: public static final Level SILENT = Level.OFF;
047:
048: /**
049: * RMI logging level corresponding to Level.FINE value.
050: */
051: public static final Level BRIEF = Level.FINE;
052:
053: /**
054: * RMI logging level corresponding to Level.FINER value.
055: */
056: public static final Level VERBOSE = Level.FINER;
057:
058: // handler for copying rmi logging messages to System.err
059: private static Handler consoleHandler = (Handler) AccessController
060: .doPrivileged(new PrivilegedAction() {
061: public Object run() {
062: Handler h = new RMIStreamHandler(System.err);
063: h.setLevel(Level.ALL);
064: return h;
065: }
066: });
067:
068: // Logger wrapped in this RMI log.
069: private Logger logger;
070:
071: // Handler set by setOutputStream() method.
072: private RMIStreamHandler rmiLogHandler;
073:
074: /**
075: * Helper method.
076: * Returns RMILog for logging remote calls on server side.
077: * If such a log does not exist, creates it.
078: *
079: * @return RMILog for logging remote calls on server side
080: */
081: public static RMILog getServerCallsLog() {
082: return getLog("harmony.rmi.server.call", //$NON-NLS-1$
083: getBoolean(LOGSERVER_PROP) ? VERBOSE : SILENT);
084: }
085:
086: /**
087: * Helper method.
088: * Returns RMILog for logging remote calls on client side.
089: * If such a log does not exist, creates it.
090: *
091: * @return RMILog for logging remote calls on client side
092: */
093: public static RMILog getClientCallsLog() {
094: return getLog("harmony.rmi.client.call", //$NON-NLS-1$
095: getBoolean(LOGCLIENT_PROP) ? VERBOSE : SILENT);
096: }
097:
098: /**
099: * Helper method.
100: * Returns RMILog for logging remote reference activity on server side.
101: * If such a log does not exist, creates it.
102: *
103: * @return RMILog for logging remote reference activity on server side
104: */
105: public static RMILog getServerRefLog() {
106: return getLog(
107: "harmony.rmi.server.ref", getString(SERVERLOGLEVEL_PROP)); //$NON-NLS-1$
108: }
109:
110: /**
111: * Helper method.
112: * Returns RMILog for logging remote reference activity on client side.
113: * If such a log does not exist, creates it.
114: *
115: * @return RMILog for logging remote reference activity on client side
116: */
117: public static RMILog getClientRefLog() {
118: return getLog(
119: "harmony.rmi.client.ref", getString(CLIENTLOGLEVEL_PROP)); //$NON-NLS-1$
120: }
121:
122: /**
123: * Helper method.
124: * Returns RMILog for logging DGC activity.
125: * If such a log does not exist, creates it.
126: *
127: * @return RMILog for logging DGC activity
128: */
129: public static RMILog getDGCLog() {
130: return getLog("harmony.rmi.dgc", getString(DGCLOGLEVEL_PROP)); //$NON-NLS-1$
131: }
132:
133: /**
134: * Helper method.
135: * Returns RMILog for logging activity of default RMIClassLoader provider.
136: * If such a log does not exist, creates it.
137: *
138: * @return RMILog for logging activity of default RMIClassLoader provider
139: */
140: public static RMILog getLoaderLog() {
141: return getLog(
142: "harmony.rmi.loader", getString(LOADERLOGLEVEL_PROP)); //$NON-NLS-1$
143: }
144:
145: /**
146: * Helper method.
147: * Returns RMILog for logging transport-layer activity.
148: * If such a log does not exist, creates it.
149: *
150: * @return RMILog for logging transport-layer activity
151: */
152: public static RMILog getTransportLog() {
153: return getLog("harmony.rmi.transport.misc", //$NON-NLS-1$
154: getString(TRANSPORTLOGLEVEL_PROP));
155: }
156:
157: /**
158: * Helper method.
159: * Returns RMILog for logging TCP binding/connection activity.
160: * If such a log does not exist, creates it.
161: *
162: * @return RMILog for logging TCP binding/connection activity
163: */
164: public static RMILog getTcpTransportLog() {
165: return getLog("harmony.rmi.transport.tcp", //$NON-NLS-1$
166: getString(TRANSPORTTCPLOGLEVEL_PROP));
167: }
168:
169: /**
170: * Helper method.
171: * Returns RMILog for logging HTTP connections activity.
172: * If such a log does not exist, creates it.
173: *
174: * @return RMILog for logging HTTP connections activity
175: */
176: public static RMILog getProxyTransportLog() {
177: return getLog("harmony.rmi.transport.proxy", //$NON-NLS-1$
178: getString(TRANSPORTPROXYLOGLEVEL_PROP));
179: }
180:
181: /**
182: * Helper method. Returns RMILog for logging Activation/ActivationGroup/Rmid
183: * events. If such a log does not exist, creates it.
184: *
185: * @return RMILog for logging remote calls on server side
186: */
187: public static RMILog getActivationLog() {
188: return getLog(
189: "harmony.rmi.activation", getString(ACTIVATIONLOGLEVEL_PROP)); //$NON-NLS-1$
190: }
191:
192: /**
193: * Creates RMILog. Underlying logger will have the name 'loggerName'. The
194: * level for created RMILog will be equal to 'logLevel' value.
195: *
196: * @param loggerName
197: * the name of the logger to be obtained
198: *
199: * @param logLevel
200: * the level for RMILog: it should be one of RMI logging levels
201: * (SILENT, BRIEF, VERBOSE or one of levels from
202: * java.util.logging.Level class
203: */
204: public static RMILog getLog(String loggerName, String logLevel) {
205: return getLog(loggerName, parseLevelString(logLevel));
206: }
207:
208: /**
209: * Creates RMILog. Underlying logger will have the name 'loggerName'. The
210: * level for created RMILog will be equal to 'logLevel' value.
211: *
212: * @param loggerName the name of the logger to be obtained
213: * @param logLevel the level for RMILog
214: */
215: public static RMILog getLog(String loggerName, final Level logLevel) {
216: final Logger logger = Logger.getLogger(loggerName);
217:
218: // add handler for publishing records to System.err
219: RMILog log = (RMILog) AccessController
220: .doPrivileged(new PrivilegedAction() {
221: public Object run() {
222:
223: if (logger.getLevel() == null
224: || !logger.isLoggable(logLevel)) {
225: logger.setLevel(logLevel);
226: }
227:
228: // remove System.err stream handler to avoid
229: // duplications
230: logger.removeHandler(consoleHandler);
231:
232: // add System.err stream handler again
233: logger.addHandler(consoleHandler);
234: return new RMILog(logger);
235: }
236: });
237: return log;
238: }
239:
240: /**
241: * Parses the given string and returns the corresponding Level object.
242: * Possible values for the incoming string are one of RMI logging
243: * levels (SILENT, BRIEF, VERBOSE or one of levels from
244: * java.util.logging.Level class. If the given string is null or it could
245: * not be parsed then Level.OFF value will be returned.
246: *
247: * @param levelStr String to be parsed
248: *
249: * @return parsed Level or Level.OFF if the given string is null or an
250: * error occurred while it's parsing
251: */
252: public static Level parseLevelString(String levelStr) {
253: if (levelStr == null) {
254: return Level.OFF;
255: }
256: levelStr = levelStr.trim().toUpperCase();
257:
258: if (levelStr.equals("SILENT")) { //$NON-NLS-1$
259: return SILENT;
260: } else if (levelStr.equals("BRIEF")) { //$NON-NLS-1$
261: return BRIEF;
262: } else if (levelStr.equals("VERBOSE")) { //$NON-NLS-1$
263: return VERBOSE;
264: }
265: Level logLevel = Level.OFF;
266:
267: try {
268: logLevel = Level.parse(levelStr);
269: } catch (IllegalArgumentException iae) {
270: }
271: return logLevel;
272: }
273:
274: /*
275: * Constructs RMILog containing specified Logger.
276: *
277: * @param logger Logger for RMILog
278: */
279: private RMILog(Logger logger) {
280: this .logger = logger;
281: }
282:
283: /**
284: * Checks if underlying logger would log a message with the specified
285: * level.
286: *
287: * @param l Logging level to be checked
288: *
289: * @return true if underlying logger would log a message with
290: * the specified level and false otherwise
291: */
292: public boolean isLoggable(Level l) {
293: return logger.isLoggable(l);
294: }
295:
296: /**
297: * Logs specified message prepended by the current Thread's name
298: * with the given level to the underlying logger.
299: *
300: * @param l logging level of the message
301: * @param msg message to be logged
302: */
303: public void log(Level l, String msg) {
304: if (isLoggable(l)) {
305: String[] logSrc = getLogSource();
306: logger.logp(l, logSrc[0], logSrc[1], Thread.currentThread()
307: .getName()
308: + ": " + msg); //$NON-NLS-1$
309: }
310: }
311:
312: /**
313: * Logs specified message prepended by the current Thread's name
314: * and Throwable object with the given level to the underlying logger.
315: *
316: * @param l logging level of the message and Throwable
317: * @param msg message to be logged
318: * @param t Throwable to be logged
319: */
320: public void log(Level l, String msg, Throwable t) {
321: if (isLoggable(l)) {
322: String[] logSrc = getLogSource();
323: logger.logp(l, logSrc[0], logSrc[1], Thread.currentThread()
324: .getName()
325: + ": " + msg, t); //$NON-NLS-1$
326: }
327: }
328:
329: /**
330: * Adds additional handler to the underlying logger from the given
331: * OutputStream. If this method with non-null parameter was already called
332: * and thus additional handler already exists, this handler will be replaced
333: * by newly created handler.
334: * This method is intended to be used by RemoteServer.setLog() method.
335: *
336: * @param out OutputStream for additional handler. If it's null then
337: * messages will not be logged to any additional handlers.
338: *
339: * @see RemoteServer.setLog(OutputStream)
340: */
341: public synchronized void setOutputStream(OutputStream out) {
342: if (rmiLogHandler != null) {
343: logger.removeHandler(rmiLogHandler);
344: }
345:
346: if (out == null) {
347: rmiLogHandler = null;
348: return;
349: }
350:
351: if (!logger.isLoggable(VERBOSE)) {
352: logger.setLevel(VERBOSE);
353: }
354:
355: rmiLogHandler = new RMIStreamHandler(out);
356: rmiLogHandler.setLevel(VERBOSE);
357: logger.addHandler(rmiLogHandler);
358: }
359:
360: /**
361: * Returns PrintStream where RMI logs messages.
362: * This method is intended to be used by RemoteServer.getLog() method
363: *
364: * @return PrintStream where RMI logs messages (possibly null)
365: *
366: * @see RemoteServer.getLog()
367: */
368: public synchronized PrintStream getPrintStream() {
369: return (rmiLogHandler == null) ? null : rmiLogHandler.ps;
370: }
371:
372: // Reads boolean value from the given property name.
373: private static boolean getBoolean(String propName) {
374: return ((Boolean) AccessController
375: .doPrivileged(new GetBooleanPropAction(propName)))
376: .booleanValue();
377: }
378:
379: // Reads string value from the given property name.
380: private static String getString(String propName) {
381: return (String) AccessController
382: .doPrivileged(new GetStringPropAction(propName));
383: }
384:
385: /*
386: * Returns string containing from 2 elements: the name of the class
387: * and the name of the method from which log() method was called.
388: * It's needed for logging the name of the method from which log() method
389: * was called.
390: */
391: private String[] getLogSource() {
392: StackTraceElement[] curST = (new Exception()).getStackTrace();
393:
394: // this method is called from appropriate log() method, so required
395: // source will be at 3-rd cell
396: return new String[] { curST[2].getClassName(),
397: curST[2].getMethodName() };
398: }
399:
400: /*
401: * Handler similar to ConsoleHandler but working with arbitrary
402: * OutputStreams.
403: */
404: private static class RMIStreamHandler extends StreamHandler {
405:
406: // PrintStream build from OutputStream provided to constructor
407: PrintStream ps;
408:
409: /*
410: * Constructs RMIStreamHandler from the given OutputStream.
411: *
412: * @param out underlying OutputStream for this handler
413: */
414: RMIStreamHandler(OutputStream out) {
415: super (out, new SimpleFormatter());
416: ps = new PrintStream(out);
417: }
418:
419: /**
420: * Publish specified LogRecord.
421: *
422: * @param rec LogRecord to be published
423: */
424: public void publish(LogRecord rec) {
425: super .publish(rec);
426: flush();
427: }
428:
429: /**
430: * Flushes the underlying OutputStream.
431: */
432: public void close() {
433: flush();
434: }
435: }
436: }
|