SLING-8646 : Remove LogService implementation
diff --git a/pom.xml b/pom.xml
index fb5edc7..a39ed5a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -57,16 +57,6 @@
<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;provide:=true,
- *
- </Import-Package>
</instructions>
</configuration>
</plugin>
@@ -98,15 +88,22 @@
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
- <version>4.2.0</version>
+ <version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- <version>4.2.0</version>
+ <artifactId>org.osgi.service.log</artifactId>
+ <version>1.4.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.service.component</artifactId>
+ <version>1.3.0</version>
+ <scope>provided</scope>
</dependency>
- <!-- testing -->
+ <!-- testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
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
index 60a4755..6d9bf64 100644
--- a/src/main/java/org/apache/sling/commons/logservice/internal/Activator.java
+++ b/src/main/java/org/apache/sling/commons/logservice/internal/Activator.java
@@ -16,16 +16,12 @@
*/
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.framework.ServiceReference;
import org.osgi.service.log.LogReaderService;
-import org.osgi.service.log.LogService;
-import org.osgi.service.startlevel.StartLevel;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* The <code>Activator</code> class is the <code>BundleActivator</code> for the
@@ -34,55 +30,51 @@
*/
public class Activator implements BundleActivator {
- private static final String VENDOR = "The Apache Software Foundation";
-
- private LogSupport logSupport;
-
- /** Reference to the start level service. */
- private ServiceReference startLevelRef;
+ private volatile ServiceTracker<LogReaderService, LogReaderService> logReaderTracker;
/**
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
*/
+ @Override
public void start(final BundleContext context) throws Exception {
- // get start level service, it's always there (required by the spec)
- startLevelRef = context.getServiceReference(StartLevel.class.getName());
+ final SLF4JSupport listener = new SLF4JSupport();
+ this.logReaderTracker = new ServiceTracker<>(context, LogReaderService.class,
+ new ServiceTrackerCustomizer<LogReaderService, LogReaderService>() {
- logSupport = new LogSupport((StartLevel)context.getService(startLevelRef));
- context.addBundleListener(logSupport);
- context.addFrameworkListener(logSupport);
- context.addServiceListener(logSupport);
+ @Override
+ public LogReaderService addingService(final ServiceReference<LogReaderService> reference) {
+ final LogReaderService srvc = context.getService(reference);
+ if (srvc != null) {
+ srvc.addLogListener(listener);
+ listener.replay(srvc.getLog());
+ }
+ return srvc;
+ }
- 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);
+ @Override
+ public void modifiedService(final ServiceReference<LogReaderService> reference,
+ final LogReaderService service) {
+ // nothing to do
+ }
- 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);
+ @Override
+ public void removedService(final ServiceReference<LogReaderService> reference,
+ final LogReaderService service) {
+ service.removeLogListener(listener);
+
+ }
+ });
+ this.logReaderTracker.open();
}
/**
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
+ @Override
public void stop(final BundleContext context) throws Exception {
- if ( startLevelRef != null ) {
- context.ungetService(startLevelRef);
- }
- if (logSupport != null) {
- context.removeBundleListener(logSupport);
- context.removeFrameworkListener(logSupport);
- context.removeServiceListener(logSupport);
- logSupport.shutdown();
- logSupport = null;
+ if (this.logReaderTracker != null) {
+ this.logReaderTracker.close();
+ this.logReaderTracker = 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
deleted file mode 100644
index a317293..0000000
--- a/src/main/java/org/apache/sling/commons/logservice/internal/LogEntryImpl.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 79791e4..0000000
--- a/src/main/java/org/apache/sling/commons/logservice/internal/LogReaderServiceFactory.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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
deleted file mode 100644
index e62faf8..0000000
--- a/src/main/java/org/apache/sling/commons/logservice/internal/LogServiceFactory.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 00be9c3..0000000
--- a/src/main/java/org/apache/sling/commons/logservice/internal/LogSupport.java
+++ /dev/null
@@ -1,630 +0,0 @@
-/*
- * 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.Arrays;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.LinkedHashMap;
-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.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.framework.SynchronousBundleListener;
-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.osgi.service.startlevel.StartLevel;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * The <code>LogReaderServiceFactory</code> TODO
- */
-public class LogSupport implements SynchronousBundleListener, 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
- @SuppressWarnings("serial")
- private final Map<Long, Logger> loggers = new LinkedHashMap<Long, Logger>(16,
- 0.75f, true) {
- private static final int MAX_SIZE = 50;
-
- @Override
- protected boolean removeEldestEntry(Map.Entry<Long, Logger> eldest) {
- return size() > MAX_SIZE;
- }
- };
-
- // the worker thread actually sending LogEvents to LogListeners
- private final LogEntryDispatcher logEntryDispatcher;
-
- private final StartLevel startLevelService;
-
- /* package */LogSupport(final StartLevel startLevelService) {
- logEntryDispatcher = new LogEntryDispatcher(this);
- logEntryDispatcher.start();
- this.startLevelService = startLevelService;
- }
-
- /* 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.RESOLVED:
- message = "BundleEvent RESOLVED";
- break;
- case BundleEvent.STARTING:
- message = "BundleEvent STARTING";
- break;
- case BundleEvent.STARTED:
- message = "BundleEvent STARTED";
- break;
- case BundleEvent.STOPPING:
- message = "BundleEvent STOPPING";
- 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.UNRESOLVED:
- message = "BundleEvent UNRESOLVED";
- break;
- case BundleEvent.UPDATED:
- message = "BundleEvent UPDATED";
- break;
- case BundleEvent.UNINSTALLED:
- // remove any cached logger for the uninstalled bundle
- ungetLogger(event.getBundle());
- message = "BundleEvent UNINSTALLED";
- 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();
- }
-
- 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 to " + this.startLevelService.getStartLevel();
- break;
- case FrameworkEvent.WARNING:
- message = "FrameworkEvent WARNING";
- break;
- case FrameworkEvent.INFO:
- message = "FrameworkEvent INFO";
- break;
- default:
- message = "FrameworkEvent " + event.getType();
- }
-
- final 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;
- synchronized (loggers) {
- 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);
- synchronized (loggers) {
- loggers.put(bundleId, log);
- }
- }
- return log;
- }
-
- /**
- * Removes the cached logger for the given bundle, for example if the
- * bundle is uninstalled and thus there will be no more logs from this
- * bundle.
- */
- private void ungetLogger(Bundle bundle) {
- synchronized (loggers) {
- loggers.remove(bundle.getBundleId());
- }
- }
-
- /**
- * Actually logs the given log entry to the logger for the bundle recorded
- * in the log entry.
- */
- private void logOut(LogEntry logEntry) {
- // get the logger for the bundle
- Logger log = getLogger(logEntry.getBundle());
- if (logEntry.getLevel() > getLevel(log))
- // early Exit, this message will not be logged, don't do any work...
- return;
-
- final StringBuilder msg = new StringBuilder();
-
- 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(", ")
- .append(Arrays.toString((String[]) sr.getProperty(Constants.OBJECTCLASS)))
- .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;
- }
- }
-
- static int getLevel(Logger log) {
- if (log.isTraceEnabled())
- return LogService.LOG_DEBUG + 1; // No constant for trace in LogService
- else if (log.isDebugEnabled())
- return LogService.LOG_DEBUG;
- else if (log.isInfoEnabled())
- return LogService.LOG_INFO;
- else if (log.isWarnEnabled())
- return LogService.LOG_WARNING;
- return LogService.LOG_ERROR;
- }
-
- // ---------- 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 ??
- }
- }
- }
- }
- }
- }
- }
-}
diff --git a/src/main/java/org/apache/sling/commons/logservice/internal/SLF4JSupport.java b/src/main/java/org/apache/sling/commons/logservice/internal/SLF4JSupport.java
new file mode 100644
index 0000000..4a7b96c
--- /dev/null
+++ b/src/main/java/org/apache/sling/commons/logservice/internal/SLF4JSupport.java
@@ -0,0 +1,171 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The {@code SLF4JSupport} uses slf4j API to output all log entries.
+ */
+public class SLF4JSupport implements LogListener {
+
+ @Override
+ public void logged(final LogEntry logEntry) {
+ doLog(logEntry);
+ }
+
+ private void doLog(final LogEntry logEntry) {
+ // get the logger for the bundle
+ final boolean isLogService = "LogService".equals(logEntry.getLoggerName());
+ final Logger logger = LoggerFactory
+ .getLogger(isLogService ? getLoggerName(logEntry.getBundle()) : logEntry.getLoggerName());
+ if (!isEnabled(logger, logEntry)) {
+ // early Exit, this message will not be logged, don't do any work...
+ return;
+ }
+ logOut(logger, logEntry);
+ }
+
+ /**
+ * The service property name of the component name (value is "component.name").
+ * Note: We use a private constant here to not create a unneeded dependency on
+ * the org.osgi.service.component package.
+ */
+ private static final String COMPONENT_NAME = ComponentConstants.COMPONENT_NAME; // "component.name";
+
+ private String getLoggerName(final Bundle bundle) {
+ 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());
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Actually logs the given log entry to the logger
+ */
+ private synchronized void logOut(final Logger logger, final LogEntry logEntry) {
+
+ final StringBuilder msg = new StringBuilder();
+
+ 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(", ")
+ .append(Arrays.toString((String[]) sr.getProperty(Constants.OBJECTCLASS)))
+ .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.getLogLevel()) {
+ case DEBUG:
+ logger.debug(message, exception);
+ break;
+ case INFO:
+ logger.info(message, exception);
+ break;
+ case WARN:
+ logger.warn(message, exception);
+ break;
+ case ERROR:
+ logger.error(message, exception);
+ break;
+ case TRACE:
+ logger.trace(message, exception);
+ break;
+ case AUDIT: // we treat audit as info for now as there is no audit in slf4j (TODO)
+ logger.info(message, exception);
+ break;
+ }
+ }
+
+ static boolean isEnabled(final Logger logger, final LogEntry logEntry) {
+ switch (logEntry.getLogLevel()) {
+ case DEBUG:
+ return logger.isDebugEnabled();
+ case INFO:
+ return logger.isInfoEnabled();
+ case WARN:
+ return logger.isWarnEnabled();
+ case ERROR:
+ return logger.isErrorEnabled();
+ case TRACE:
+ return logger.isTraceEnabled();
+ case AUDIT: // we treat audit as info for now as there is no audit in slf4j (TODO)
+ return logger.isInfoEnabled();
+ }
+ return false;
+ }
+
+ public synchronized void replay(final Enumeration<LogEntry> log) {
+ // the enumeration contains the most recent entries first
+ final List<LogEntry> entries = new ArrayList<>(Collections.list(log));
+ Collections.reverse(entries);
+ for (final LogEntry entry : entries) {
+ doLog(entry);
+ }
+ }
+}
diff --git a/src/test/java/org/apache/sling/commons/logservice/internal/LogSupportTest.java b/src/test/java/org/apache/sling/commons/logservice/internal/LogSupportTest.java
deleted file mode 100644
index 7bf0c2f..0000000
--- a/src/test/java/org/apache/sling/commons/logservice/internal/LogSupportTest.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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 static org.junit.Assert.assertEquals;
-
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleEvent;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.FrameworkEvent;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.log.LogEntry;
-import org.osgi.service.log.LogService;
-import org.osgi.service.startlevel.StartLevel;
-import org.slf4j.Logger;
-
-public class LogSupportTest {
-
- private Bundle bundle;
- private LogSupport logSupport;
- private Logger testLogger;
-
- @Before
- @SuppressWarnings("unchecked")
- public void prepare() throws Exception {
-
- bundle = Mockito.mock(Bundle.class);
- Mockito.when(bundle.getSymbolicName()).thenReturn("foo.bundle");
- Mockito.when(bundle.getBundleId()).thenReturn(42L);
-
- StartLevel startLevel = Mockito.mock(StartLevel.class);
- logSupport = new LogSupport(startLevel);
- Field loggerField = LogSupport.class.getDeclaredField("loggers");
- loggerField.setAccessible(true);
- Map<Long, Logger> loggers = (Map<Long, Logger>) loggerField.get(logSupport);
-
- testLogger = getMockInfoLogger();
- loggers.put(bundle.getBundleId(), testLogger);
- }
-
- @Test
- public void testServiceEvent() throws Exception {
-
-
- final Map<String, Object> props = new HashMap<String, Object>();
- props.put(Constants.OBJECTCLASS, new String [] {"some.class.Name"});
- props.put(Constants.SERVICE_ID, 999L);
-
- ServiceReference sr = Mockito.mock(ServiceReference.class);
- Mockito.when(sr.getBundle()).thenReturn(bundle);
- Mockito.when(sr.getProperty(Mockito.anyString())).then(new Answer<Object>() {
- public Object answer(InvocationOnMock invocation) throws Throwable {
- return props.get(invocation.getArguments()[0]);
- }
- });
- Mockito.when(sr.getPropertyKeys()).thenReturn(props.keySet().toArray(new String[] {}));
- ServiceEvent se = new ServiceEvent(ServiceEvent.REGISTERED, sr);
-
- logSupport.serviceChanged(se);
-
- Mockito.verify(testLogger).info("Service [999, [some.class.Name]] ServiceEvent REGISTERED", (Throwable) null);
- }
-
- @Test
- public void testEarlyExit() throws Exception {
-
- ServiceReference sr = Mockito.mock(ServiceReference.class);
- LogEntry le = new LogEntryImpl(bundle, sr, LogService.LOG_DEBUG, "test", null);
-
- logSupport.fireLogEvent(le);
-
- // The log message is on DEBUG level while the logger is set to INFO level
- // we don't want the actual log.info() call to be made, neither do we want
- // any preparatory work on the log message being made (which involves
- // inspecting the service reference).
- Mockito.verify(testLogger).isTraceEnabled();
- Mockito.verify(testLogger).isDebugEnabled();
- Mockito.verify(testLogger).isInfoEnabled();
- Mockito.verifyNoMoreInteractions(testLogger);
- Mockito.verifyZeroInteractions(sr);
- }
-
- @Test
- public void testErrorLogger() throws Exception {
-
- Exception e = new Exception();
- LogEntry le = new LogEntryImpl(bundle, null, LogService.LOG_ERROR, "my-error-msg", e);
-
- logSupport.fireLogEvent(le);
-
- Mockito.verify(testLogger).error("my-error-msg (java.lang.Exception)", e);
- }
-
- @Test
- public void testWarningLogger() throws Exception {
-
- Exception e = new Exception();
- LogEntry le = new LogEntryImpl(bundle, null, LogService.LOG_WARNING, "my-warning-message", e);
-
- logSupport.fireLogEvent(le);
-
- Mockito.verify(testLogger).warn("my-warning-message (java.lang.Exception)", e);
- }
-
- @Test
- public void testInfoLogger() throws Exception {
-
- LogEntry le = new LogEntryImpl(bundle, null, LogService.LOG_INFO, "my-info-message", null);
-
- logSupport.fireLogEvent(le);
-
- Mockito.verify(testLogger).info("my-info-message", (Throwable) null);
- }
-
- @Test
- public void testBundleChanges() throws Exception {
-
- logSupport.bundleChanged(new BundleEvent(BundleEvent.INSTALLED, bundle));
-
- Mockito.verify(testLogger).info("BundleEvent INSTALLED", (Throwable) null);
- }
-
- @Test
- public void testFrameworkEventStarted() throws Exception {
-
- FrameworkEvent frameworkEvent = new FrameworkEvent(FrameworkEvent.STARTED, bundle, null);
-
- logSupport.frameworkEvent(frameworkEvent);
-
- Mockito.verify(testLogger).info("FrameworkEvent STARTED", (Throwable) null);
- }
-
- @Test
- public void testFrameworkEventError() throws Exception {
-
- BundleException bundleException = new BundleException("my bundle exception", BundleException.ACTIVATOR_ERROR);
- FrameworkEvent frameworkEvent = new FrameworkEvent(FrameworkEvent.ERROR, bundle, bundleException);
-
- logSupport.frameworkEvent(frameworkEvent);
-
- Mockito.verify(testLogger).error("FrameworkEvent ERROR (org.osgi.framework.BundleException: my bundle exception)", bundleException);
- }
-
- @Test
- public void testGetLevels() {
- Logger traceLogger = Mockito.mock(Logger.class);
- Mockito.when(traceLogger.isTraceEnabled()).thenReturn(true);
- assertEquals(5, LogSupport.getLevel(traceLogger));
-
- Logger debugLogger = Mockito.mock(Logger.class);
- Mockito.when(debugLogger.isDebugEnabled()).thenReturn(true);
- assertEquals(LogService.LOG_DEBUG, LogSupport.getLevel(debugLogger));
-
- Logger infoLogger = Mockito.mock(Logger.class);
- Mockito.when(infoLogger.isInfoEnabled()).thenReturn(true);
- assertEquals(LogService.LOG_INFO, LogSupport.getLevel(infoLogger));
-
- Logger warnLogger = Mockito.mock(Logger.class);
- Mockito.when(warnLogger.isWarnEnabled()).thenReturn(true);
- assertEquals(LogService.LOG_WARNING, LogSupport.getLevel(warnLogger));
-
- Logger errorLogger = Mockito.mock(Logger.class);
- Mockito.when(errorLogger.isErrorEnabled()).thenReturn(true);
- assertEquals(LogService.LOG_ERROR, LogSupport.getLevel(errorLogger));
- }
-
- private Logger getMockInfoLogger() {
- Logger testLogger = Mockito.mock(Logger.class);
- Mockito.when(testLogger.isTraceEnabled()).thenReturn(false);
- Mockito.when(testLogger.isDebugEnabled()).thenReturn(false);
- Mockito.when(testLogger.isInfoEnabled()).thenReturn(true);
- Mockito.when(testLogger.isWarnEnabled()).thenReturn(true);
- Mockito.when(testLogger.isErrorEnabled()).thenReturn(true);
- return testLogger;
- }
-}