Skip to content

Commit e4521ba

Browse files
committed
felix.log: Implement 101.8.4 Log Events
Log entries must be mapped into events by the Log Service implementation and delivered asynchronously to the Event Admin service (if present).
1 parent 0000f17 commit e4521ba

File tree

3 files changed

+149
-3
lines changed

3 files changed

+149
-3
lines changed

log/pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
<dependency>
4949
<groupId>org.osgi</groupId>
5050
<artifactId>osgi.core</artifactId>
51-
<version>6.0.0</version>
51+
<version>7.0.0</version>
5252
<scope>provided</scope>
5353
</dependency>
5454
<dependency>
@@ -62,6 +62,11 @@
6262
<artifactId>org.osgi.service.log</artifactId>
6363
<version>1.5.0</version>
6464
</dependency>
65+
<dependency>
66+
<groupId>org.osgi</groupId>
67+
<artifactId>org.osgi.service.event</artifactId>
68+
<version>1.4.1</version>
69+
</dependency>
6570
<dependency>
6671
<groupId>org.osgi</groupId>
6772
<artifactId>osgi.annotation</artifactId>
@@ -95,6 +100,7 @@
95100
<Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
96101
<Include-Resource>META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/DEPENDENCIES=DEPENDENCIES</Include-Resource>
97102
<_reproducible>true</_reproducible>
103+
<_noimportjava>true</_noimportjava>
98104
</instructions>
99105
</configuration>
100106
</plugin>

