From 7f9d82fa47dab1f830961c81b13bf9623a34cf5e Mon Sep 17 00:00:00 2001 From: "Jason P. Leasure" Date: Tue, 16 Jun 2020 15:29:53 -0400 Subject: [PATCH] fix: refresh bundles synchronously - remove unnecessary waits during synchronous activate/deactivate --- .../app/plugin/core/osgi/BundleHost.java | 72 +++++++++++-------- .../plugin/core/osgi/GhidraSourceBundle.java | 2 +- .../ghidra/app/script/JavaScriptProvider.java | 2 +- 3 files changed, 46 insertions(+), 30 deletions(-) diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java index 2bd2b01c3a..beb1070051 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/BundleHost.java @@ -19,9 +19,9 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.Semaphore; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.IntStream; import org.apache.felix.framework.FrameworkFactory; import org.apache.felix.framework.util.FelixConstants; @@ -520,29 +520,13 @@ public class BundleHost { return frameworkBundleContext.getBundle(bundleLocation); } - private static boolean anyMatch(Bundle bundle, int... bundleStates) { - Integer bundleState = bundle.getState(); - return IntStream.of(bundleStates).anyMatch(bundleState::equals); - } - - private static void waitFor(Bundle bundle, int... bundleStates) throws InterruptedException { - while (true) { - if (anyMatch(bundle, bundleStates)) { - return; - } - Thread.sleep(500); - } - } - /** - * Activate a bundle, returning only after the bundle is active. + * Activate a bundle. Either an exception is thrown or the bundle will be in "ACTIVE" state. * * @param bundle the bundle - * @throws InterruptedException if the wait is interrupted * @throws GhidraBundleException if there's a problem activating */ - public void activateSynchronously(Bundle bundle) - throws InterruptedException, GhidraBundleException { + public void activateSynchronously(Bundle bundle) throws GhidraBundleException { if (bundle.getState() == Bundle.ACTIVE) { return; } @@ -555,11 +539,10 @@ public class BundleHost { fireBundleException(bundleException); throw bundleException; } - waitFor(bundle, Bundle.ACTIVE); } /** - * Activate a bundle, returning only after the bundle is active. + * Activate a bundle. Either an exception is thrown or the bundle will be in "ACTIVE" state. * * @param bundleLocation the bundle location identifier * @throws InterruptedException if the wait is interrupted @@ -575,14 +558,12 @@ public class BundleHost { } /** - * Deactivate a bundle, returning only after the bundle is inactive. + * Deactivate a bundle. Either an exception is thrown or the bundle will be in "UNINSTALLED" state. * * @param bundle the bundle - * @throws InterruptedException if the wait is interrupted * @throws GhidraBundleException if there's a problem activating */ - public void deactivateSynchronously(Bundle bundle) - throws InterruptedException, GhidraBundleException { + public void deactivateSynchronously(Bundle bundle) throws GhidraBundleException { if (bundle.getState() == Bundle.UNINSTALLED) { return; } @@ -593,7 +574,10 @@ public class BundleHost { Bundle dependentBundle = dependentBundles.pop(); try { dependentBundle.uninstall(); - frameworkWiring.refreshBundles(dependentBundles); + if (dependentBundle.getState() != Bundle.UNINSTALLED) { + System.err.printf("It ain't uninstalled!\n"); + } + refreshBundlesSynchronously(new ArrayList<>(dependentBundles)); } catch (BundleException e) { GhidraBundleException exception = @@ -601,12 +585,11 @@ public class BundleHost { fireBundleException(exception); throw exception; } - waitFor(dependentBundle, Bundle.UNINSTALLED); } } /** - * Deactivate a bundle, returning only after the bundle is inactive. + * Deactivate a bundle. Either an exception is thrown or the bundle will be in "UNINSTALLED" state. * * @param bundleLocation the bundle location identifier * @throws InterruptedException if the wait is interrupted @@ -620,6 +603,39 @@ public class BundleHost { } } + /** + * Refreshes the specified bundles. This forces the update (replacement) + * or removal of packages exported by the specified bundles. + * + * @param bundles the bundles to refresh + * @see FrameworkWiring#refreshBundles + */ + protected void refreshBundlesSynchronously(Collection bundles) { + FrameworkWiring frameworkWiring = felixFramework.adapt(FrameworkWiring.class); + Semaphore sema = new Semaphore(0); + frameworkWiring.refreshBundles(bundles, new FrameworkListener() { + @Override + public void frameworkEvent(FrameworkEvent event) { + switch (event.getType()) { + case FrameworkEvent.ERROR: + Bundle bundle = event.getBundle(); + Msg.error(BundleHost.this, + String.format("OSGi error refreshing bundle: %s", bundle)); + break; + case FrameworkEvent.PACKAGES_REFRESHED: + sema.release(); + break; + } + } + }); + try { + sema.acquire(); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + } + /** * Remove a listener for OSGi framework events. * diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java index f9b3afd25b..4772920b36 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/osgi/GhidraSourceBundle.java @@ -523,7 +523,7 @@ public class GhidraSourceBundle extends GhidraBundle { } return anythingChanged | wipeBinDir(); } - catch (IOException | GhidraBundleException | InterruptedException e) { + catch (IOException | GhidraBundleException e) { Msg.showError(this, null, "source bundle clean error", "while attempting to delete the compiled directory, an exception was thrown", e); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java index e1eefcb3d6..9b1fdaad94 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/script/JavaScriptProvider.java @@ -65,7 +65,7 @@ public class JavaScriptProvider extends GhidraScriptProvider { bundleHost.deactivateSynchronously(osgiBundle); } } - catch (GhidraBundleException | InterruptedException e) { + catch (GhidraBundleException e) { e.printStackTrace(); Msg.error(this, "while deactivating bundle for delete", e); return false;