From a88eb0d85092b114b9709531e99c3f2c359ce4d3 Mon Sep 17 00:00:00 2001 From: Christophe Pache Date: Mon, 28 Jan 2013 16:03:42 +0100 Subject: [PATCH] OSGi EventHandler dynamically listen to LogService More information in logging Conflicts: project/AkkaBuild.scala --- .../akka/osgi/ActorSystemActivator.scala | 35 ++++++-- .../scala/akka/osgi/DefaultOSGiLogger.scala | 83 +++++++++++++++++++ .../akka/osgi/impl/DefaultOSGiLogger.scala | 45 ---------- 3 files changed, 109 insertions(+), 54 deletions(-) create mode 100644 akka-osgi/src/main/scala/akka/osgi/DefaultOSGiLogger.scala delete mode 100644 akka-osgi/src/main/scala/akka/osgi/impl/DefaultOSGiLogger.scala diff --git a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala index b3a1bbe43c..f569a673a4 100644 --- a/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala +++ b/akka-osgi/src/main/scala/akka/osgi/ActorSystemActivator.scala @@ -5,9 +5,8 @@ package akka.osgi import akka.actor.ActorSystem import java.util.{ Dictionary, Properties } -import org.osgi.framework.{ ServiceRegistration, BundleContext, BundleActivator } +import org.osgi.framework._ import org.osgi.service.log.LogService -import org.osgi.util.tracker.ServiceTracker /** * Abstract bundle activator implementation to bootstrap and configure an actor system in an @@ -40,22 +39,40 @@ abstract class ActorSystemActivator extends BundleActivator { */ def start(context: BundleContext): Unit = { system = Some(OsgiActorSystemFactory(context).createActorSystem(Option(getActorSystemName(context)))) - findLogService(context) foreach (log ⇒ system.foreach(sys ⇒ sys.eventStream.publish(log))) + system foreach (addLogServiceListener(context, _)) system foreach (configure(context, _)) } /** - * Finds a LogService in the BundleContext in any + * Adds a LogService Listener that will advertise the ActorSystem on LogService registration and unregistration * * @param context the BundleContext - * @return An option of the LogService in the BundleContext + * @param system the ActorSystem to be advertised */ - def findLogService(context: BundleContext): Option[LogService] = { - val logServiceTracker = new ServiceTracker(context, classOf[LogService].getName, null) - logServiceTracker.open() - Option(logServiceTracker.getService.asInstanceOf[LogService]) + def addLogServiceListener(context: BundleContext, system: ActorSystem) { + val logServiceListner = new ServiceListener { + def serviceChanged(event: ServiceEvent) { + event.getType match { + case ServiceEvent.REGISTERED ⇒ system.eventStream.publish(serviceForReference[LogService](context, event.getServiceReference)) + case ServiceEvent.UNREGISTERING ⇒ system.eventStream.publish(UnregisteringLogService) + } + } + } + val filter = s"(objectclass=${classOf[LogService].getName})" + context.addServiceListener(logServiceListner, filter) + + //Small trick to create an event if the service is registred before this start listing for + Option(context.getServiceReference(classOf[LogService].getName)).foreach(x ⇒ { + logServiceListner.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, x)) + }) } + /** + * Convenience method to find a service by its reference. + */ + def serviceForReference[T](context: BundleContext, reference: ServiceReference): T = + context.getService(reference).asInstanceOf[T] + /** * Shuts down the ActorSystem when the bundle is stopped and, if necessary, unregisters a service registration. * diff --git a/akka-osgi/src/main/scala/akka/osgi/DefaultOSGiLogger.scala b/akka-osgi/src/main/scala/akka/osgi/DefaultOSGiLogger.scala new file mode 100644 index 0000000000..2221ba6300 --- /dev/null +++ b/akka-osgi/src/main/scala/akka/osgi/DefaultOSGiLogger.scala @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2009-2013 Typesafe Inc. + */ +package akka.osgi + +import akka.event.Logging +import org.osgi.service.log.LogService +import akka.event.Logging.{ DefaultLogger, LogEvent } +import akka.event.Logging.Error.NoCause + +/** + * EventHandler for OSGi environment. + * Stands for an interface between akka and the OSGi LogService + * It uses the OSGi LogService to log the received LogEvents + */ +class DefaultOSGiLogger extends DefaultLogger { + + val messageFormat = " %s | %s | %s | %s" + + override def receive: Receive = uninitialisedReceive orElse super.receive + + /** + * Behaviour of the EventHandler that waits for its LogService + * @return Receive: Store LogEvent or become initialised + */ + def uninitialisedReceive: Receive = { + var messagesToLog: Vector[LogEvent] = Vector() + //the Default Logger needs to be aware of the LogService which is published on the EventStream + context.system.eventStream.subscribe(self, classOf[LogService]) + context.system.eventStream.unsubscribe(self, UnregisteringLogService.getClass) + /** + * Logs every already received LogEvent and set the EventHandler ready to log every incoming LogEvent. + * + * @param logService OSGi LogService that has been registered, + */ + def setLogService(logService: LogService) { + messagesToLog.foreach(x ⇒ { + logMessage(logService, x) + }) + context.become(initialisedReceive(logService)) + } + + { + case logService: LogService ⇒ setLogService(logService) + case logEvent: LogEvent ⇒ messagesToLog :+= logEvent + } + } + + /** + * Behaviour of the Eventhanlder that is setup (has received a LogService) + * @param logService registrered OSGi LogService + * @return Receive : Logs LogEvent or go back to the uninitialised state + */ + def initialisedReceive(logService: LogService): Receive = { + context.system.eventStream.subscribe(self, UnregisteringLogService.getClass) + context.system.eventStream.unsubscribe(self, classOf[LogService]) + + { + case logEvent: LogEvent ⇒ logMessage(logService, logEvent) + case UnregisteringLogService ⇒ context.become(uninitialisedReceive) + } + } + + /** + * Logs a message in an OSGi LogService + * + * @param logService OSGi LogService registered and used for logging + * @param event akka LogEvent that is log unsing the LogService + */ + def logMessage(logService: LogService, event: LogEvent) { + event match { + case error: Logging.Error if error.cause != NoCause ⇒ logService.log(event.level.asInt, messageFormat.format(timestamp, event.thread.getName, event.logSource, event.message), error.cause) + case _ ⇒ logService.log(event.level.asInt, messageFormat.format(timestamp, event.thread.getName, event.logSource, event.message)) + } + } + +} + +/** + * Message sent when LogService is unregistred. + * Sent from the ActorSystemActivator to an EventHandler (as DefaultOsgiLogger). + */ +case object UnregisteringLogService diff --git a/akka-osgi/src/main/scala/akka/osgi/impl/DefaultOSGiLogger.scala b/akka-osgi/src/main/scala/akka/osgi/impl/DefaultOSGiLogger.scala deleted file mode 100644 index 2533c19cd9..0000000000 --- a/akka-osgi/src/main/scala/akka/osgi/impl/DefaultOSGiLogger.scala +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (C) 2009-2012 Typesafe Inc. - */ -package akka.osgi.impl - -import akka.event.Logging.{ LogEvent, DefaultLogger } -import org.osgi.service.log.LogService -import akka.event.Logging -import collection.mutable.Buffer - -/** - * EventHandler for OSGi environment. - * Stands for an interface between akka and the OSGi LogService - * It uses the OSGi LogService to log the received LogEvents - */ -class DefaultOSGiLogger extends DefaultLogger { - - //the Default Logger needs to be aware of the LogService which is published on the EventStream - context.system.eventStream.subscribe(self, classOf[LogService]) - - val messageFormat = "[%s] %s" - - var messagesToLog: Vector[LogEvent] = Vector() - - override def receive: Receive = { - //register the log service to use - case logService: LogService ⇒ setLogService(logService) - case logEvent: LogEvent ⇒ messagesToLog = messagesToLog :+ logEvent - } - - def setLogService(logService: LogService) { - messagesToLog.foreach(logMessage(logService, _)) - context.become(receiveEvent(logService)) - } - - def receiveEvent(logService: LogService): Receive = { case logEvent: LogEvent ⇒ logMessage(logService, logEvent) } - - def logMessage(logService: LogService, event: LogEvent) { - event match { - case error: Logging.Error ⇒ logService.log(event.level.asInt, Logging.stackTraceFor(error.cause)) - case _ => logService.log(event.level.asInt, messageFormat.format(event.logSource, event.message)) - } - } - -} \ No newline at end of file