fix: refresh bundles synchronously

- remove unnecessary waits during synchronous activate/deactivate
This commit is contained in:
Jason P. Leasure 2020-06-16 15:29:53 -04:00
parent 0d520dae16
commit 7f9d82fa47
3 changed files with 46 additions and 30 deletions

View file

@ -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<Bundle> 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.
*

View file

@ -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);
}

View file

@ -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;