This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to annotated tag org.apache.sling.commons.logservice-1.0.0 in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-logservice.git
commit 3852c89a413b87b702aa66fcb8f50070a81c3c38 Author: Felix Meschberger <[email protected]> AuthorDate: Sun Oct 2 13:32:34 2011 +0000 SLING-2224 Create new LogService bundle git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/logservice@1178187 13f79535-47bb-0310-9956-ffa450edef68 --- README.txt | 66 +++ pom.xml | 115 ++++ .../commons/logservice/internal/Activator.java | 77 +++ .../commons/logservice/internal/LogEntryImpl.java | 71 +++ .../internal/LogReaderServiceFactory.java | 83 +++ .../logservice/internal/LogServiceFactory.java | 97 ++++ .../commons/logservice/internal/LogSupport.java | 586 +++++++++++++++++++++ 7 files changed, 1095 insertions(+) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..45fc4a5 --- /dev/null +++ b/README.txt @@ -0,0 +1,66 @@ +Apache Sling OSGi LogService Implementation + + +================================================= +Welcome to Sling - OSGi LogService Implementation +================================================= + +The "logservice" project implements the OSGi LogService imple,entation ontop +of the SLF4J logging API. This bundle should be installed as one of the first +modules in the OSGi framework along with the SLF4J API and implementation and +- provided the framework supports start levels - be set to start at start +level 1. This ensures the Logging bundle is loaded as early as possible thus +providing services to the framework and preparing logging. + +See the Apache Sling web site (http://sling.apache.org) for +documentation and other information. You are welcome to join the +Sling mailing lists (http://sling.apache.org/site/project-information.html) +to discuss this component and to use the Sling issue tracker +(http://issues.apache.org/jira/browse/SLING) to report issues or request +new features. + +Apache Sling is a project of the Apache Software Foundation +(http://www.apache.org). + +License (see also LICENSE) +========================== + +Collective work: Copyright 2007 The Apache Software Foundation. + +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. + +Getting Started +=============== + +This component uses a Maven 2 (http://maven.apache.org/) build +environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/) +2.0.7 or later. We recommend to use the latest Maven version. + +If you have Maven 2 installed, you can compile and +package the jar using the following command: + + mvn package + +See the Maven 2 documentation for other build features. + +The latest source code for this component is available in the +Subversion (http://subversion.tigris.org/) source repository of +the Apache Software Foundation. If you have Subversion installed, +you can checkout the latest source using the following command: + + svn checkout http://svn.apache.org/repos/asf/sling/trunk/commons/log + +See the Subversion documentation for other source control features. diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..de63690 --- /dev/null +++ b/pom.xml @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>12</version> + <relativePath>../../../parent/pom.xml</relativePath> + </parent> + + <artifactId>org.apache.sling.commons.logservice</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>Apache Sling OSGi LogService Implementation</name> + <description> + Implementation of the OSGi Compendium Log Service using SLF4J + as the actual logging backend. + </description> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/commons/logservice</connection> + <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/commons/logservice</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/bundles/commons/logservice</url> + </scm> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-Activator> + org.apache.sling.commons.logservice.internal.Activator + </Bundle-Activator> + <Bundle-DocURL> + http://sling.apache.org/site/logging.html + </Bundle-DocURL> + <Export-Package> + org.osgi.service.log + </Export-Package> + <Private-Package> + org.apache.sling.commons.logservice.*, + </Private-Package> + <Import-Package> + org.osgi.service.log;version="[$(version;==;$(@)),$(version;=+;$(@)))", + * + </Import-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <configuration> + <!-- No javadocs at all --> + <skip>true</skip> + </configuration> + </plugin> + </plugins> + </reporting> + + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>1.5.0</version> + <scope>provided</scope> + </dependency> + + <!-- OSGi Libraries not included here --> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + <version>4.0.0</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + <version>4.2.0</version> + </dependency> + + <!-- testing --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/org/apache/sling/commons/logservice/internal/Activator.java b/src/main/java/org/apache/sling/commons/logservice/internal/Activator.java new file mode 100644 index 0000000..60766b5 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/logservice/internal/Activator.java @@ -0,0 +1,77 @@ +/* + * 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.sling.commons.logservice.internal; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.service.log.LogReaderService; +import org.osgi.service.log.LogService; + +/** + * The <code>Activator</code> class is the <code>BundleActivator</code> for the + * log service bundle. This activator registers the <code>LogService</code> and + * <code>LogReaderService</code>. + */ +public class Activator implements BundleActivator { + + private static final String VENDOR = "The Apache Software Foundation"; + + private LogSupport logSupport; + + /** + * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) + */ + public void start(final BundleContext context) throws Exception { + logSupport = new LogSupport(); + context.addBundleListener(logSupport); + context.addFrameworkListener(logSupport); + context.addServiceListener(logSupport); + + LogServiceFactory lsf = new LogServiceFactory(logSupport); + Dictionary<String, String> props = new Hashtable<String, String>(); + props.put(Constants.SERVICE_PID, lsf.getClass().getName()); + props.put(Constants.SERVICE_DESCRIPTION, + "Apache Sling LogService implementation"); + props.put(Constants.SERVICE_VENDOR, VENDOR); + context.registerService(LogService.class.getName(), lsf, props); + + LogReaderServiceFactory lrsf = new LogReaderServiceFactory(logSupport); + props = new Hashtable<String, String>(); + props.put(Constants.SERVICE_PID, lrsf.getClass().getName()); + props.put(Constants.SERVICE_DESCRIPTION, + "Apache Sling LogReaderService implementation"); + props.put(Constants.SERVICE_VENDOR, VENDOR); + context.registerService(LogReaderService.class.getName(), lrsf, props); + } + + /** + * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) + */ + public void stop(final BundleContext context) throws Exception { + if (logSupport != null) { + context.removeBundleListener(logSupport); + context.removeFrameworkListener(logSupport); + context.removeServiceListener(logSupport); + logSupport.shutdown(); + logSupport = null; + } + } +} diff --git a/src/main/java/org/apache/sling/commons/logservice/internal/LogEntryImpl.java b/src/main/java/org/apache/sling/commons/logservice/internal/LogEntryImpl.java new file mode 100644 index 0000000..a317293 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/logservice/internal/LogEntryImpl.java @@ -0,0 +1,71 @@ +/* + * 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.sling.commons.logservice.internal; + +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceReference; +import org.osgi.service.log.LogEntry; + +public class LogEntryImpl implements LogEntry { + + private final Bundle bundle; + + private final ServiceReference serviceReference; + + private final int level; + + private final String message; + + private final Throwable exception; + + private final long time; + + /* package */LogEntryImpl(Bundle bundle, + ServiceReference serviceReference, int level, String message, + Throwable exception) { + this.bundle = bundle; + this.serviceReference = serviceReference; + this.level = level; + this.message = message; + this.exception = exception; + this.time = System.currentTimeMillis(); + } + + public Bundle getBundle() { + return this.bundle; + } + + public ServiceReference getServiceReference() { + return this.serviceReference; + } + + public int getLevel() { + return this.level; + } + + public String getMessage() { + return this.message; + } + + public Throwable getException() { + return this.exception; + } + + public long getTime() { + return this.time; + } +} diff --git a/src/main/java/org/apache/sling/commons/logservice/internal/LogReaderServiceFactory.java b/src/main/java/org/apache/sling/commons/logservice/internal/LogReaderServiceFactory.java new file mode 100644 index 0000000..79791e4 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/logservice/internal/LogReaderServiceFactory.java @@ -0,0 +1,83 @@ +/* + * 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.sling.commons.logservice.internal; + +import java.util.Enumeration; + +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.log.LogListener; +import org.osgi.service.log.LogReaderService; + +/** + * The <code>LogReaderServiceFactory</code> is the service factory for + * <code>LogReader</code> service instances supplied to bundles. + * <p> + * <blockquote> When a bundle which registers a LogListener object is stopped or + * otherwise releases the Log Reader Service, the Log Reader Service must remove + * all of the bundle's listeners.</blockquote> + */ +public class LogReaderServiceFactory implements ServiceFactory { + + private LogSupport logSupport; + + LogReaderServiceFactory(LogSupport logSupport) { + this.logSupport = logSupport; + } + + // ---------- ServiceFactory interface ------------------------------------ + + public Object getService(Bundle bundle, ServiceRegistration registration) { + return new LogReaderServiceImpl(bundle); + } + + public void ungetService(Bundle bundle, ServiceRegistration registration, + Object service) { + ((LogReaderServiceImpl) service).shutdown(); + } + + // --------- internal LogReaderService implementation ---------------------- + + private class LogReaderServiceImpl implements LogReaderService { + + private Bundle bundle; + + /* package */LogReaderServiceImpl(Bundle bundle) { + this.bundle = bundle; + } + + /* package */void shutdown() { + LogReaderServiceFactory.this.logSupport.removeLogListeners(this.bundle); + } + + public void addLogListener(LogListener listener) { + LogReaderServiceFactory.this.logSupport.addLogListener(this.bundle, + listener); + } + + public void removeLogListener(LogListener listener) { + LogReaderServiceFactory.this.logSupport.removeLogListener(listener); + } + + @SuppressWarnings("rawtypes") + public Enumeration getLog() { + return LogReaderServiceFactory.this.logSupport.getLog(); + } + } + +} diff --git a/src/main/java/org/apache/sling/commons/logservice/internal/LogServiceFactory.java b/src/main/java/org/apache/sling/commons/logservice/internal/LogServiceFactory.java new file mode 100644 index 0000000..e62faf8 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/logservice/internal/LogServiceFactory.java @@ -0,0 +1,97 @@ +/* + * 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.sling.commons.logservice.internal; + +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogService; + +/** + * The <code>LogServiceFactory</code> implements the OSGi Log Service + * specification and provides the functionality for the logging system. This + * service should be one of the first services loaded in the system. + */ +public class LogServiceFactory implements ServiceFactory { + + private LogSupport logSupport; + + /** + * Initializes the logging system with settings from some startup properties + * before the real configuration is read after ContentBus bootstrap. + * + * @param properties The startup properties to initialize the logging system + * with. + */ + LogServiceFactory(LogSupport logSupport) { + this.logSupport = logSupport; + + } + + // ---------- ServiceFactory + + public Object getService(Bundle bundle, ServiceRegistration registration) { + return new LogServiceImpl(bundle); + } + + public void ungetService(Bundle bundle, ServiceRegistration registration, + Object service) { + // nothing to do currently + } + + private class LogServiceImpl implements LogService { + + private Bundle bundle; + + /** + * Initializes the logging system with settings from some startup + * properties before the real configuration is read after ContentBus + * bootstrap. + * + * @param properties The startup properties to initialize the logging + * system with. + */ + /* package */LogServiceImpl(Bundle bundle) { + this.bundle = bundle; + } + + // ---------- LogService + + public void log(int level, String message) { + this.log(null, level, message, null); + } + + public void log(int level, String message, Throwable exception) { + this.log(null, level, message, exception); + } + + public void log(ServiceReference sr, int level, String message) { + this.log(sr, level, message, null); + } + + public void log(ServiceReference sr, int level, String message, + Throwable exception) { + // simply fire a log event + LogEntry entry = new LogEntryImpl(this.bundle, sr, level, message, + exception); + LogServiceFactory.this.logSupport.fireLogEvent(entry); + } + } + +} diff --git a/src/main/java/org/apache/sling/commons/logservice/internal/LogSupport.java b/src/main/java/org/apache/sling/commons/logservice/internal/LogSupport.java new file mode 100644 index 0000000..8a78926 --- /dev/null +++ b/src/main/java/org/apache/sling/commons/logservice/internal/LogSupport.java @@ -0,0 +1,586 @@ +/* + * 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.sling.commons.logservice.internal; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleException; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkListener; +import org.osgi.framework.ServiceEvent; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentConstants; +import org.osgi.service.log.LogEntry; +import org.osgi.service.log.LogListener; +import org.osgi.service.log.LogService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The <code>LogReaderServiceFactory</code> TODO + */ +public class LogSupport implements BundleListener, ServiceListener, + FrameworkListener { + + /** + * The service property name of the component name (value is + * "component.name"). Note: We use a private constant here to not create a + * unneded dependency on the org.osgi.service.component package. + */ + private static final String COMPONENT_NAME = ComponentConstants.COMPONENT_NAME; // "component.name"; + + /** + * The empty enumeration currently returned on the {@link #getLog()} call + * because we do not currently record the log events. + */ + private final Enumeration<?> EMPTY = Collections.enumeration(Collections.emptyList()); + + // The registered LogListeners + private LogListenerProxy[] listeners; + + // The lock used to guard concurrent access to the listeners array + private final Object listenersLock = new Object(); + + // The loggers by bundle id used for logging messages originated from + // specific bundles + private Map<Long, Logger> loggers = new HashMap<Long, Logger>(); + + // the worker thread actually sending LogEvents to LogListeners + private LogEntryDispatcher logEntryDispatcher; + + /* package */LogSupport() { + logEntryDispatcher = new LogEntryDispatcher(this); + logEntryDispatcher.start(); + } + + /* package */void shutdown() { + + // terminate the dispatcher and wait for its termination here + logEntryDispatcher.terminate(); + try { + logEntryDispatcher.join(1000L); + } catch (InterruptedException ie) { + // don't care + } + + // drop all listeners + synchronized (listenersLock) { + listeners = null; + } + } + + // ---------- LogReaderService interface ----------------------------------- + + /* package */void addLogListener(Bundle bundle, LogListener listener) { + synchronized (listenersLock) { + LogListenerProxy llp = new LogListenerProxy(bundle, listener); + if (listeners == null) { + listeners = new LogListenerProxy[] { llp }; + } else if (getListener(listener) < 0) { + LogListenerProxy[] newListeners = new LogListenerProxy[listeners.length + 1]; + System.arraycopy(listeners, 0, newListeners, 0, + listeners.length); + newListeners[listeners.length] = llp; + listeners = newListeners; + } + } + } + + /* package */void removeLogListener(LogListener listener) { + synchronized (listenersLock) { + // no listeners registered, nothing to do + if (listeners == null) { + return; + } + + // listener is not registered, nothing to do + int idx = getListener(listener); + if (idx < 0) { + return; + } + + LogListenerProxy[] newListeners = new LogListenerProxy[listeners.length - 1]; + if (idx > 0) { + System.arraycopy(listeners, 0, newListeners, 0, idx); + } + if (idx < listeners.length) { + System.arraycopy(listeners, idx + 1, newListeners, 0, + newListeners.length - idx); + } + listeners = newListeners; + } + } + + /** + * Removes all registered LogListeners belonging to the given bundle. This + * is the task required by the specification from a Log Service + * implemenation: + * <p> + * <blockquote> When a bundle which registers a LogListener object is + * stopped or otherwise releases the Log Reader Service, the Log Reader + * Service must remove all of the bundle's listeners.</blockquote> + * <p> + * + * @param bundle The bundle whose listeners are to be removed. + */ + /* package */void removeLogListeners(Bundle bundle) { + // grab an immediate copy of the array + LogListenerProxy[] current = getListeners(); + if (current == null) { + return; + } + + // check for listeners by bundle + for (int i = 0; i < current.length; i++) { + if (current[i].hasBundle(bundle)) { + removeLogListener(current[i]); + } + } + } + + private int getListener(LogListener listener) { + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + if (listeners[i].isSame(listener)) { + return i; + } + } + } + + // fall back to not found + return -1; + } + + /** + * Returns the currently registered LogListeners + */ + private LogListenerProxy[] getListeners() { + synchronized (listenersLock) { + return listeners; + } + } + + /** + * Returns an empty enumeration for now because we do not implement log + * entry recording for the moment. + */ + Enumeration<?> getLog() { + return EMPTY; + } + + // ---------- Firing a log event ------------------------------------------- + + /** + * Logs the given log entry to the log file and enqueues for the dispatching + * to the registered LogListeners in a separate worker thread. + */ + /* package */void fireLogEvent(LogEntry logEntry) { + + // actually log it to SLF4J + logOut(logEntry); + + // enqueue for asynchronous delivery + logEntryDispatcher.enqueLogEntry(logEntry); + } + + // ---------- BundleListener ----------------------------------------------- + + /** + * Listens for Bundle events and logs the respective events according to the + * Log Service specification. In addition, all LogListener instances + * registered for stopped bundles are removed by this method. + */ + public void bundleChanged(BundleEvent event) { + String message; + switch (event.getType()) { + case BundleEvent.INSTALLED: + message = "BundleEvent INSTALLED"; + break; + case BundleEvent.STARTED: + message = "BundleEvent STARTED"; + break; + case BundleEvent.STOPPED: + // this is special, as we have to fix the listener list for + // stopped bundles + removeLogListeners(event.getBundle()); + message = "BundleEvent STOPPED"; + break; + case BundleEvent.UPDATED: + message = "BundleEvent UPDATED"; + break; + case BundleEvent.UNINSTALLED: + message = "BundleEvent UNINSTALLED"; + break; + case BundleEvent.RESOLVED: + message = "BundleEvent RESOLVED"; + break; + case BundleEvent.UNRESOLVED: + message = "BundleEvent UNRESOLVED"; + break; + default: + message = "BundleEvent " + event.getType(); + } + + LogEntry entry = new LogEntryImpl(event.getBundle(), null, + LogService.LOG_INFO, message, null); + fireLogEvent(entry); + } + + // ---------- ServiceListener ---------------------------------------------- + + /** + * Listens for Service events and logs the respective events according to + * the Log Service specification. + */ + public void serviceChanged(ServiceEvent event) { + int level = LogService.LOG_INFO; + String message; + switch (event.getType()) { + case ServiceEvent.REGISTERED: + message = "ServiceEvent REGISTERED"; + break; + case ServiceEvent.MODIFIED: + message = "ServiceEvent MODIFIED"; + level = LogService.LOG_DEBUG; + break; + case ServiceEvent.UNREGISTERING: + message = "ServiceEvent UNREGISTERING"; + break; + default: + message = "ServiceEvent " + event.getType(); + } + + String s = (event.getServiceReference().getBundle() == null) + ? null + : "Bundle " + event.getServiceReference().getBundle(); + s = (s == null) ? message : s + " " + message; + + LogEntry entry = new LogEntryImpl( + event.getServiceReference().getBundle(), + event.getServiceReference(), level, message, null); + fireLogEvent(entry); + } + + // ---------- FrameworkListener -------------------------------------------- + + /** + * Listens for Framework events and logs the respective events according to + * the Log Service specification. + * <p> + * In the case of a Framework ERROR which is a ClassNotFoundException for an + * unresolved bundle, the message is logged at INFO level instead of ERROR + * level as prescribed by the spec. This is because such a situation should + * not really result in a Framework ERROR but the Apache Felix framework has + * no means of controlling this at the moment (framework 1.0.4 release). + */ + public void frameworkEvent(FrameworkEvent event) { + int level = LogService.LOG_INFO; + String message; + Throwable exception = event.getThrowable(); + switch (event.getType()) { + case FrameworkEvent.STARTED: + message = "FrameworkEvent STARTED"; + break; + case FrameworkEvent.ERROR: + message = "FrameworkEvent ERROR"; + + // special precaution for Felix.loadBundleClass event overkill + // FIXME: actually, the error is ok, if the bundle failed to + // resolve + if (exception instanceof BundleException) { + StackTraceElement[] ste = exception.getStackTrace(); + if (ste != null && ste.length > 0 + && "loadBundleClass".equals(ste[0].getMethodName())) { + message += ": Class " + exception.getMessage() + + " not found"; + if (event.getBundle() != null) { + message += " in bundle " + + event.getBundle().getSymbolicName() + " (" + + event.getBundle().getBundleId() + ")"; + } + level = LogService.LOG_INFO; + exception = null; // don't care for a stack trace here + break; + } + } + + level = LogService.LOG_ERROR; + break; + case FrameworkEvent.PACKAGES_REFRESHED: + message = "FrameworkEvent PACKAGES REFRESHED"; + break; + case FrameworkEvent.STARTLEVEL_CHANGED: + message = "FrameworkEvent STARTLEVEL CHANGED"; + break; + case FrameworkEvent.WARNING: + message = "FrameworkEvent WARNING"; + break; + case FrameworkEvent.INFO: + message = "FrameworkEvent INFO"; + break; + default: + message = "FrameworkEvent " + event.getType(); + } + + String s = (event.getBundle() == null) ? null : "Bundle " + + event.getBundle(); + s = (s == null) ? message : s + " " + message; + + LogEntry entry = new LogEntryImpl(event.getBundle(), null, level, + message, exception); + fireLogEvent(entry); + } + + // ---------- Effective logging -------------------------------------------- + + /** + * Get a logger for messages orginating from the given bundle. If no bundle + * is specified, we use the system bundle logger. + * + * @param bundle The bundle for which a logger is to be returned. + * @return The Logger for the bundle. + */ + private Logger getLogger(Bundle bundle) { + Long bundleId = new Long((bundle == null) ? 0 : bundle.getBundleId()); + Logger log = loggers.get(bundleId); + if (log == null) { + + String name; + if (bundle == null) { + + // if we have no bundle, use the system bundle's name + name = Constants.SYSTEM_BUNDLE_SYMBOLICNAME; + + } else { + + // otherwise use the bundle symbolic name + name = bundle.getSymbolicName(); + + // if the bundle has no symbolic name, use the location + if (name == null) { + name = bundle.getLocation(); + } + + // if the bundle also has no location, use the bundle Id + if (name == null) { + name = String.valueOf(bundle.getBundleId()); + } + } + + log = LoggerFactory.getLogger(name); + loggers.put(bundleId, log); + } + return log; + } + + /** + * Actually logs the given log entry to the logger for the bundle recorded + * in the log entry. + */ + private void logOut(LogEntry logEntry) { + // /* package */ void logOut(Bundle bundle, ServiceReference sr, int + // level, String message, Throwable exception) { + + // get the logger for the bundle + Logger log = getLogger(logEntry.getBundle()); + + StringBuffer msg = new StringBuffer(); + + ServiceReference sr = logEntry.getServiceReference(); + if (sr != null) { + msg.append("Service ["); + if (sr.getProperty(Constants.SERVICE_PID) != null) { + msg.append(sr.getProperty(Constants.SERVICE_PID)).append(','); + } else if (sr.getProperty(COMPONENT_NAME) != null) { + msg.append(sr.getProperty(COMPONENT_NAME)).append(','); + } else if (sr.getProperty(Constants.SERVICE_DESCRIPTION) != null) { + msg.append(sr.getProperty(Constants.SERVICE_DESCRIPTION)).append( + ','); + } + msg.append(sr.getProperty(Constants.SERVICE_ID)).append("] "); + } + + if (logEntry.getMessage() != null) { + msg.append(logEntry.getMessage()); + } + + Throwable exception = logEntry.getException(); + if (exception != null) { + msg.append(" (").append(exception).append(')'); + } + + String message = msg.toString(); + switch (logEntry.getLevel()) { + case LogService.LOG_DEBUG: + log.debug(message, exception); + break; + case LogService.LOG_INFO: + log.info(message, exception); + break; + case LogService.LOG_WARNING: + log.warn(message, exception); + break; + case LogService.LOG_ERROR: + log.error(message, exception); + break; + default: + if (logEntry.getLevel() > LogService.LOG_DEBUG) { + log.trace(message, exception); + } else if (logEntry.getLevel() < LogService.LOG_ERROR) { + log.error(message, exception); + } + break; + } + } + + // ---------- internal class ----------------------------------------------- + + /** + * The <code>LogListenerProxy</code> class is a proxy to the actually + * registered <code>LogListener</code> which also records the bundle + * registering the listener. This allows for the removal of the log + * listeners registered by bundles which have not been removed before the + * bundle has been stopped. + */ + private static class LogListenerProxy implements LogListener { + + private final int runningBundle = Bundle.STARTING | Bundle.ACTIVE + | Bundle.STOPPING; + + private final Bundle bundle; + + private final LogListener delegatee; + + public LogListenerProxy(Bundle bundle, LogListener delegatee) { + this.bundle = bundle; + this.delegatee = delegatee; + } + + public void logged(LogEntry entry) { + if ((bundle.getState() & runningBundle) != 0) { + delegatee.logged(entry); + } + } + + /* package */boolean isSame(LogListener listener) { + return listener == delegatee || listener == this; + } + + /* package */boolean hasBundle(Bundle bundle) { + return this.bundle == bundle; + } + } + + /** + * The <code>LogEntryDispatcher</code> implements the worker thread + * responsible for delivering log events to the log listeners. + */ + private static class LogEntryDispatcher extends Thread { + + // provides the actual log listeners on demand + private final LogSupport logSupport; + + // the queue of log events to be dispatched + private final BlockingQueue<LogEntry> dispatchQueue; + + // true as long as the thread is active + private boolean active; + + LogEntryDispatcher(LogSupport logSupport) { + super("LogEntry Dispatcher"); + + this.logSupport = logSupport; + this.dispatchQueue = new LinkedBlockingQueue<LogEntry>(); + this.active = true; + } + + /** + * Add a log entry for dispatching. + */ + void enqueLogEntry(LogEntry logEntry) { + dispatchQueue.offer(logEntry); + } + + /** + * Get the next log entry for dispatching. This method blocks until an + * event is available or the thread is interrupted. + * + * @return The next event to dispatch + * @throws InterruptedException If the thread has been interrupted while + * waiting for a log event to dispatch. + */ + LogEntry dequeueLogEntry() throws InterruptedException { + return dispatchQueue.take(); + } + + /** + * Terminates this work thread by resetting the active flag and + * interrupting itself such that the {@link #dequeueLogEntry()} is + * aborted for the thread to terminate. + */ + void terminate() { + active = false; + interrupt(); + } + + /** + * Runs the actual log event dispatching. This method continues to get + * log events from the {@link #dequeueLogEntry()} method until the + * active flag is reset. + */ + @Override + public void run() { + while (active) { + + LogEntry logEntry = null; + try { + logEntry = dequeueLogEntry(); + } catch (InterruptedException ie) { + // don't care, this is expected + } + + // dispatch the log entry + if (logEntry != null) { + + // grab an immediate copy of the array + LogListener[] logListeners = logSupport.getListeners(); + + // fire the events outside of the listenersLock + if (logListeners != null) { + for (LogListener logListener : logListeners) { + try { + logListener.logged(logEntry); + } catch (Throwable t) { + // should we really care ?? + } + } + } + } + } + } + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
