/* * Copyright (c) 2003, KNOPFLERFISH project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * - Neither the name of the KNOPFLERFISH project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.knopflerfish.service.log; import java.text.SimpleDateFormat; import java.util.Date; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceEvent; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; /** * * LogRef is an utility class that simplifies the use of the LogService. * * * <P> * * LogRef let you use the log without worrying about getting new * * service objects when the log service is restarted. It also * supplies methods * with short names that does logging with all the * different LogService * severity types. * * * </P> * <P> * * To use the LogRef you need to import * * <code>org.knopflerfish.service.log.LogRef</code> and instantiate * LogRef * with your bundle context as parameter. The bundle context * is used for * getting the LogService and adding a service listener. * * * </P> * * * <H2>Example usage</H2> * * The <code>if</code> statement that protects * each call to the * <code>LogRef</code> instance below is there to save the * effort * required for creating the message string object in cases where the * * log will throw away the log entry due to its unimportance. The * user must * have this <code>if</code>-test in his code since that is * the only way to * avoid constructing the string object. Placing it * in the wrapper (LogRef) * will not help due to the design of the Java * programming language. * * * * * <PRE> * package org.knopflerfish.example.hello; * * import * org.osgi.framework.*; * import org.knopflerfish.service.log.LogRef; * * * * public class Hello implements BundleActivator { * LogRef log; * * public void * start(BundleContext bundleContext) { * log = new LogRef(bundleContext); * if * (log.doInfo()) log.info("Hello started."); * } * * public void * stop(BundleContext bundleContext) { * if (log.doDebug()) log.debug("Hello * stopped."); * } * } * * * </PRE> * * * * * @author Gatespace AB * * @see org.osgi.service.log.LogService * * @see org.knopflerfish.service.log.LogService */ public class LogRef implements ServiceListener, LogService { // Class name of the OSGI log service private final static String LOG_CLASS_OSGI = org.osgi.service.log.LogService.class .getName(); // Class name of Knopflerfish extended log service private final static String LOG_CLASS_KF = org.knopflerfish.service.log.LogService.class .getName(); private final static String logServiceFilter = "(|" + "(objectClass=" + LOG_CLASS_KF + ")(objectClass=" + LOG_CLASS_OSGI + "))"; // Date formater used then sending entries to System.out private static SimpleDateFormat simpleDateFormat = null; // Handle to the framework private BundleContext bc; // Service reference for the current log service private ServiceReference logSR; // The current log service private org.osgi.service.log.LogService log; // If true and no log service, print on System.out private boolean useOut; // The id of the calling bundle private long bundleId; // If true warn about using closed LogRef object private boolean doWarnIfClosed; /** * * Create new LogRef object for a given bundle. * * * @param bc * the bundle context of the bundle that this log ref * instance * belongs too. * * @param out * If true print messages on <code>System.out</code> when * * there is no log service. */ public LogRef(BundleContext bc, boolean out) { init(bc, out); } /** * * Create new LogRef object for a given bundle. * * * <p> * If the system property <tt>org.knopflerfish.log.out</tt> equals * * "true", system.out will be used as fallback if no log service * is found. * * </p> * * * @param bc * the bundle context of the bundle that this log ref * instance * belongs too. */ public LogRef(BundleContext bc) { boolean b = false; try { b = "true".equals(System.getProperty("org.knopflerfish.log.out")); } catch (Throwable t) { System.err.println("get system property failed: " + t); t.printStackTrace(); } init(bc, b); } private void init(BundleContext bc, boolean out) { this.bc = bc; useOut = out; bundleId = bc.getBundle().getBundleId(); try { bc.addServiceListener(this, logServiceFilter); } catch (InvalidSyntaxException e) { error("Failed to register log service listener (filter=" + logServiceFilter + ")", e); } } /** * * Service listener entry point. Releases the log service object if * one * has been fetched. * * * * @param evt * Service event */ public void serviceChanged(ServiceEvent evt) { if (evt.getServiceReference() == logSR && evt.getType() == ServiceEvent.UNREGISTERING) { ungetLogService(); } } /** * * Unget the log service. Note that this method is synchronized on * the * same object as the internal method that calls the actual log * service. * This ensures that the log service is not removed by * this method while a * log message is generated. */ private synchronized void ungetLogService() { doWarnIfClosed = doDebug(); if (log != null) { bc.ungetService(logSR); logSR = null; log = null; } } /** * * Close this LogRef object. Ungets the log service if active. */ public void close() { ungetLogService(); bc.removeServiceListener(this); bc = null; } /** * * Sends a message to the log if possible. * * * * @param msg * Human readable string describing the condition. * * @param level * The severity of the message (Should be one of the * four * predefined severities). * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. * * @param e * The exception that reflects the condition. */ protected synchronized void doLog(String msg, int level, ServiceReference sr, Throwable e) { if (bc != null && log == null) { logSR = bc.getServiceReference(LOG_CLASS_KF); if (logSR == null) { // No service implementing the Knopflerfish extended log, try to // look for a standard OSGi log service. logSR = bc.getServiceReference(LOG_CLASS_OSGI); } if (logSR != null) { log = (org.osgi.service.log.LogService) bc.getService(logSR); } if (log == null) { // Failed to get log service clear the service reference. logSR = null; } } if (log != null) { log.log(sr, level, msg, e); } else if (useOut || doWarnIfClosed) { if (bc == null) { System.err.println("WARNING! Bundle #" + bundleId + " called closed LogRef object"); } // No log service and request for messages on System.out System.out.print(LogUtil.fromLevel(level, 8)); System.out.print(" "); if (simpleDateFormat == null) { simpleDateFormat = new SimpleDateFormat("yyyyMMdd HH:mm:ss"); } System.out.print(simpleDateFormat.format(new Date())); System.out.print(" "); System.out.print(getBundleName()); System.out.print(" - "); if (sr != null) { System.out.print("["); System.out.print(sr); System.out.print("] "); } System.out.print(msg); if (e != null) { System.out.print(" ("); System.out.print(e); System.out.print(")"); System.out.println(); e.printStackTrace(); } System.out.println(); } } /** * * Returns the current log level. There is no use to generate log * * entries with a severity level less than this value since such * entries * will be thrown away by the log. * * * * @return the current severity log level for this bundle. */ public int getLogLevel() { if (log != null && (log instanceof LogService)) { return ((LogService) log).getLogLevel(); } return LOG_DEBUG; } /** * * Returns true if messages with severity debug or higher * are saved by * the log. * * * @return <code>true</code> if messages with severity LOG_DEBUG * or * higher are included in the log, otherwise <code>false</code>. */ public boolean doDebug() { return getLogLevel() >= LOG_DEBUG; } /** * * Returns true if messages with severity warning or higher * are saved by * the log. * * * @return <code>true</code> if messages with severity LOG_WARNING * or * higher are included in the log, otherwise <code>false</code>. */ public boolean doWarn() { return getLogLevel() >= LOG_WARNING; } /** * * Returns true if messages with severity info or higher * are saved by * the log. * * * @return <code>true</code> if messages with severity LOG_INFO * or * higher are included in the log, otherwise <code>false</code>. */ public boolean doInfo() { return getLogLevel() >= LOG_INFO; } /** * * Returns true if messages with severity error or higher * are saved by * the log. * * * @return <code>true</code> if messages with severity LOG_ERROR * or * higher are included in the log, otherwise <code>false</code>. */ public boolean doError() { return getLogLevel() >= LOG_ERROR; } /** * * Log a debug level message * * * * @param msg * Log message. */ public void debug(String msg) { doLog(msg, LOG_DEBUG, null, null); } /** * * Log a debug level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. */ public void debug(String msg, ServiceReference sr) { doLog(msg, LOG_DEBUG, sr, null); } /** * * Log a debug level message. * * * * @param msg * Log message * * @param e * The exception that reflects the condition. */ public void debug(String msg, Throwable e) { doLog(msg, LOG_DEBUG, null, e); } /** * * Log a debug level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. * * @param e * The exception that reflects the condition. */ public void debug(String msg, ServiceReference sr, Throwable e) { doLog(msg, LOG_DEBUG, sr, e); } /** * * Log an info level message. * * * * @param msg * Log message */ public void info(String msg) { doLog(msg, LOG_INFO, null, null ); } /** * * Log an info level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. */ public void info(String msg, ServiceReference sr) { doLog(msg, LOG_INFO, sr, null ); } /** * * Log an info level message. * * * * @param msg * Log message * * @param e * The exception that reflects the condition. */ public void info(String msg, Throwable e) { doLog(msg, LOG_INFO, null, e); } /** * * Log an info level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. * * @param e * The exception that reflects the condition. */ public void info(String msg, ServiceReference sr, Throwable e) { doLog(msg, LOG_INFO, sr, e); } /** * * Log a warning level message. * * * * @param msg * Log message */ public void warn(String msg) { doLog(msg, LOG_WARNING, null, null ); } /** * * Log a warning level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. */ public void warn(String msg, ServiceReference sr) { doLog(msg, LOG_WARNING, sr, null ); } /** * * Log a warning level message. * * * * @param msg * Log message * * @param e * The exception that reflects the condition. */ public void warn(String msg, Throwable e) { doLog(msg, LOG_WARNING, null, e); } /** * * Log a warning level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. * * @param e * The exception that reflects the condition. */ public void warn(String msg, ServiceReference sr, Throwable e) { doLog(msg, LOG_WARNING, sr, e); } /** * * Log an error level message. * * * * @param msg * Log message */ public void error(String msg) { doLog(msg, LOG_ERROR, null, null ); } /** * * Log an error level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. */ public void error(String msg, ServiceReference sr) { doLog(msg, LOG_ERROR, sr, null ); } /** * * Log an error level message. * * * * @param msg * Log message * * @param e * The exception that reflects the condition. */ public void error(String msg, Throwable e) { doLog(msg, LOG_ERROR, null, e); } /** * * Log an error level message. * * * * @param msg * Log message * * @param sr * The <code>ServiceReference</code> of the service * that this * message is associated with. * * @param e * The exception that reflects the condition. */ public void error(String msg, ServiceReference sr, Throwable e) { doLog(msg, LOG_ERROR, sr, e); } /** * * Log a message. * The ServiceDescription field and the Throwable * field * of the LogEntry will be set to null. * * * @param level * The severity of the message. (Should be one of the * four * predefined severities.) * * @param message * Human readable string describing the condition. */ public void log(int level, String message) { doLog(message, level, null, null ); } /** * * Log a message with an exception. * The ServiceDescription field of the * LogEntry will be set to null. * * * @param level * The severity of the message. (Should be one of the * four * predefined severities.) * * @param message * Human readable string describing the condition. * * @param exception * The exception that reflects the condition. */ public void log(int level, String message, Throwable exception) { doLog(message, level, null, exception); } /** * * Log a message associated with a specific Service. * The Throwable field * of the LogEntry will be set to null. * * * @param sr * The <code>ServiceReference</code> of the service that * this * message is associated with. * * @param level * The severity of the message. (Should be one of the * four * predefined severities.) * * @param message * Human readable string describing the condition. */ public void log(ServiceReference sr, int level, String message) { doLog(message, level, sr, null ); } /** * * Log a message with an exception associated with a specific Service. * * * @param sr * The <code>ServiceReference</code> of the service that * this * message is associated with. * * @param level * The severity of the message. (Should be one of the * four * predefined severities.) * * @param message * Human readable string describing the condition. * * @param exception * The exception that reflects the condition. */ public void log(ServiceReference sr, int level, String message, Throwable exception) { doLog(message, level, sr, exception); } /** * * Returns a human readable name for the bundle that * <code>bc</code> * represents. * * * @return Name of the bundle that uses this wrapper * (at least 12 * characters). */ private String getBundleName() { StringBuffer bundleName = new StringBuffer(24); // We can't get bundle-name since it requires AdminPermission. // bundleName.append((String)bc.getBundle().getHeaders().get("Bundle-Name")); // If name was not found use the Bid as name. if (bundleName.length() <= 0) { bundleName.append("bid#"); bundleName.append(String.valueOf(bundleId)); } if (bundleName.length() < 12) { bundleName.append(" "); bundleName.setLength(12); } return bundleName.toString(); } }