log/src/main/java/org/apache/felix/log/Activator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ private static String getDefaultLogLevel(final BundleContext context) {
141141
public void start(final BundleContext context) throws Exception
142142
{
143143
// create the log instance
144-
m_log = new Log(getMaxSize(context), getStoreDebug(context));
144+
m_log = new Log(context, getMaxSize(context), getStoreDebug(context));
145145
// create the LoggerAdmin instance
146146
m_loggerAdmin = new LoggerAdminImpl(getDefaultLogLevel(context), m_log);
147147

log/src/main/java/org/apache/felix/log/Log.java

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,32 @@
1818
*/
1919
package org.apache.felix.log;
2020

21+
import java.lang.reflect.Constructor;
22+
import java.lang.reflect.InvocationTargetException;
23+
import java.lang.reflect.Method;
2124
import java.util.Enumeration;
25+
import java.util.HashMap;
26+
import java.util.Map;
27+
import java.util.concurrent.atomic.AtomicReference;
2228

29+
import org.osgi.annotation.bundle.Requirement;
2330
import org.osgi.framework.Bundle;
31+
import org.osgi.framework.BundleContext;
2432
import org.osgi.framework.BundleEvent;
2533
import org.osgi.framework.BundleListener;
34+
import org.osgi.framework.Constants;
2635
import org.osgi.framework.FrameworkEvent;
2736
import org.osgi.framework.FrameworkListener;
2837
import org.osgi.framework.ServiceEvent;
2938
import org.osgi.framework.ServiceListener;
3039
import org.osgi.framework.ServiceReference;
40+
import org.osgi.namespace.implementation.ImplementationNamespace;
41+
import org.osgi.service.event.EventConstants;
3142
import org.osgi.service.log.LogEntry;
3243
import org.osgi.service.log.LogLevel;
3344
import org.osgi.service.log.LogListener;
45+
import org.osgi.util.tracker.ServiceTracker;
46+
import org.osgi.util.tracker.ServiceTrackerCustomizer;
3447

3548
/**
3649
* Class used to represent the log. This class is used by the implementations
@@ -39,8 +52,16 @@
3952
* @see org.osgi.service.log.LogService
4053
* @see org.osgi.service.log.LogReaderService
4154
*/
55+
@Requirement(namespace = ImplementationNamespace.IMPLEMENTATION_NAMESPACE,
56+
name = EventConstants.EVENT_ADMIN_IMPLEMENTATION,
57+
version = EventConstants.EVENT_ADMIN_SPECIFICATION_VERSION,
58+
resolution = Requirement.Resolution.OPTIONAL)
4259
final class Log implements BundleListener, FrameworkListener, ServiceListener
4360
{
61+
private static final String EVENT_ADMIN_CLASS = "org.osgi.service.event.EventAdmin";
62+
private static final String EVENT_CLASS = "org.osgi.service.event.Event";
63+
private static final String POST_EVENT_METHOD = "postEvent";
64+
4465
/** The first log entry. */
4566
private volatile LogNode m_head;
4667
/** The last log entry. */
@@ -55,16 +76,111 @@ final class Log implements BundleListener, FrameworkListener, ServiceListener
5576
private final boolean m_storeDebug;
5677
/** Active flag */
5778
private volatile boolean active = true;
79+
/** Bundle context */
80+
private final BundleContext m_context;
81+
/** Service tracker for EventAdmin */
82+
private final ServiceTracker<?, EAProxy> m_eventAdminTracker;
83+
84+
static class EventAdminServiceInfo {
85+
final Constructor<?> m_eventClassCtor;
86+
final Method m_postEventMethod;
87+
final Object m_service;
88+
89+
EventAdminServiceInfo(Constructor<?> eventClassCtor, Method postEventMethod, Object service) {
90+
this.m_eventClassCtor = eventClassCtor;
91+
this.m_postEventMethod = postEventMethod;
92+
this.m_service = service;
93+
}
94+
}
95+
96+
static class EAProxy {
97+
final AtomicReference<EventAdminServiceInfo> m_info = new AtomicReference<>();
98+
99+
public EAProxy(ServiceReference<?> reference, Object service) {
100+
setServiceInfo(reference, service);
101+
}
102+
103+
final void setServiceInfo(ServiceReference<?> reference, Object service) {
104+
final Bundle bundle = reference.getBundle();
105+
try {
106+
Class<?> eventClass = bundle.loadClass(EVENT_CLASS);
107+
final Constructor<?> eventConstructor = eventClass.getConstructor(String.class, Map.class);
108+
Class<?> eventAdminClass = bundle.loadClass(EVENT_ADMIN_CLASS);
109+
final Method postMethod = eventAdminClass.getMethod(POST_EVENT_METHOD, eventClass);
110+
this.m_info.set(new EventAdminServiceInfo(eventConstructor, postMethod, service));
111+
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) {
112+
throw new IllegalStateException(
113+
"Failure reflecting over API from Event Admin service bundle", e);
114+
}
115+
}
116+
117+
void resetServiceInfo() {
118+
this.m_info.set(null);
119+
}
120+
121+
void postEvent(LogEntry entry) {
122+
final EventAdminServiceInfo eventAdminServiceInfo = this.m_info.get();
123+
if (eventAdminServiceInfo == null) {
124+
return;
125+
}
126+
127+
try {
128+
final LogLevel logLevel = entry.getLogLevel();
129+
final String topic;
130+
if (logLevel == LogLevel.TRACE) {
131+
topic = "org/osgi/service/log/LogEntry/LOG_OTHER";
132+
} else {
133+
topic = "org/osgi/service/log/LogEntry/LOG_" + logLevel.name();
134+
}
135+
final Map<String, Object> props = new HashMap<>(10);
136+
final Bundle bundle = entry.getBundle();
137+
props.put(EventConstants.BUNDLE_ID, bundle.getBundleId());
138+
props.put(EventConstants.BUNDLE_SYMBOLICNAME, bundle.getSymbolicName());
139+
props.put(EventConstants.BUNDLE, bundle);
140+
props.put("log.level", entry.getLogLevel());
141+
props.put("log.loggername", entry.getLoggerName());
142+
props.put("log.threadinfo", entry.getThreadInfo());
143+
props.put("log.loglevel", logLevel);
144+
props.put(EventConstants.MESSAGE, entry.getMessage());
145+
props.put(EventConstants.TIMESTAMP, entry.getTime());
146+
props.put("log.entry", entry);
147+
final Throwable exception = entry.getException();
148+
if (exception != null) {
149+
props.put(EventConstants.EXCEPTION_CLASS, exception.getClass().getName());
150+
props.put(EventConstants.EXCEPTION_MESSAGE, exception.getMessage());
151+
props.put(EventConstants.EXCEPTION, exception);
152+
}
153+
final ServiceReference<?> serviceReference = entry.getServiceReference();
154+
if (serviceReference != null) {
155+
props.put(EventConstants.SERVICE, serviceReference);
156+
props.put(EventConstants.SERVICE_ID, serviceReference.getProperty(Constants.SERVICE_ID));
157+
final Object servicePid = serviceReference.getProperty(Constants.SERVICE_PID);
158+
if (servicePid != null) {
159+
props.put(EventConstants.SERVICE_PID, servicePid);
160+
}
161+
props.put(EventConstants.SERVICE_OBJECTCLASS, serviceReference.getProperty(Constants.OBJECTCLASS));
162+
}
163+
final Object event = eventAdminServiceInfo.m_eventClassCtor.newInstance(topic, props);
164+
eventAdminServiceInfo.m_postEventMethod.invoke(eventAdminServiceInfo.m_service, event);
165+
} catch (IllegalAccessException | InvocationTargetException | InstantiationException e) {
166+
throw new RuntimeException(e);
167+
}
168+
}
169+
}
58170

59171
/**
60172
* Create a new instance.
61173
* @param maxSize the maximum size for the log
62174
* @param storeDebug whether or not to store debug messages
63175
*/
64-
Log(final int maxSize, final boolean storeDebug)
176+
Log(final BundleContext context, final int maxSize, final boolean storeDebug)
65177
{
178+
this.m_context = context;
66179
this.m_maxSize = maxSize;
67180
this.m_storeDebug = storeDebug;
181+
this.m_eventAdminTracker = new ServiceTracker<>(context, EVENT_ADMIN_CLASS,
182+
new EAProxyServiceTrackerCustomizer());
183+
this.m_eventAdminTracker.open();
68184
}
69185

70186
/**
@@ -78,6 +194,7 @@ synchronized void close()
78194
listenerThread.shutdown();
79195
listenerThread = null;
80196
}
197+
m_eventAdminTracker.close();
81198

82199
m_head = null;
83200
m_tail = null;
@@ -151,6 +268,12 @@ synchronized void addEntry(final LogEntry entry)
151268
{
152269
listenerThread.addEntry(entry);
153270
}
271+
272+
final EAProxy eaProxy = this.m_eventAdminTracker.getService();
273+
if (eaProxy != null)
274+
{
275+
eaProxy.postEvent(entry);
276+
}
154277
}
155278

156279
/**
@@ -330,4 +453,21 @@ public void serviceChanged(final ServiceEvent event)
330453
message,
331454
null);
332455
}
456+
457+
private class EAProxyServiceTrackerCustomizer implements ServiceTrackerCustomizer<Object, EAProxy> {
458+
@Override
459+
public EAProxy addingService(ServiceReference<Object> reference) {
460+
return new EAProxy(reference, m_context.getService(reference));
461+
}
462+
463+
@Override
464+
public void modifiedService(ServiceReference<Object> reference, EAProxy proxy) {
465+
proxy.setServiceInfo(reference, m_context.getService(reference));
466+
}
467+
468+
@Override
469+
public void removedService(ServiceReference<Object> reference, EAProxy proxy) {
470+
proxy.resetServiceInfo();
471+
}
472+
}
333473
}

0 commit comments

Comments
 (0)