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..ab904885cf8 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,19 @@ * 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.concurrent.ConcurrentHashMap; +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 +42,22 @@ public class ConfigurationAdminFactory implements ServiceFactory coordinationServiceTracker; + + private final Map coordinationParticipants = new ConcurrentHashMap<>(); 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 +69,7 @@ void stop() { managedServiceFactoryTracker.close(); eventDispatcher.stop(); pluginManager.stop(); + coordinationServiceTracker.close(); } @Override @@ -137,17 +148,24 @@ void dispatchEvent(int type, String factoryPid, String pid) { } void notifyConfigurationUpdated(ConfigurationImpl config, boolean isFactory) { - if (isFactory) + if (isFactory) { managedServiceFactoryTracker.notifyUpdated(config); - else + } else { managedServiceTracker.notifyUpdated(config); + } + } + + ConfigurationAdminParticipant coordinationParticipant(Coordination coordination) { + return coordinationParticipants.computeIfAbsent(coordination.getId(), + c -> new ConfigurationAdminParticipant(coordination)); } void notifyConfigurationDeleted(ConfigurationImpl config, boolean isFactory) { - if (isFactory) + if (isFactory) { managedServiceFactoryTracker.notifyDeleted(config); - else + } else { managedServiceTracker.notifyDeleted(config); + } } void notifyLocationChanged(ConfigurationImpl config, String oldLocation, boolean isFactory) { @@ -161,4 +179,50 @@ void notifyLocationChanged(ConfigurationImpl config, String oldLocation, boolean Dictionary modifyConfiguration(ServiceReference reference, ConfigurationImpl config) { return pluginManager.modifyConfiguration(reference, config); } + + 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 HashMap<>(); + + 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..20bf737c673 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,12 +11,15 @@ * 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 org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.service.cm.*; +import org.osgi.service.coordinator.Coordination; +import org.osgi.service.coordinator.Participant; import org.osgi.util.tracker.ServiceTracker; /** @@ -33,9 +36,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,23 +86,49 @@ 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; + configurationAdminFactory.coordinate().ifPresentOrElse(coordinate -> { + coordinate.addParticipant(new Participant() { + + private boolean done; + + @Override + public void failed(Coordination coordination) throws Exception { + deliverEvent(); } - try { - listener.configurationEvent(event); - } catch (Throwable t) { - log.error(t.getMessage(), t); + + @Override + public void ended(Coordination coordination) throws Exception { + deliverEvent(); } - } - }); + + private void deliverEvent() { + if (!done) { + done = true; + enqueue(event, ref); + } + } + }); + }, () -> 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; 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..0b9eb679682 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; @@ -283,19 +284,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); } - }); + })); } }