blob: a91c11c66603b24bb493f98f057981e5eb069617 [file] [log] [blame]
Marcel Offermansa962bc92009-11-21 17:59:33 +00001/*
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 Ropf74a1162009-12-04 22:40:38 +000019package org.apache.felix.dm.impl;
Marcel Offermansa962bc92009-11-21 17:59:33 +000020
21import java.lang.reflect.InvocationTargetException;
22import java.lang.reflect.Method;
23
24import org.osgi.framework.BundleContext;
25import org.osgi.framework.BundleException;
26import org.osgi.framework.InvalidSyntaxException;
27import org.osgi.framework.ServiceEvent;
28import org.osgi.framework.ServiceListener;
29import 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 Offermanse49478f2009-12-29 09:14:14 +000042 * 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 Offermansa962bc92009-11-21 17:59:33 +000045 *
46 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
47 */
48public 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 Offermansa962bc92009-11-21 17:59:33 +0000148 try {
Marcel Offermanse49478f2009-12-29 09:14:14 +0000149 // add a service listener for log services, carefully avoiding any code dependency on it
Marcel Offermansa962bc92009-11-21 17:59:33 +0000150 m_context.addServiceListener(this, "(objectClass=org.osgi.service.log.LogService)");
151 }
152 catch (InvalidSyntaxException ex) {
Marcel Offermanse49478f2009-12-29 09:14:14 +0000153 // this will never happen since the filter is hard coded
Marcel Offermansa962bc92009-11-21 17:59:33 +0000154 }
Marcel Offermanse49478f2009-12-29 09:14:14 +0000155 // try to get an existing log service
Marcel Offermansa962bc92009-11-21 17:59:33 +0000156 m_logRef = m_context.getServiceReference("org.osgi.service.log.LogService");
Marcel Offermanse49478f2009-12-29 09:14:14 +0000157 // get the service object if available and set it in the logger
Marcel Offermansa962bc92009-11-21 17:59:33 +0000158 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 Offermanse49478f2009-12-29 09:14:14 +0000173 // if no logger is in use, then grab this one
Marcel Offermansa962bc92009-11-21 17:59:33 +0000174 if ((event.getType() == ServiceEvent.REGISTERED) && (m_logRef == null)) {
175 m_logRef = event.getServiceReference();
Marcel Offermanse49478f2009-12-29 09:14:14 +0000176 // get the service object and set it in the logger
Marcel Offermansa962bc92009-11-21 17:59:33 +0000177 setLogger(m_context.getService(m_logRef));
178 }
Marcel Offermanse49478f2009-12-29 09:14:14 +0000179 // if a logger is in use, but this one has a higher ranking, then swap
180 // it for the existing logger
Marcel Offermansa962bc92009-11-21 17:59:33 +0000181 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 Offermanse49478f2009-12-29 09:14:14 +0000189 // if the current logger is going away, release it and try to
190 // find another one
Marcel Offermansa962bc92009-11-21 17:59:33 +0000191 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 Offermanse49478f2009-12-29 09:14:14 +0000196 // get the service object if available and set it in the logger
Marcel Offermansa962bc92009-11-21 17:59:33 +0000197 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}