Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ConfigAdminListener.java290
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceFactory.java15
-rw-r--r--bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LogServiceManager.java5
-rwxr-xr-xbundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerContextTargetMap.java1
4 files changed, 298 insertions, 13 deletions
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ConfigAdminListener.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ConfigAdminListener.java
new file mode 100644
index 000000000..3c3fbb2ab
--- /dev/null
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ConfigAdminListener.java
@@ -0,0 +1,290 @@
+/*******************************************************************************
+ * Copyright (c) 2017 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.osgi.internal.log;
+
+import java.lang.reflect.*;
+import java.util.*;
+import org.osgi.framework.*;
+import org.osgi.service.log.LogLevel;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+class ConfigAdminListener implements ServiceTrackerCustomizer<Object, ServiceRegistration<?>> {
+
+ private static final String CLASS_CONFIG_ADMIN = "org.osgi.service.cm.ConfigurationAdmin"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_ADMIN_GET_CONFIGURATION = "getConfiguration"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_ADMIN_LIST_CONFIGURATIONS = "listConfigurations"; //$NON-NLS-1$
+
+ private static final String CLASS_SYNC_CONFIG_LISTENER = "org.osgi.service.cm.SynchronousConfigurationListener"; //$NON-NLS-1$
+ private static final String CLASS_CONFIG_EVENT = "org.osgi.service.cm.ConfigurationEvent"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_EVENT_GET_PID = "getPid"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_EVENT_GET_FACTORY_PID = "getFactoryPid"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_EVENT_GET_REFERENCE = "getReference"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_EVENT_GET_TYPE = "getType"; //$NON-NLS-1$
+ private static final int CM_UPDATED = 1;
+ private static final int CM_DELETED = 2;
+ private static final int CM_LOCATION_CHANGED = 3;
+
+ private static final String CLASS_CONFIG = "org.osgi.service.cm.Configuration"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_GET_PROPERTIES = "getProperties"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_GET_PID = "getPid"; //$NON-NLS-1$
+ private static final String METHOD_CONFIG_GET_FACTORY_PID = "getFactoryPid"; //$NON-NLS-1$
+
+ private static final String PID_PREFIX_LOG_ADMIN = "org.osgi.service.log.admin"; //$NON-NLS-1$
+ private static final String NULL_LEVEL = "NULL"; //$NON-NLS-1$
+ // using String constructor here to avoid interning
+ private static final String PID_FILTER = '(' + Constants.SERVICE_PID + '=' + PID_PREFIX_LOG_ADMIN + '*' + ')';
+
+ private final ServiceTracker<Object, ServiceRegistration<?>> configTracker;
+ final ExtendedLogServiceFactory factory;
+ final BundleContext context;
+
+ ConfigAdminListener(BundleContext context, ExtendedLogServiceFactory factory) {
+ this.context = context;
+ this.configTracker = new ServiceTracker<>(context, CLASS_CONFIG_ADMIN, this);
+ this.factory = factory;
+ }
+
+ void start() {
+ configTracker.open();
+ }
+
+ void stop() {
+ configTracker.close();
+ }
+
+ private ServiceRegistration<?> registerConfigurationListener(ServiceReference<?> configRef) {
+ try {
+ Class<?> listenerClass = configRef.getBundle().loadClass(CLASS_SYNC_CONFIG_LISTENER);
+ return registerProxyConfigListener(configRef, listenerClass);
+ } catch (ClassNotFoundException | NoSuchMethodException e) {
+ throw new RuntimeException(CLASS_SYNC_CONFIG_LISTENER, e);
+ }
+ }
+
+ private ServiceRegistration<?> registerProxyConfigListener(ServiceReference<?> configRef, Class<?> listenerClass) throws ClassNotFoundException, NoSuchMethodException {
+ LoggerContextConfiguration loggerConfiguration = new LoggerContextConfiguration(listenerClass, configRef);
+ return loggerConfiguration.register();
+ }
+
+ @Override
+ public ServiceRegistration<?> addingService(ServiceReference<Object> configRef) {
+ return registerConfigurationListener(configRef);
+ }
+
+ @Override
+ public void modifiedService(ServiceReference<Object> configRef, ServiceRegistration<?> configReg) {
+ // Nothing to do
+ }
+
+ @Override
+ public void removedService(ServiceReference<Object> configRef, ServiceRegistration<?> loggerConfiguration) {
+ loggerConfiguration.unregister();
+ }
+
+ class LoggerContextConfiguration implements InvocationHandler {
+ private final Object listenerProxy;
+ private final Object configAdmin;
+ private final ServiceReference<?> configAdminRef;
+
+ private final Class<?> configClass;
+ private final Method getConfigProperties;
+ private final Method getConfigPid;
+ private final Method getConfigFactoryPid;
+
+ private final Class<?> configAdminClass;
+ private final Method getConfiguration;
+ private final Method listConfigurations;
+
+ private final Class<?> configEventClass;
+ private final Method getEventPid;
+ private final Method getEventFactoryPid;
+ private final Method getEventReference;
+ private final Method getEventType;
+
+ public LoggerContextConfiguration(Class<?> listenerClass, ServiceReference<?> ref) throws ClassNotFoundException, NoSuchMethodException {
+ listenerProxy = Proxy.newProxyInstance(listenerClass.getClassLoader(), new Class[] {listenerClass}, this);
+ configAdminRef = ref;
+ ClassLoader cl = listenerClass.getClassLoader();
+
+ configClass = cl.loadClass(CLASS_CONFIG);
+ getConfigProperties = configClass.getMethod(METHOD_CONFIG_GET_PROPERTIES);
+ getConfigFactoryPid = configClass.getMethod(METHOD_CONFIG_GET_FACTORY_PID);
+ getConfigPid = configClass.getMethod(METHOD_CONFIG_GET_PID);
+
+ configAdminClass = cl.loadClass(CLASS_CONFIG_ADMIN);
+ getConfiguration = configAdminClass.getMethod(METHOD_CONFIG_ADMIN_GET_CONFIGURATION, String.class, String.class);
+ listConfigurations = configAdminClass.getMethod(METHOD_CONFIG_ADMIN_LIST_CONFIGURATIONS, String.class);
+
+ configEventClass = cl.loadClass(CLASS_CONFIG_EVENT);
+ getEventPid = configEventClass.getMethod(METHOD_CONFIG_EVENT_GET_PID);
+ getEventFactoryPid = configEventClass.getMethod(METHOD_CONFIG_EVENT_GET_FACTORY_PID);
+ getEventReference = configEventClass.getMethod(METHOD_CONFIG_EVENT_GET_REFERENCE);
+ getEventType = configEventClass.getMethod(METHOD_CONFIG_EVENT_GET_TYPE);
+
+ configAdmin = context.getService(ref);
+ }
+
+ public ServiceRegistration<?> register() {
+ // register it with the config admin context to ensure consistent class space
+ Bundle configBundle = configAdminRef.getBundle();
+ BundleContext configContext = configBundle != null ? configAdminRef.getBundle().getBundleContext() : null;
+ if (configContext == null) {
+ // seems the bundle has stopped!
+ return null;
+ }
+ ServiceRegistration<?> registration = configContext.registerService(CLASS_SYNC_CONFIG_LISTENER, listenerProxy, null);
+
+ try {
+ Object[] configs = (Object[]) listConfigurations.invoke(configAdmin, PID_FILTER);
+ if (configs != null) {
+ for (Object config : configs) {
+ String factoryPid = (String) getConfigFactoryPid.invoke(config);
+ if (factoryPid != null) {
+ continue;
+ }
+ String pid = (String) getConfigPid.invoke(config);
+ String contextName = getContextName(pid);
+ @SuppressWarnings("unchecked")
+ Dictionary<String, Object> configDictionary = (Dictionary<String, Object>) getConfigProperties.invoke(config);
+ if (configDictionary != null) {
+ setLogLevels(contextName, getLogLevels(configDictionary));
+ }
+ }
+ }
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ return registration;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] event) throws Throwable {
+ // There is only one method on ConfigurationListener, no need to check the method type
+ // ConfigurationListener::configurationEvent(ConfigurationEvent)
+ if (!configAdminRef.equals(getReference(event))) {
+ // ignore other config admin events
+ return null;
+ }
+
+ String pid = getEventPid(event);
+ if (pid == null) {
+ // a factory pid or more likely doesn't have the correct prefix; ignore
+ return null;
+ }
+
+ int type = getType(event);
+ if (type == CM_LOCATION_CHANGED) {
+ // TODO not sure if we should check location or not
+ return null;
+ }
+
+ String contextName = getContextName(pid);
+ if (type == CM_DELETED) {
+ setLogLevels(contextName, Collections.<String, LogLevel> emptyMap());
+ return null;
+ }
+
+ if (type == CM_UPDATED) {
+ Dictionary<String, Object> configDictionary = findConfiguration(pid);
+ if (configDictionary == null) {
+ // Configuration got deleted before we could get it so treat as deleted
+ setLogLevels(contextName, Collections.<String, LogLevel> emptyMap());
+ return null;
+ }
+
+ Map<String, LogLevel> levelConfig = getLogLevels(configDictionary);
+ setLogLevels(contextName, levelConfig);
+ }
+ return null;
+ }
+
+ private String getContextName(String pid) {
+ if (PID_PREFIX_LOG_ADMIN.equals(pid)) {
+ return null;
+ }
+ char separator = pid.charAt(PID_PREFIX_LOG_ADMIN.length());
+ if (separator != '|') {
+ return null;
+ }
+ int startName = PID_PREFIX_LOG_ADMIN.length() + 1;
+ return pid.substring(startName);
+ }
+
+ private Map<String, LogLevel> getLogLevels(Dictionary<String, Object> configDictionary) {
+ Map<String, LogLevel> result = new HashMap<>(configDictionary.size());
+ for (Enumeration<String> keys = configDictionary.keys(); keys.hasMoreElements();) {
+ String key = keys.nextElement();
+ Object v = configDictionary.get(key);
+ if (v instanceof String) {
+ if (NULL_LEVEL.equals(v)) {
+ result.put(key, null);
+ } else {
+ try {
+ result.put(key, LogLevel.valueOf((String) v));
+ } catch (IllegalArgumentException e) {
+ // ignore invalid values
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private Object getReference(Object[] event) {
+ try {
+ return getEventReference.invoke(event[0]);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private String getEventPid(Object[] event) {
+ try {
+ String factoryPid = (String) getEventFactoryPid.invoke(event[0]);
+ if (factoryPid != null) {
+ // ignore factory pids
+ return null;
+ }
+ String pid = (String) getEventPid.invoke(event[0]);
+ if (pid.startsWith(PID_PREFIX_LOG_ADMIN)) {
+ return pid;
+ }
+ return null;
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private int getType(Object[] event) {
+ try {
+ Integer type = (Integer) getEventType.invoke(event[0]);
+ return type == null ? 0 : type.intValue();
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, Object> findConfiguration(String pid) {
+ try {
+ Object config = getConfiguration.invoke(configAdmin, pid, null);
+ return (Dictionary<String, Object>) getConfigProperties.invoke(config);
+ } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void setLogLevels(String contextName, Map<String, LogLevel> logLevels) {
+ factory.getLoggerAdmin().getLoggerContext(contextName).setLogLevels(logLevels);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceFactory.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceFactory.java
index 11feeb46d..a33f86c38 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceFactory.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/ExtendedLogServiceFactory.java
@@ -143,7 +143,7 @@ public class ExtendedLogServiceFactory implements ServiceFactory<ExtendedLogServ
}
if (level == null && contextName != null) {
// non-null context name is a non-root context;
- // must not check the root context
+ // must check the root context for non-root contexts
EquinoxLoggerContext rootContext = loggerContextTargetMap.getRootLoggerContext();
if (rootContext != null) {
level = rootContext.getEffectiveLogLevel(name);
@@ -170,17 +170,6 @@ public class ExtendedLogServiceFactory implements ServiceFactory<ExtendedLogServ
@Override
public void setLogLevels(Map<String, LogLevel> logLevels) {
- if (!setWithConfigAdmin(logLevels)) {
- doSetLogLevels(logLevels);
- }
- }
-
- private boolean setWithConfigAdmin(Map<String, LogLevel> logLevels) {
- // TODO Auto-generated method stub
- return false;
- }
-
- private void doSetLogLevels(Map<String, LogLevel> logLevels) {
boolean readLocked = false;
try {
contextsLock.writeLock().lock();
@@ -203,7 +192,7 @@ public class ExtendedLogServiceFactory implements ServiceFactory<ExtendedLogServ
@Override
public void clear() {
- doSetLogLevels(Collections.<String, LogLevel> emptyMap());
+ setLogLevels(Collections.<String, LogLevel> emptyMap());
}
@Override
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LogServiceManager.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LogServiceManager.java
index 7994ad4b5..98baab7fd 100644
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LogServiceManager.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LogServiceManager.java
@@ -33,6 +33,7 @@ public class LogServiceManager implements BundleListener, FrameworkListener, Ser
private final ExtendedLogServiceFactory logServiceFactory;
private final ExtendedLogServiceImpl systemBundleLog;
private EventAdminAdapter eventAdminAdapter;
+ private ConfigAdminListener configAdminListener;
public LogServiceManager(int maxHistory, LogListener... systemListeners) {
logReaderServiceFactory = new ExtendedLogReaderServiceFactory(maxHistory);
@@ -63,9 +64,13 @@ public class LogServiceManager implements BundleListener, FrameworkListener, Ser
eventAdminAdapter = new EventAdminAdapter(context, logReaderServiceFactory);
eventAdminAdapter.start();
+ configAdminListener = new ConfigAdminListener(context, logServiceFactory);
+ configAdminListener.start();
}
public void stop(BundleContext context) {
+ configAdminListener.stop();
+ configAdminListener = null;
eventAdminAdapter.stop();
eventAdminAdapter = null;
loggerAdminRegistration.unregister();
diff --git a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerContextTargetMap.java b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerContextTargetMap.java
index 15234d6ca..46a2ece04 100755
--- a/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerContextTargetMap.java
+++ b/bundles/org.eclipse.osgi/container/src/org/eclipse/osgi/internal/log/LoggerContextTargetMap.java
@@ -104,6 +104,7 @@ public class LoggerContextTargetMap {
logServices.clear();
qualifiedNameToTargets.clear();
targetToQualifiedNames.clear();
+ loggerContexts.clear();
}
LoggerContext createLoggerContext(String name, ExtendedLogServiceFactory factory) {

Back to the top