/*
* 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();
}
}