1818 */
1919package org.apache.felix.log;
2020
21+ import java.lang.reflect.Constructor;
22+ import java.lang.reflect.InvocationTargetException;
23+ import java.lang.reflect.Method;
2124import 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;
2330import org.osgi.framework.Bundle;
31+ import org.osgi.framework.BundleContext;
2432import org.osgi.framework.BundleEvent;
2533import org.osgi.framework.BundleListener;
34+ import org.osgi.framework.Constants;
2635import org.osgi.framework.FrameworkEvent;
2736import org.osgi.framework.FrameworkListener;
2837import org.osgi.framework.ServiceEvent;
2938import org.osgi.framework.ServiceListener;
3039import org.osgi.framework.ServiceReference;
40+ import org.osgi.namespace.implementation.ImplementationNamespace;
41+ import org.osgi.service.event.EventConstants;
3142import org.osgi.service.log.LogEntry;
3243import org.osgi.service.log.LogLevel;
3344import 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
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)
4259final 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