blob: 73c1040c7a1c608be4336488167d4900649d34c9 [file] [log] [blame]
/*
* 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.felix.log;
import java.util.Enumeration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
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.log.LogEntry;
import org.osgi.service.log.LogLevel;
import org.osgi.service.log.LogListener;
/**
* Class used to represent the log. This class is used by the implementations
* of both the {@link org.osgi.service.log.LogService} interface and the
* {@link org.osgi.service.log.LogReaderService} to access the log.
* @see org.osgi.service.log.LogService
* @see org.osgi.service.log.LogReaderService
*/
final class Log implements BundleListener, FrameworkListener, ServiceListener
{
/** The first log entry. */
private LogNode m_head;
/** The last log entry. */
private LogNode m_tail;
/** The log size. */
private int m_size;
/** The log listener thread. */
private LogListenerThread listenerThread;
/** The maximum size for the log. */
private final int m_maxSize;
/** Whether or not to store debug messages. */
private final boolean m_storeDebug;
/**
* Create a new instance.
* @param maxSize the maximum size for the log
* @param storeDebug whether or not to store debug messages
*/
Log(final int maxSize, final boolean storeDebug)
{
this.m_maxSize = maxSize;
this.m_storeDebug = storeDebug;
}
/**
* Close the log.
*/
void close()
{
if (listenerThread != null)
{
listenerThread.shutdown();
listenerThread = null;
}
m_head = null;
m_tail = null;
m_size = 0;
}
void log(
final String name,
final Bundle bundle,
final ServiceReference<?> sr,
final LogLevel level,
final String message,
final Throwable exception) {
addEntry(new LogEntryImpl(name, bundle, sr, level, message, exception, getStackTraceElement()));
}
/**
* Adds the entry to the log.
* @param entry the entry to add to the log
*/
synchronized void addEntry(final LogEntry entry)
{
if (m_maxSize != 0)
{
// add the entry to the historic log
if (m_storeDebug || entry.getLogLevel() != LogLevel.DEBUG)
{
// create a new node for the entry
LogNode node = new LogNode(entry);
// add to the front of the linked list
node.setNextNode(m_head);
if (m_head != null)
{
m_head.setPreviousNode(node);
}
// and store the node
m_head = node;
// bump the size of the list
++m_size;
// if no tail node - add the node to the tail
if (m_tail == null)
{
m_tail = node;
}
}
// ensure the historic log doesn't grow beyond a certain size
if (m_maxSize != -1)
{
if (m_size > m_maxSize)
{
LogNode last = m_tail.getPreviousNode();
last.setNextNode(null);
m_tail = last;
--m_size;
}
}
}
// notify any listeners
if (listenerThread != null)
{
listenerThread.addEntry(entry);
}
}
/**
* Add a listener to the log.
* @param listener the log listener to subscribe
*/
synchronized void addListener(final LogListener listener)
{
if (listenerThread == null)
{
// create a new listener thread if necessary:
// the listener thread only runs if there are any registered listeners
listenerThread = new LogListenerThread();
listenerThread.start();
}
listenerThread.addListener(listener);
}
/**
* Remove a listener from the log.
* @param listener the log listener to unsubscribe
*/
synchronized void removeListener(final LogListener listener)
{
if (listenerThread != null)
{
listenerThread.removeListener(listener);
// shutdown the thread if there are no listeners
if (listenerThread.getListenerCount() == 0)
{
listenerThread.shutdown();
listenerThread = null;
}
}
}
/**
* Returns an enumeration of all the entries in the log most recent first.
* @return an enumeration of all the entries in the log most recent first
*/
synchronized Enumeration<LogEntry> getEntries()
{
return new LogNodeEnumeration(m_head, m_tail);
}
/** The messages returned for the framework events. */
private static final String[] FRAMEWORK_EVENT_MESSAGES =
{
"FrameworkEvent STARTED",
"FrameworkEvent ERROR",
"FrameworkEvent PACKAGES REFRESHED",
"FrameworkEvent STARTLEVEL CHANGED",
"FrameworkEvent WARNING",
"FrameworkEvent INFO"
};
/**
* Called when a framework event occurs.
* @param event the event that occured
*/
@Override
public void frameworkEvent(final FrameworkEvent event)
{
int eventType = event.getType();
String message = null;
for (int i = 0; message == null && i < FRAMEWORK_EVENT_MESSAGES.length; ++i)
{
if (eventType >> i == 1)
{
message = FRAMEWORK_EVENT_MESSAGES[i];
}
}
log(
"Events.Framework",
event.getBundle(),
null,
(eventType == FrameworkEvent.ERROR) ? LogLevel.ERROR : LogLevel.INFO,
message,
event.getThrowable());
}
/** The messages returned for the bundle events. */
private static final String[] BUNDLE_EVENT_MESSAGES =
{
"BundleEvent INSTALLED",
"BundleEvent STARTED",
"BundleEvent STOPPED",
"BundleEvent UPDATED",
"BundleEvent UNINSTALLED",
"BundleEvent RESOLVED",
"BundleEvent UNRESOLVED",
"BundleEvent STARTING",
"BundleEvent STOPPING",
"BundleEvent LAZY ACTIVATION",
};
/**
* Called when a bundle event occurs.
* @param event the event that occured
*/
@Override
public void bundleChanged(final BundleEvent event)
{
int eventType = event.getType();
String message = null;
for (int i = 0; message == null && i < BUNDLE_EVENT_MESSAGES.length; ++i)
{
if (eventType >> i == 1)
{
message = BUNDLE_EVENT_MESSAGES[i];
}
}
if (message != null)
{
log(
"Events.Bundle",
event.getBundle(),
null,
LogLevel.INFO,
message,
null);
}
}
public static StackTraceElement getStackTraceElement() {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
if (elements.length == 0) {
return null;
}
for (int i = 1; i < elements.length; i++) {
if (!elements[i].getClassName().startsWith("org.apache.felix.log")) {
return elements[i];
}
}
return elements[1];
}
/** The messages returned for the service events. */
private static final String[] SERVICE_EVENT_MESSAGES =
{
"ServiceEvent REGISTERED",
"ServiceEvent MODIFIED",
"ServiceEvent UNREGISTERING"
};
/**
* Called when a service event occurs.
* @param event the event that occured
*/
@Override
public void serviceChanged(final ServiceEvent event)
{
int eventType = event.getType();
String message = null;
for (int i = 0; message == null && i < SERVICE_EVENT_MESSAGES.length; ++i)
{
if (eventType >> i == 1)
{
message = SERVICE_EVENT_MESSAGES[i];
}
}
log(
"Events.Service",
event.getServiceReference().getBundle(),
event.getServiceReference(),
(eventType == ServiceEvent.MODIFIED) ? LogLevel.DEBUG : LogLevel.INFO,
message,
null);
}
}