From 1682857bc26009441f070c344537c9fad926e992 Mon Sep 17 00:00:00 2001 From: Eike Stepper Date: Thu, 10 Oct 2024 10:10:12 +0200 Subject: [PATCH] Provide TARGET_SAVED and TARGET_DELETED events When building tools on top of PDE's target platform framework it's often necessary to be informed about state changes in targets. PDE already uses an IEventBroker to send "workspaceTargetChanged" events. This change is about sending two new events: TARGET_SAVED and TARGET_DELETED --- .../.settings/.api_filters | 22 +++++ ui/org.eclipse.pde.core/META-INF/MANIFEST.MF | 3 +- .../eclipse/pde/core/target/TargetEvents.java | 99 ++++++++++++++++++- .../core/target/AbstractTargetHandle.java | 10 +- .../core/target/ExternalFileTargetHandle.java | 2 +- .../core/target/LocalTargetHandle.java | 2 +- .../core/target/TargetPlatformService.java | 45 ++++++--- .../target/WorkspaceFileTargetHandle.java | 2 +- .../editor/targetdefinition/TargetEditor.java | 16 +++ 9 files changed, 179 insertions(+), 22 deletions(-) diff --git a/ui/org.eclipse.pde.core/.settings/.api_filters b/ui/org.eclipse.pde.core/.settings/.api_filters index f26c3f1cfb..8d60950802 100644 --- a/ui/org.eclipse.pde.core/.settings/.api_filters +++ b/ui/org.eclipse.pde.core/.settings/.api_filters @@ -1,5 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF index 898c1c96cf..604ef4b871 100644 --- a/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF +++ b/ui/org.eclipse.pde.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %name Bundle-SymbolicName: org.eclipse.pde.core; singleton:=true -Bundle-Version: 3.19.100.qualifier +Bundle-Version: 3.20.0.qualifier Bundle-Activator: org.eclipse.pde.internal.core.PDECore Bundle-Vendor: %provider-name Bundle-Localization: plugin @@ -101,6 +101,7 @@ Import-Package: aQute.bnd.build;version="[4.4.0,5.0.0)", org.eclipse.equinox.internal.p2.publisher.eclipse, org.eclipse.equinox.p2.publisher, org.eclipse.equinox.p2.publisher.eclipse, + org.osgi.service.event;version="[1.4.0,2.0.0)";resolution:=optional, org.osgi.service.repository;version="[1.1.0,2.0.0)", org.osgi.util.promise;version="[1.3.0,2.0.0)" Require-Bundle: diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetEvents.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetEvents.java index 23cdcaa935..da74c97df5 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetEvents.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/core/target/TargetEvents.java @@ -12,28 +12,119 @@ ********************************************************************************/ package org.eclipse.pde.core.target; +import org.eclipse.e4.core.services.events.IEventBroker; +import org.osgi.service.event.EventHandler; + /** - * Target events and event topic definitions + * Target events and event topic definitions. Can be used as follows: + * + *
+ * EventHandler eventHandler = event -> {
+ * 	Object data = event.getProperty(IEventBroker.DATA);
+ * 	if (data instanceof ITargetHandle) {
+ * 		ITargetHandle handle = (ITargetHandle) data;
+ * 		// Work with the target handle...
+ * 	}
+ * };
  *
+ * IEclipseContext context = EclipseContextFactory.getServiceContext(bundleContext);
+ * IEventBroker broker = context.get(IEventBroker.class);
+ * if (broker != null) {
+ * 	broker.subscribe(TargetEvents.TOPIC_TARGET_SAVED, eventHandler);
+ * 	// Do not forget to unsubscribe later!
+ * }
+ * 
+ * + * @see ITargetPlatformService + * @see IEventBroker#subscribe(String, EventHandler) + * @see IEventBroker#subscribe(String, String, EventHandler, boolean) + * @see IEventBroker#unsubscribe(EventHandler) * @since 3.13 */ public class TargetEvents { /** - * Base topic for all Target events + * Base topic for all target events. */ public static final String TOPIC_BASE = "org/eclipse/pde/core/target/TargetEvents"; //$NON-NLS-1$ /** - * Topic for all Target events + * Topic for all target events. */ public static final String TOPIC_ALL = TOPIC_BASE + "/*"; //$NON-NLS-1$ /** - * Sent when workspace target definition is changed + * Sent when workspace target definition is changed. Can be used as follows: + * + *
+	 * EventHandler eventHandler = event -> {
+	 * 	Object data = event.getProperty(IEventBroker.DATA);
+	 * 	if (data instanceof ITargetDefinition) {
+	 * 		ITargetDefinition definition = (ITargetDefinition) data;
+	 * 		// Work with the target definition...
+	 * 	}
+	 * };
+	 *
+	 * IEclipseContext context = EclipseContextFactory.getServiceContext(bundleContext);
+	 * IEventBroker broker = context.get(IEventBroker.class);
+	 * if (broker != null) {
+	 * 	broker.subscribe(TargetEvents.TOPIC_WORKSPACE_TARGET_CHANGED, eventHandler);
+	 * 	// Do not forget to unsubscribe later!
+	 * }
+	 * 
* * @see ITargetPlatformService#getWorkspaceTargetDefinition() */ public static final String TOPIC_WORKSPACE_TARGET_CHANGED = TOPIC_BASE + "/workspaceTargetChanged"; //$NON-NLS-1$ + /** + * Sent when a target is saved. Can be used as follows: + * + *
+	 * EventHandler eventHandler = event -> {
+	 * 	Object data = event.getProperty(IEventBroker.DATA);
+	 * 	if (data instanceof ITargetHandle) {
+	 * 		ITargetHandle handle = (ITargetHandle) data;
+	 * 		// Work with the target handle...
+	 * 	}
+	 * };
+	 *
+	 * IEclipseContext context = EclipseContextFactory.getServiceContext(bundleContext);
+	 * IEventBroker broker = context.get(IEventBroker.class);
+	 * if (broker != null) {
+	 * 	broker.subscribe(TargetEvents.TOPIC_TARGET_SAVED, eventHandler);
+	 * 	// Do not forget to unsubscribe later!
+	 * }
+	 * 
+ * + * @see ITargetPlatformService#saveTargetDefinition(ITargetDefinition) + * @see IEventBroker + * @since 3.20 + */ + public static final String TOPIC_TARGET_SAVED = TOPIC_BASE + "/targetSaved"; //$NON-NLS-1$ + + /** + * Sent when a target is deleted. Can be used as follows: + * + *
+	 * EventHandler eventHandler = event -> {
+	 * 	Object data = event.getProperty(IEventBroker.DATA);
+	 * 	if (data instanceof ITargetHandle) {
+	 * 		ITargetHandle handle = (ITargetHandle) data;
+	 * 		// Work with the target handle...
+	 * 	}
+	 * };
+	 *
+	 * IEclipseContext context = EclipseContextFactory.getServiceContext(bundleContext);
+	 * IEventBroker broker = context.get(IEventBroker.class);
+	 * if (broker != null) {
+	 * 	broker.subscribe(TargetEvents.TOPIC_TARGET_DELETED, eventHandler);
+	 * 	// Do not forget to unsubscribe later!
+	 * }
+	 * 
+ * + * @see ITargetPlatformService#deleteTarget(ITargetHandle) + * @since 3.20 + */ + public static final String TOPIC_TARGET_DELETED = TOPIC_BASE + "/targetDeleted"; //$NON-NLS-1$ } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/AbstractTargetHandle.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/AbstractTargetHandle.java index 9fd550a693..6bb76bddc6 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/AbstractTargetHandle.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/AbstractTargetHandle.java @@ -20,6 +20,7 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.pde.core.target.ITargetDefinition; import org.eclipse.pde.core.target.ITargetHandle; +import org.eclipse.pde.core.target.TargetEvents; /** * Common implementation of target handles. @@ -53,11 +54,18 @@ public ITargetDefinition getTargetDefinition() throws CoreException { */ abstract void delete() throws CoreException; + public final void save(ITargetDefinition definition) throws CoreException { + doSave(definition); + + TargetPlatformService service = (TargetPlatformService) TargetPlatformService.getDefault(); + service.scheduleEvent(TargetEvents.TOPIC_TARGET_SAVED, definition.getHandle()); + } + /** * Saves the definition to underlying storage. * * @param definition target to save * @throws CoreException on failure */ - abstract void save(ITargetDefinition definition) throws CoreException; + abstract void doSave(ITargetDefinition definition) throws CoreException; } diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ExternalFileTargetHandle.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ExternalFileTargetHandle.java index 92dff3e733..ed2cda5460 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ExternalFileTargetHandle.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/ExternalFileTargetHandle.java @@ -81,7 +81,7 @@ protected InputStream getInputStream() throws CoreException { @Override - void save(ITargetDefinition definition) throws CoreException { + void doSave(ITargetDefinition definition) throws CoreException { try (OutputStream stream = new BufferedOutputStream(new FileOutputStream(fFile))) { ((TargetDefinition) definition).write(stream); } catch (IOException e) { diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/LocalTargetHandle.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/LocalTargetHandle.java index 35396814ff..64237b274b 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/LocalTargetHandle.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/LocalTargetHandle.java @@ -193,7 +193,7 @@ protected OutputStream getOutputStream() throws CoreException { } @Override - void save(ITargetDefinition definition) throws CoreException { + void doSave(ITargetDefinition definition) throws CoreException { try (OutputStream stream = getOutputStream()) { ((TargetDefinition) definition).write(stream); } catch (IOException e) { diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetPlatformService.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetPlatformService.java index d99ca4b00b..0f8d1ed30a 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetPlatformService.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/TargetPlatformService.java @@ -78,6 +78,7 @@ import org.eclipse.pde.internal.core.PDEPreferencesManager; import org.eclipse.pde.internal.core.TargetDefinitionManager; import org.eclipse.pde.internal.core.TargetPlatformHelper; +import org.osgi.framework.BundleContext; import org.osgi.service.prefs.BackingStoreException; /** @@ -161,6 +162,7 @@ public void deleteTarget(ITargetHandle handle) throws CoreException { } ((AbstractTargetHandle) handle).delete(); TargetPlatformHelper.getTargetDefinitionMap().remove(handle); + scheduleEvent(TargetEvents.TOPIC_TARGET_DELETED, handle); } @Override @@ -306,7 +308,8 @@ public ITargetDefinition newTarget() { @Override public void saveTargetDefinition(ITargetDefinition definition) throws CoreException { - ((AbstractTargetHandle) definition.getHandle()).save(definition); + ITargetHandle handle = definition.getHandle(); + ((AbstractTargetHandle) handle).save(definition); } @Override @@ -365,24 +368,30 @@ public void setWorkspaceTargetDefinition(ITargetDefinition target, boolean async boolean changed = !Objects.equals(oldTarget, target); if (changed) { if (asyncEvents) { - eventSendingJob.schedule(target); + scheduleEvent(TargetEvents.TOPIC_WORKSPACE_TARGET_CHANGED, target); } else { - notifyTargetChanged(target); + notifyEvent(TargetEvents.TOPIC_WORKSPACE_TARGET_CHANGED, target); } } } - static void notifyTargetChanged(ITargetDefinition target) { - IEclipseContext context = EclipseContextFactory.getServiceContext(PDECore.getDefault().getBundleContext()); + public final void scheduleEvent(String topic, Object data) { + eventSendingJob.schedule(topic, data); + } + + private static void notifyEvent(String topic, Object data) { + BundleContext bundleContext = PDECore.getDefault().getBundleContext(); + IEclipseContext context = EclipseContextFactory.getServiceContext(bundleContext); + IEventBroker broker = context.get(IEventBroker.class); if (broker != null) { - broker.send(TargetEvents.TOPIC_WORKSPACE_TARGET_CHANGED, target); + broker.send(topic, data); } } - static class EventDispatcher extends Job { + private static final class EventDispatcher extends Job { - private final ConcurrentLinkedQueue queue; + private final ConcurrentLinkedQueue queue; private final Object myFamily; /** @@ -406,9 +415,9 @@ public boolean belongsTo(Object family) { @Override protected IStatus run(IProgressMonitor monitor) { - ITargetDefinition target; - while ((target = queue.poll()) != null && !monitor.isCanceled()) { - notifyTargetChanged(target); + EventInfo eventInfo; + while ((eventInfo = queue.poll()) != null && !monitor.isCanceled()) { + notifyEvent(eventInfo.topic, eventInfo.data); } if (!queue.isEmpty() && !monitor.isCanceled()) { // in case actions got faster scheduled then processed @@ -424,10 +433,20 @@ protected IStatus run(IProgressMonitor monitor) { /** * Enqueue a task asynchronously. **/ - public void schedule(ITargetDefinition target) { - queue.offer(target); + public void schedule(String topic, Object data) { + queue.offer(new EventInfo(topic, data)); schedule(); // will reschedule if already running } + + private static final class EventInfo { + final String topic; + final Object data; + + EventInfo(String topic, Object data) { + this.topic = topic; + this.data = data; + } + } } /** diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/WorkspaceFileTargetHandle.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/WorkspaceFileTargetHandle.java index 57e1ba08a6..34a003631d 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/WorkspaceFileTargetHandle.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/target/WorkspaceFileTargetHandle.java @@ -76,7 +76,7 @@ public String getMemento() throws CoreException { } @Override - public void save(ITargetDefinition definition) throws CoreException { + void doSave(ITargetDefinition definition) throws CoreException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ((TargetDefinition) definition).write(outputStream); ByteArrayInputStream stream = new ByteArrayInputStream(outputStream.toByteArray()); diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java index 3ac514f8bf..a8e535a566 100644 --- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java +++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/targetdefinition/TargetEditor.java @@ -65,6 +65,7 @@ import org.eclipse.pde.internal.core.PDEPreferencesManager; import org.eclipse.pde.internal.core.target.P2TargetUtils; import org.eclipse.pde.internal.core.target.TargetDefinitionPersistenceHelper; +import org.eclipse.pde.internal.core.target.TargetPlatformService; import org.eclipse.pde.internal.core.target.WorkspaceFileTargetHandle; import org.eclipse.pde.internal.ui.IHelpContextIds; import org.eclipse.pde.internal.ui.PDEPlugin; @@ -175,6 +176,7 @@ protected void pageChange(int newPageIndex) { @Override public void doSave(IProgressMonitor monitor) { + ITargetHandle handle = fInputHandler.getTarget().getHandle(); fInputHandler.setSaving(true); if (!isActiveTabTextualEditor()) { markStale(); @@ -184,6 +186,9 @@ public void doSave(IProgressMonitor monitor) { fDirty = false; editorDirtyStateChanged(); fInputHandler.setSaving(false); + + TargetPlatformService service = (TargetPlatformService) TargetPlatformService.getDefault(); + service.scheduleEvent(TargetEvents.TOPIC_TARGET_SAVED, handle); } @Override @@ -286,6 +291,17 @@ public void dispose() { super.dispose(); } + @Override + public T getAdapter(Class adapter) { + if (adapter.equals(ITargetHandle.class)) { + ITargetDefinition target = getTarget(); + if (target != null) { + return adapter.cast(target.getHandle()); + } + } + + return super.getAdapter(adapter); + } /** * Returns the target model backing this editor * @return target model