blob: e1cc5e79a055720a485161d75cb24e87bcdc18fb [file] [log] [blame]
Karl Pauls91761932006-04-28 11:12:54 +00001/*
2 * Copyright 2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17package org.apache.felix.eventadmin.impl.util;
18
19import java.util.HashSet;
20import java.util.Iterator;
21import java.util.Set;
22
23import org.osgi.framework.BundleContext;
24import org.osgi.framework.BundleException;
25import org.osgi.framework.Constants;
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 will be used by the EventAdmin for all logging. The
34 * implementation of this class sends log messages to standard output, if no
35 * <tt>LogService</tt> is present; it uses a log service if one is
36 * installed in the framework. To do that without creating a hard dependency on the
37 * package it uses fully qualified class names and registers a listener with the
38 * framework hence, it does not need access to the <tt>LogService</tt> class but will
39 * use it if the listener is informed about an available service. By using a
40 * DynamicImport-Package dependency we don't need the package but
41 * use it if present. Additionally, all log methods prefix the log message with
42 * <tt>EventAdmin: </tt>.
43 *
44 * @see org.osgi.service.log.LogService
45 *
46 * @author <a href="mailto:felix-dev@incubator.apache.org">Felix Project Team</a>
47**/
48// TODO: At the moment we log a message to all currently available LogServices.
49// Maybe, we should only log to the one with the highest ranking instead?
50// What is the best practice in this case?
51public class LogWrapper
52{
53 /**
54 * ERROR LEVEL
55 *
56 * @see org.osgi.service.log.LogService#LOG_ERROR
57 */
58 public static final int LOG_ERROR = 1;
59
60 /**
61 * WARNING LEVEL
62 *
63 * @see org.osgi.service.log.LogService#LOG_WARNING
64 */
65 public static final int LOG_WARNING = 2;
66
67 /**
68 * INFO LEVEL
69 *
70 * @see org.osgi.service.log.LogService#LOG_INFO
71 */
72 public static final int LOG_INFO = 3;
73
74 /**
75 * DEBUG LEVEL
76 *
77 * @see org.osgi.service.log.LogService#LOG_DEBUG
78 */
79 public static final int LOG_DEBUG = 4;
80
81 // A set containing the currently available LogServices. Furthermore used as lock
82 private final Set m_loggerRefs = new HashSet();
83
84 // Only null while not set and m_loggerRefs is empty hence, only needs to be
85 // checked in case m_loggerRefs is empty otherwise it will not be null.
86 private BundleContext m_context;
87
88 /*
89 * A thread save variant of the double checked locking singleton.
90 */
91 private static class LogWrapperLoader
92 {
93 static final LogWrapper m_singleton = new LogWrapper();
94 }
95
96 /**
97 * Returns the singleton instance of this LogWrapper that can be used to send
98 * log messages to all currently available LogServices or to standard output,
99 * respectively.
100 *
101 * @return the singleton instance of this LogWrapper.
102 */
103 public static LogWrapper getLogger()
104 {
105 return LogWrapperLoader.m_singleton;
106 }
107
108 /**
109 * Set the <tt>BundleContext</tt> of the bundle. This method registers a service
110 * listener for LogServices with the framework that are subsequently used to
111 * log messages.
112 *
113 * @param context The context of the bundle.
114 */
115 public static void setContext(final BundleContext context)
116 {
117 LogWrapperLoader.m_singleton.setBundleContext(context);
118
119 try
120 {
121 context.addServiceListener(new ServiceListener()
122 {
123 // Add a newly available LogService reference to the singleton.
124 public void serviceChanged(final ServiceEvent event)
125 {
126 if (ServiceEvent.REGISTERED == event.getType())
127 {
128 LogWrapperLoader.m_singleton.addLoggerRef(
129 event.getServiceReference());
130 }
131 // unregistered services are handled in the next log operation.
132 }
133
134 }, "(" + Constants.OBJECTCLASS
135 + "=org.osgi.service.log.LogService)");
136
137 // Add all available LogService references to the singleton.
138 final ServiceReference[] refs = context.getServiceReferences(
139 "org.osgi.service.log.LogService", null);
140
141 if (null != refs)
142 {
143 for (int i = 0; i < refs.length; i++)
144 {
145 LogWrapperLoader.m_singleton.addLoggerRef(refs[i]);
146 }
147 }
148 } catch (InvalidSyntaxException e)
149 {
150 // this never happens
151 }
152 }
153
154 /*
155 * The private singleton constructor.
156 */
157 LogWrapper()
158 {
159 // Singleton
160 }
161
162 /*
163 * Add a reference to a newly available LogService
164 */
165 void addLoggerRef(final ServiceReference ref)
166 {
167 synchronized (m_loggerRefs)
168 {
169 m_loggerRefs.add(ref);
170 }
171 }
172
173 /*
174 * Set the context of the bundle in the singleton implementation.
175 */
176 private void setBundleContext(final BundleContext context)
177 {
178 synchronized(m_loggerRefs)
179 {
180 m_context = context;
181 }
182 }
183
184 /**
185 * Log a message with the given log level. Note that this will prefix the message
186 * with <tt>EventAdmin: </tt>.
187 *
188 * @param level The log level with which to log the msg.
189 * @param msg The message to log.
190 */
191 public void log(final int level, final String msg)
192 {
193 // The method will remove any unregistered service reference as well.
194 synchronized(m_loggerRefs)
195 {
196 final String logMsg = "EventAdmin: " + msg;
197
198 if (!m_loggerRefs.isEmpty())
199 {
200 // There is at least one LogService available hence, we can use the
201 // class as well.
202 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
203 {
204 org.osgi.service.log.LogService logger =
205 (org.osgi.service.log.LogService) m_context.getService(
206 (ServiceReference) iter.next());
207
208 if (null != logger)
209 {
210 logger.log(level, logMsg);
211 }
212 else
213 {
214 // The context returned null for the reference - it follows
215 // that the service is unregistered and we can remove it
216 iter.remove();
217 }
218 }
219 }
220 else
221 {
222 _log(null, level, logMsg, null);
223 }
224 }
225 }
226
227 /**
228 * Log a message with the given log level and the associated exception. Note that
229 * this will prefix the message with <tt>EventAdmin: </tt>.
230 *
231 * @param level The log level with which to log the msg.
232 * @param msg The message to log.
233 * @param ex The exception associated with the message.
234 */
235 public void log(final int level, final String msg, final Throwable ex)
236 {
237 // The method will remove any unregistered service reference as well.
238 synchronized(m_loggerRefs)
239 {
240 final String logMsg = "EventAdmin: " + msg;
241
242 if (!m_loggerRefs.isEmpty())
243 {
244 // There is at least one LogService available hence, we can use the
245 // class as well.
246 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
247 {
248 org.osgi.service.log.LogService logger =
249 (org.osgi.service.log.LogService) m_context.getService(
250 (ServiceReference) iter.next());
251
252 if (null != logger)
253 {
254 logger.log(level, logMsg, ex);
255 }
256 else
257 {
258 // The context returned null for the reference - it follows
259 // that the service is unregistered and we can remove it
260 iter.remove();
261 }
262 }
263 }
264 else
265 {
266 _log(null, level, logMsg, ex);
267 }
268 }
269 }
270
271 /**
272 * Log a message with the given log level together with the associated service
273 * reference. Note that this will prefix the message with <tt>EventAdmin: </tt>.
274 *
275 * @param sr The reference of the service associated with this message.
276 * @param level The log level with which to log the msg.
277 * @param msg The message to log.
278 */
279 public void log(final ServiceReference sr, final int level, final String msg)
280 {
281 // The method will remove any unregistered service reference as well.
282 synchronized(m_loggerRefs)
283 {
284 final String logMsg = "EventAdmin: " + msg;
285
286 if (!m_loggerRefs.isEmpty())
287 {
288 // There is at least one LogService available hence, we can use the
289 // class as well.
290 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
291 {
292 org.osgi.service.log.LogService logger =
293 (org.osgi.service.log.LogService) m_context.getService(
294 (ServiceReference) iter.next());
295
296 if (null != logger)
297 {
298 logger.log(sr, level, logMsg);
299 }
300 else
301 {
302 // The context returned null for the reference - it follows
303 // that the service is unregistered and we can remove it
304 iter.remove();
305 }
306 }
307 }
308 else
309 {
310 _log(sr, level, logMsg, null);
311 }
312 }
313 }
314
315 /**
316 * Log a message with the given log level, the associated service reference and
317 * exception. Note that this will prefix the message with <tt>EventAdmin: </tt>.
318 *
319 * @param sr The reference of the service associated with this message.
320 * @param level The log level with which to log the msg.
321 * @param msg The message to log.
322 * @param ex The exception associated with the message.
323 */
324 public void log(final ServiceReference sr, final int level, final String msg,
325 final Throwable ex)
326 {
327 // The method will remove any unregistered service reference as well.
328 synchronized(m_loggerRefs)
329 {
330 final String logMsg = "EventAdmin: " + msg;
331
332 if (!m_loggerRefs.isEmpty())
333 {
334 // There is at least one LogService available hence, we can use the
335 // class as well.
336 for (Iterator iter = m_loggerRefs.iterator(); iter.hasNext();)
337 {
338 org.osgi.service.log.LogService logger =
339 (org.osgi.service.log.LogService) m_context.getService(
340 (ServiceReference) iter.next());
341
342 if (null != logger)
343 {
344 logger.log(sr, level, logMsg, ex);
345 }
346 else
347 {
348 // The context returned null for the reference - it follows
349 // that the service is unregistered and we can remove it
350 iter.remove();
351 }
352 }
353 }
354 else
355 {
356 _log(sr, level, logMsg, ex);
357 }
358 }
359 }
360
361 /*
362 * Log the message to standard output. This appends the level to the message.
363 * null values are handled appropriate.
364 */
365 private void _log(final ServiceReference sr, final int level, final String msg,
366 Throwable ex)
367 {
368 String s = (sr == null) ? null : "SvcRef " + sr;
369 s = (s == null) ? msg : s + " " + msg;
370 s = (ex == null) ? s : s + " (" + ex + ")";
371
372 switch (level)
373 {
374 case LOG_DEBUG:
375 System.out.println("DEBUG: " + s);
376 break;
377 case LOG_ERROR:
378 System.out.println("ERROR: " + s);
379 if (ex != null)
380 {
381 if ((ex instanceof BundleException)
382 && (((BundleException) ex).getNestedException() != null))
383 {
384 ex = ((BundleException) ex).getNestedException();
385 }
386
387 ex.printStackTrace();
388 }
389 break;
390 case LOG_INFO:
391 System.out.println("INFO: " + s);
392 break;
393 case LOG_WARNING:
394 System.out.println("WARNING: " + s);
395 break;
396 default:
397 System.out.println("UNKNOWN[" + level + "]: " + s);
398 }
399 }
400}