From 906f6d642b35bd86317b0e3dbac5dc1a66dbf9f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Fri, 16 Feb 2024 06:10:05 +0100 Subject: [PATCH] Add support for Coordinator to Equinox Configuration Admin Chapter 11 of the Configuration Admin Service Specification mandates the support of the Coordinator service but currently Equinox fails the TCK tests in that area. This adds support for participation in the coordinator for the Equinox Configuration Admin. --- .../META-INF/MANIFEST.MF | 3 +- .../cm/ConfigurationAdminFactory.java | 66 ++++++++++++++++++- .../equinox/internal/cm/EventDispatcher.java | 40 ++++++----- .../cm/ManagedServiceFactoryTracker.java | 11 ++-- .../internal/cm/ManagedServiceTracker.java | 32 ++++----- 5 files changed, 111 insertions(+), 41 deletions(-) diff --git a/bundles/org.eclipse.equinox.cm/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.cm/META-INF/MANIFEST.MF index 84a50cbb60e..6d95f2232bc 100644 --- a/bundles/org.eclipse.equinox.cm/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.cm/META-INF/MANIFEST.MF @@ -8,8 +8,9 @@ Bundle-Version: 1.6.0.qualifier Bundle-Activator: org.eclipse.equinox.internal.cm.Activator Import-Package: org.osgi.framework;version="1.7.0", org.osgi.service.cm;version="[1.6,1.7)", + org.osgi.service.coordinator;version="[1.0.0,2.0.0]", + org.osgi.service.event;version="1.0";resolution:=optional, org.osgi.service.log;version="1.3.0", - org.osgi.service.event;version="1.0"; resolution:=optional, org.osgi.util.tracker;version="1.3.1" Bundle-RequiredExecutionEnvironment: JavaSE-17 Provide-Capability: diff --git a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ConfigurationAdminFactory.java b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ConfigurationAdminFactory.java index 14e1dfce0e4..36861f8396a 100644 --- a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ConfigurationAdminFactory.java +++ b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ConfigurationAdminFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2019 Cognos Incorporated, IBM Corporation and others. + * Copyright (c) 2005, 2024 Cognos Incorporated, IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,14 +11,18 @@ * Contributors: * Cognos Incorporated - initial API and implementation * IBM Corporation - bug fixes and enhancements + * Christoph Läubrich - add support for Coordinator *******************************************************************************/ package org.eclipse.equinox.internal.cm; import java.security.Permission; -import java.util.Dictionary; +import java.util.*; +import java.util.function.Predicate; import org.osgi.framework.*; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.cm.ConfigurationPermission; +import org.osgi.service.coordinator.*; +import org.osgi.util.tracker.ServiceTracker; /** * ConfigurationAdminFactory provides a Configuration Admin ServiceFactory but @@ -37,17 +41,20 @@ public class ConfigurationAdminFactory implements ServiceFactory coordinationServiceTracker; public ConfigurationAdminFactory(BundleContext context, LogTracker log) { this.log = log; configurationStore = new ConfigurationStore(this, context); - eventDispatcher = new EventDispatcher(context, log); + eventDispatcher = new EventDispatcher(context, log, this); pluginManager = new PluginManager(context); managedServiceTracker = new ManagedServiceTracker(this, configurationStore, context); managedServiceFactoryTracker = new ManagedServiceFactoryTracker(this, configurationStore, context); + coordinationServiceTracker = new ServiceTracker<>(context, Coordinator.class, null); } void start() { + coordinationServiceTracker.open(); eventDispatcher.start(); pluginManager.start(); managedServiceTracker.open(); @@ -59,6 +66,7 @@ void stop() { managedServiceFactoryTracker.close(); eventDispatcher.stop(); pluginManager.stop(); + coordinationServiceTracker.close(); } @Override @@ -161,4 +169,56 @@ void notifyLocationChanged(ConfigurationImpl config, String oldLocation, boolean Dictionary modifyConfiguration(ServiceReference reference, ConfigurationImpl config) { return pluginManager.modifyConfiguration(reference, config); } + + ConfigurationAdminParticipant coordinationParticipant(Coordination coordination) { + return coordination.getParticipants().stream().filter(ConfigurationAdminParticipant.class::isInstance) + .map(ConfigurationAdminParticipant.class::cast).findFirst() + .orElseGet(() -> new ConfigurationAdminParticipant(coordination)); + } + + Optional coordinate() { + return Optional.ofNullable(coordinationServiceTracker.getService()).map(Coordinator::peek) + .filter(Predicate.not(Coordination::isTerminated)); + } + + private final class ConfigurationAdminParticipant implements Participant { + + private Map tasks = new LinkedHashMap<>(); + + public ConfigurationAdminParticipant(Coordination coordination) { + coordination.addParticipant(this); + } + + @Override + public void ended(Coordination coordination) throws Exception { + finish(coordination); + } + + @Override + public void failed(Coordination coordination) throws Exception { + finish(coordination); + } + + private void finish(Coordination coordination) { + tasks.values().forEach(Runnable::run); + tasks.clear(); + } + + public void cancelTask(Object key) { + tasks.remove(key); + } + + public void addTask(Object key, Runnable runnable) { + tasks.put(key, runnable); + } + } + + void executeCoordinated(Object key, Runnable runnable) { + coordinate().ifPresentOrElse(coordination -> coordinationParticipant(coordination).addTask(key, runnable), + () -> runnable.run()); + } + + void cancelExecuteCoordinated(Object key) { + coordinate().ifPresent(coordination -> coordinationParticipant(coordination).cancelTask(key)); + } } diff --git a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/EventDispatcher.java b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/EventDispatcher.java index ad7004faee2..281cc1d1109 100644 --- a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/EventDispatcher.java +++ b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/EventDispatcher.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2018 Cognos Incorporated, IBM Corporation and others. + * Copyright (c) 2005, 2024 Cognos Incorporated, IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,6 +11,7 @@ * Contributors: * Cognos Incorporated - initial API and implementation * IBM Corporation - bug fixes and enhancements + * Christoph Läubrich - add support for Coordinator *******************************************************************************/ package org.eclipse.equinox.internal.cm; @@ -33,9 +34,11 @@ public class EventDispatcher { /** @GuardedBy this */ private ServiceReference configAdminReference; final LogTracker log; + private ConfigurationAdminFactory configurationAdminFactory; - public EventDispatcher(BundleContext context, LogTracker log) { + public EventDispatcher(BundleContext context, LogTracker log, ConfigurationAdminFactory configurationAdminFactory) { this.log = log; + this.configurationAdminFactory = configurationAdminFactory; tracker = new ServiceTracker<>(context, ConfigurationListener.class, null); syncTracker = new ServiceTracker<>(context, SynchronousConfigurationListener.class, null); } @@ -81,27 +84,32 @@ public void dispatchEvent(int type, String factoryPid, String pid) { return; for (final ServiceReference ref : refs) { - queue.put(new Runnable() { - @Override - public void run() { - ConfigurationListener listener = tracker.getService(ref); - if (listener == null) { - return; - } - try { - listener.configurationEvent(event); - } catch (Throwable t) { - log.error(t.getMessage(), t); - } - } - }); + configurationAdminFactory.executeCoordinated(new Object(), () -> enqueue(event, ref)); } } + protected void enqueue(final ConfigurationEvent event, final ServiceReference ref) { + queue.put(new Runnable() { + @Override + public void run() { + ConfigurationListener listener = tracker.getService(ref); + if (listener == null) { + return; + } + try { + listener.configurationEvent(event); + } catch (Throwable t) { + log.error(t.getMessage(), t); + } + } + }); + } + private synchronized ConfigurationEvent createConfigurationEvent(int type, String factoryPid, String pid) { if (configAdminReference == null) return null; return new ConfigurationEvent(configAdminReference, type, factoryPid, pid); } + } diff --git a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceFactoryTracker.java b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceFactoryTracker.java index 11559251ad1..b3658dc2a27 100644 --- a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceFactoryTracker.java +++ b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceFactoryTracker.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2018 Cognos Incorporated, IBM Corporation and others. + * Copyright (c) 2005, 2024 Cognos Incorporated, IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,6 +11,7 @@ * Contributors: * Cognos Incorporated - initial API and implementation * IBM Corporation - bug fixes and enhancements + * Christoph Läubrich - add support for Coordinator *******************************************************************************/ package org.eclipse.equinox.internal.cm; @@ -286,12 +287,12 @@ public void run() { private void asynchUpdated(final ManagedServiceFactory service, final String pid, final Dictionary properties) { + configurationAdminFactory.cancelExecuteCoordinated(service); if (properties == null) { return; } - queue.put(new Runnable() { - @Override - public void run() { + configurationAdminFactory.executeCoordinated(service, () -> { + queue.put(() -> { try { service.updated(pid, properties); } catch (ConfigurationException e) { @@ -301,7 +302,7 @@ public void run() { } catch (Throwable t) { configurationAdminFactory.error(t.getMessage(), t); } - } + }); }); } } diff --git a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceTracker.java b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceTracker.java index 5201f0034d9..d76b3d57b47 100644 --- a/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceTracker.java +++ b/bundles/org.eclipse.equinox.cm/src/org/eclipse/equinox/internal/cm/ManagedServiceTracker.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2018 Cognos Incorporated, IBM Corporation and others. + * Copyright (c) 2005, 2024 Cognos Incorporated, IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,6 +11,7 @@ * Contributors: * Cognos Incorporated - initial API and implementation * IBM Corporation - bug fixes and enhancements + * Christoph Läubrich - add support for Coordinator *******************************************************************************/ package org.eclipse.equinox.internal.cm; @@ -202,13 +203,14 @@ public void modifiedService(ServiceReference reference, ManagedS @Override public void removedService(ServiceReference reference, ManagedService service) { untrackManagedService(reference); - + configurationAdminFactory.cancelExecuteCoordinated(service); context.ungetService(reference); } private void addReference(ServiceReference reference, ManagedService service) { List> qualifiedPidLists = trackManagedService(reference); - updateManagedService(qualifiedPidLists, reference, service); + configurationAdminFactory.executeCoordinated(service, + () -> updateManagedService(qualifiedPidLists, reference, service)); } private void updateManagedService(List> qualifiedPidLists, ServiceReference reference, @@ -283,19 +285,17 @@ private List> getManagedServiceReferences(Strin } private void asynchUpdated(final ManagedService service, final Dictionary properties) { - queue.put(new Runnable() { - @Override - public void run() { - try { - service.updated(properties); - } catch (ConfigurationException e) { - // we might consider doing more for ConfigurationExceptions - Throwable cause = e.getCause(); - configurationAdminFactory.error(e.getMessage(), cause != null ? cause : e); - } catch (Throwable t) { - configurationAdminFactory.error(t.getMessage(), t); - } + configurationAdminFactory.cancelExecuteCoordinated(service); + configurationAdminFactory.executeCoordinated(service, () -> queue.put(() -> { + try { + service.updated(properties); + } catch (ConfigurationException e) { + // we might consider doing more for ConfigurationExceptions + Throwable cause = e.getCause(); + configurationAdminFactory.error(e.getMessage(), cause != null ? cause : e); + } catch (Throwable t) { + configurationAdminFactory.error(t.getMessage(), t); } - }); + })); } }