blob: a47af96f2378b55dabddbc79ee1c78354fb73949 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.coordinator.impl;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
/**
* This class mimics the standard OSGi <tt>LogService</tt> interface. It logs to an
* available log service with the highest service ranking.
*
* @see org.osgi.service.log.LogService
**/
public class LogWrapper
{
/**
* ERROR LEVEL
*
* @see org.osgi.service.log.LogService#LOG_ERROR
*/
public static final int LOG_ERROR = 1;
/**
* WARNING LEVEL
*
* @see org.osgi.service.log.LogService#LOG_WARNING
*/
public static final int LOG_WARNING = 2;
/**
* INFO LEVEL
*
* @see org.osgi.service.log.LogService#LOG_INFO
*/
public static final int LOG_INFO = 3;
/**
* DEBUG LEVEL
*
* @see org.osgi.service.log.LogService#LOG_DEBUG
*/
public static final int LOG_DEBUG = 4;
/** A sorted set containing the currently available LogServices.
* Furthermore used as lock
*/
private final Set<ServiceReference> loggerRefs = new TreeSet<ServiceReference>(
new Comparator<ServiceReference>() {
public int compare(ServiceReference o1, ServiceReference o2) {
return o2.compareTo(o1);
}
});
/**
* Only null while not set and loggerRefs is empty hence, only needs to be
* checked in case m_loggerRefs is empty otherwise it will not be null.
*/
private BundleContext context;
private ServiceListener logServiceListener;
/**
* Current log level. Message with log level less than or equal to
* current log level will be logged.
* The default value is {@link #LOG_WARNING}
*
* @see #setLogLevel(int)
*/
private int logLevel = LOG_WARNING;
/**
* Create the singleton
*/
private static class LogWrapperLoader
{
static final LogWrapper SINGLETON = new LogWrapper();
}
/**
* Returns the singleton instance of this LogWrapper that can be used to send
* log messages to all currently available LogServices or to standard output,
* respectively.
*
* @return the singleton instance of this LogWrapper.
*/
public static LogWrapper getLogger()
{
return LogWrapperLoader.SINGLETON;
}
/**
* Set the <tt>BundleContext</tt> of the bundle. This method registers a service
* listener for LogServices with the framework that are subsequently used to
* log messages.
* <p>
* If the bundle context is <code>null</code>, the service listener is
* unregistered and all remaining references to LogServices dropped before
* internally clearing the bundle context field.
*
* @param context The context of the bundle.
*/
public static void setContext( final BundleContext context )
{
final LogWrapper logWrapper = LogWrapperLoader.SINGLETON;
// context is removed, unregister and drop references
if ( context == null )
{
if ( logWrapper.logServiceListener != null )
{
logWrapper.context.removeServiceListener( logWrapper.logServiceListener );
logWrapper.logServiceListener = null;
}
logWrapper.removeLoggerRefs();
}
// set field
logWrapper.setBundleContext( context );
// context is set, register and get existing services
if ( context != null )
{
try
{
final ServiceListener listener = new ServiceListener()
{
// Add a newly available LogService reference to the singleton.
public void serviceChanged( final ServiceEvent event )
{
if ( ServiceEvent.REGISTERED == event.getType() )
{
LogWrapperLoader.SINGLETON.addLoggerRef( event.getServiceReference() );
}
else if ( ServiceEvent.UNREGISTERING == event.getType() )
{
LogWrapperLoader.SINGLETON.removeLoggerRef( event.getServiceReference() );
}
}
};
context.addServiceListener( listener, "(" + Constants.OBJECTCLASS + "=org.osgi.service.log.LogService)" );
logWrapper.logServiceListener = listener;
// Add all available LogService references to the singleton.
final ServiceReference[] refs = context.getServiceReferences( "org.osgi.service.log.LogService", null );
if ( null != refs )
{
for ( int i = 0; i < refs.length; i++ )
{
logWrapper.addLoggerRef( refs[i] );
}
}
}
catch ( InvalidSyntaxException e )
{
// this never happens
}
}
}
/**
* The private singleton constructor.
*/
LogWrapper()
{
// Singleton
}
/**
* Removes all references to LogServices still kept
*/
void removeLoggerRefs()
{
synchronized ( loggerRefs )
{
loggerRefs.clear();
}
}
/**
* Add a reference to a newly available LogService
*/
void addLoggerRef( final ServiceReference ref )
{
synchronized (loggerRefs)
{
loggerRefs.add(ref);
}
}
/**
* Remove a reference to a LogService
*/
void removeLoggerRef( final ServiceReference ref )
{
synchronized (loggerRefs)
{
loggerRefs.remove(ref);
}
}
/**
* Set the context of the bundle in the singleton implementation.
*/
private void setBundleContext(final BundleContext context)
{
synchronized(loggerRefs)
{
this.context = context;
}
}
public void log(final int level, final String msg)
{
log(null, level, msg, null);
}
public void log(final int level, final String msg, final Throwable ex)
{
log(null, level, msg, null);
}
public void log(final ServiceReference sr, final int level, final String msg)
{
log(sr, level, msg, null);
}
public void log(final ServiceReference sr, final int level, final String msg,
final Throwable ex)
{
// The method will remove any unregistered service reference as well.
synchronized (loggerRefs)
{
if (level > logLevel)
{
return; // don't log
}
boolean logged = false;
if (!loggerRefs.isEmpty())
{
// There is at least one LogService available hence, we can use the
// class as well.
for (Iterator<ServiceReference> iter = loggerRefs.iterator(); iter.hasNext();)
{
final ServiceReference next = iter.next();
org.osgi.service.log.LogService logger =
(org.osgi.service.log.LogService) context.getService(next);
if (null != logger)
{
if ( sr == null )
{
if ( ex == null )
{
logger.log(level, msg);
}
else
{
logger.log(level, msg, ex);
}
}
else
{
if ( ex == null )
{
logger.log(sr, level, msg);
}
else
{
logger.log(sr, level, msg, ex);
}
}
context.ungetService(next);
// we logged, so we can finish
logged = true;
break;
}
else
{
// The context returned null for the reference - it follows
// that the service is unregistered and we can remove it
iter.remove();
}
}
}
if ( !logged)
{
_log(sr, level, msg, ex);
}
}
}
/*
* Log the message to standard output. This appends the level to the message.
* null values are handled appropriate.
*/
private void _log(final ServiceReference sr, final int level, final String msg,
Throwable ex)
{
String s = (sr == null) ? null : "SvcRef " + sr;
s = (s == null) ? msg : s + " " + msg;
s = (ex == null) ? s : s + " (" + ex + ")";
switch (level)
{
case LOG_DEBUG:
System.out.println("DEBUG: " + s);
break;
case LOG_ERROR:
System.out.println("ERROR: " + s);
if (ex != null)
{
if ((ex instanceof BundleException)
&& (((BundleException) ex).getNestedException() != null))
{
ex = ((BundleException) ex).getNestedException();
}
ex.printStackTrace();
}
break;
case LOG_INFO:
System.out.println("INFO: " + s);
break;
case LOG_WARNING:
System.out.println("WARNING: " + s);
break;
default:
System.out.println("UNKNOWN[" + level + "]: " + s);
}
}
/**
* Change the current log level. Log level decides what messages gets
* logged. Any message with a log level higher than the currently set
* log level is not logged.
*
* @param logLevel new log level
*/
public void setLogLevel(int logLevel)
{
synchronized (loggerRefs)
{
logLevel = logLevel;
}
}
/**
* @return current log level.
*/
public int getLogLevel()
{
synchronized (loggerRefs)
{
return logLevel;
}
}
}