Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | * or more contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. The ASF licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
| 18 | */ |
Pierre De Rop | f74a116 | 2009-12-04 22:40:38 +0000 | [diff] [blame] | 19 | package org.apache.felix.dm.impl; |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 20 | |
| 21 | import java.lang.reflect.InvocationTargetException; |
| 22 | import java.lang.reflect.Method; |
| 23 | |
| 24 | import org.osgi.framework.BundleContext; |
| 25 | import org.osgi.framework.BundleException; |
| 26 | import org.osgi.framework.InvalidSyntaxException; |
| 27 | import org.osgi.framework.ServiceEvent; |
| 28 | import org.osgi.framework.ServiceListener; |
| 29 | import org.osgi.framework.ServiceReference; |
| 30 | |
| 31 | /** |
| 32 | * This class mimics the standard OSGi <tt>LogService</tt> interface. An |
| 33 | * instance of this class is used by the dependency manager for all logging. |
| 34 | * By default this class logs messages to standard out. The log level can be set to |
| 35 | * control the amount of logging performed, where a higher number results in |
| 36 | * more logging. A log level of zero turns off logging completely. |
| 37 | * |
| 38 | * The log levels match those specified in the OSGi Log Service. |
| 39 | * This class also tracks log services and will use the highest ranking |
| 40 | * log service, if present, as a back end instead of printing to standard |
| 41 | * out. The class uses reflection to invoking the log service's method to |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 42 | * avoid a dependency on the log interface, which is also why it does not |
| 43 | * actually implement <code>LogService</code>. This class is in many ways |
| 44 | * similar to the one used in the system bundle for that same purpose. |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 45 | * |
| 46 | * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| 47 | */ |
| 48 | public class Logger implements ServiceListener { |
| 49 | public static final int LOG_ERROR = 1; |
| 50 | public static final int LOG_WARNING = 2; |
| 51 | public static final int LOG_INFO = 3; |
| 52 | public static final int LOG_DEBUG = 4; |
| 53 | |
| 54 | private final BundleContext m_context; |
| 55 | |
| 56 | private final static int LOGGER_OBJECT_IDX = 0; |
| 57 | private final static int LOGGER_METHOD_IDX = 1; |
| 58 | private ServiceReference m_logRef = null; |
| 59 | private Object[] m_logger = null; |
| 60 | |
| 61 | public Logger(BundleContext context) { |
| 62 | m_context = context; |
| 63 | startListeningForLogService(); |
| 64 | } |
| 65 | |
| 66 | public final void log(int level, String msg) { |
| 67 | _log(null, level, msg, null); |
| 68 | } |
| 69 | |
| 70 | public final void log(int level, String msg, Throwable throwable) { |
| 71 | _log(null, level, msg, throwable); |
| 72 | } |
| 73 | |
| 74 | public final void log(ServiceReference sr, int level, String msg) { |
| 75 | _log(sr, level, msg, null); |
| 76 | } |
| 77 | |
| 78 | public final void log(ServiceReference sr, int level, String msg, Throwable throwable) { |
| 79 | _log(sr, level, msg, throwable); |
| 80 | } |
| 81 | |
| 82 | protected void doLog(ServiceReference sr, int level, String msg, Throwable throwable) { |
| 83 | String s = (sr == null) ? null : "SvcRef " + sr; |
| 84 | s = (s == null) ? msg : s + " " + msg; |
| 85 | s = (throwable == null) ? s : s + " (" + throwable + ")"; |
| 86 | switch (level) { |
| 87 | case LOG_DEBUG: |
| 88 | System.out.println("DEBUG: " + s); |
| 89 | break; |
| 90 | case LOG_ERROR: |
| 91 | System.out.println("ERROR: " + s); |
| 92 | if (throwable != null) { |
| 93 | if ((throwable instanceof BundleException) && (((BundleException) throwable).getNestedException() != null)) { |
| 94 | throwable = ((BundleException) throwable).getNestedException(); |
| 95 | } |
| 96 | throwable.printStackTrace(); |
| 97 | } |
| 98 | break; |
| 99 | case LOG_INFO: |
| 100 | System.out.println("INFO: " + s); |
| 101 | break; |
| 102 | case LOG_WARNING: |
| 103 | System.out.println("WARNING: " + s); |
| 104 | break; |
| 105 | default: |
| 106 | System.out.println("UNKNOWN[" + level + "]: " + s); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | private void _log(ServiceReference sr, int level, String msg, Throwable throwable) { |
| 111 | // Save our own copy just in case it changes. We could try to do |
| 112 | // more conservative locking here, but let's be optimistic. |
| 113 | Object[] logger = m_logger; |
| 114 | // Use the log service if available. |
| 115 | if (logger != null) { |
| 116 | _logReflectively(logger, sr, level, msg, throwable); |
| 117 | } |
| 118 | // Otherwise, default logging action. |
| 119 | else { |
| 120 | doLog(sr, level, msg, throwable); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | private void _logReflectively(Object[] logger, ServiceReference sr, int level, String msg, Throwable throwable) { |
| 125 | if (logger != null) { |
| 126 | Object[] params = { sr, new Integer(level), msg, throwable }; |
| 127 | try { |
| 128 | ((Method) logger[LOGGER_METHOD_IDX]).invoke(logger[LOGGER_OBJECT_IDX], params); |
| 129 | } |
| 130 | catch (InvocationTargetException ex) { |
| 131 | System.err.println("Logger: " + ex); |
| 132 | } |
| 133 | catch (IllegalAccessException ex) { |
| 134 | System.err.println("Logger: " + ex); |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * This method is called when the bundle context is set; |
| 141 | * it simply adds a service listener so that the bundle can track |
| 142 | * log services to be used as the back end of the logging mechanism. It also |
| 143 | * attempts to get an existing log service, if present, but in general |
| 144 | * there will never be a log service present since the system bundle is |
| 145 | * started before every other bundle. |
| 146 | */ |
| 147 | private synchronized void startListeningForLogService() { |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 148 | try { |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 149 | // add a service listener for log services, carefully avoiding any code dependency on it |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 150 | m_context.addServiceListener(this, "(objectClass=org.osgi.service.log.LogService)"); |
| 151 | } |
| 152 | catch (InvalidSyntaxException ex) { |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 153 | // this will never happen since the filter is hard coded |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 154 | } |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 155 | // try to get an existing log service |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 156 | m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService"); |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 157 | // get the service object if available and set it in the logger |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 158 | if (m_logRef != null) { |
| 159 | setLogger(m_context.getService(m_logRef)); |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | /** |
| 164 | * This method implements the callback for the ServiceListener interface. |
| 165 | * It is public as a byproduct of implementing the interface and should |
| 166 | * not be called directly. This method tracks run-time changes to log |
| 167 | * service availability. If the log service being used by the framework's |
| 168 | * logging mechanism goes away, then this will try to find an alternative. |
| 169 | * If a higher ranking log service is registered, then this will switch |
| 170 | * to the higher ranking log service. |
| 171 | */ |
| 172 | public final synchronized void serviceChanged(ServiceEvent event) { |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 173 | // if no logger is in use, then grab this one |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 174 | if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef == null)) { |
| 175 | m_logRef = event.getServiceReference(); |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 176 | // get the service object and set it in the logger |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 177 | setLogger(m_context.getService(m_logRef)); |
| 178 | } |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 179 | // if a logger is in use, but this one has a higher ranking, then swap |
| 180 | // it for the existing logger |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 181 | else if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef != null)) { |
| 182 | ServiceReference ref = m_context.getServiceReference("org.osgi.service.log.LogService"); |
| 183 | if (!ref.equals(m_logRef)) { |
| 184 | m_context.ungetService(m_logRef); |
| 185 | m_logRef = ref; |
| 186 | setLogger(m_context.getService(m_logRef)); |
| 187 | } |
| 188 | } |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 189 | // if the current logger is going away, release it and try to |
| 190 | // find another one |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 191 | else if ((event.getType() == ServiceEvent.UNREGISTERING) && m_logRef.equals(event.getServiceReference())) { |
| 192 | // Unget the service object. |
| 193 | m_context.ungetService(m_logRef); |
| 194 | // Try to get an existing log service. |
| 195 | m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService"); |
Marcel Offermans | e49478f | 2009-12-29 09:14:14 +0000 | [diff] [blame] | 196 | // get the service object if available and set it in the logger |
Marcel Offermans | a962bc9 | 2009-11-21 17:59:33 +0000 | [diff] [blame] | 197 | if (m_logRef != null) { |
| 198 | setLogger(m_context.getService(m_logRef)); |
| 199 | } |
| 200 | else { |
| 201 | setLogger(null); |
| 202 | } |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | /** |
| 207 | * This method sets the new log service object. It also caches the method to |
| 208 | * invoke. The service object and method are stored in array to optimistically |
| 209 | * eliminate the need to locking when logging. |
| 210 | */ |
| 211 | private void setLogger(Object logObj) { |
| 212 | if (logObj == null) { |
| 213 | m_logger = null; |
| 214 | } |
| 215 | else { |
| 216 | Class[] formalParams = { ServiceReference.class, Integer.TYPE, String.class, Throwable.class }; |
| 217 | try { |
| 218 | Method logMethod = logObj.getClass().getMethod("log", formalParams); |
| 219 | logMethod.setAccessible(true); |
| 220 | m_logger = new Object[] { logObj, logMethod }; |
| 221 | } |
| 222 | catch (NoSuchMethodException ex) { |
| 223 | System.err.println("Logger: " + ex); |
| 224 | m_logger = null; |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | } |