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