[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [equinox-dev] Is there a way to capture BundleExceptions?

OK, I found a way but it's bizarre....
I'm going to post the solution here for future reference, also if you know if a better, simpler way, please let me know.

What happens is that Equinox will not create BundleException instances when bundles cannot be resolved. Instead, it just logs it to the BaseAdaptor's FrameworkLog and move on. I cannot get access to what's coming into the BaseAdaptor's framework log, so I had to get it some other way. The solution is was follows:

1 - Created an AdaptorHook that keeps a reference to the BaseAdaptor instance passed to it from the initialize method.

2 - This adaptor hook register a service (which I called IBundleResolutionErrorService) using the system bundle context. This service is just to expose the adaptor hook's functionality to other bundles. This service has a single method which is getBundleResolutionErrors, which returns a list of BundleResolutionError generated for each unresolved bundle. This is a simple class that encapsulates the VersionConstraints or ResolverError instances which describe why the bundle was not resolved.

3 - Craeate the detectAllUnresolvedBundles method inside your adaptor hook to find all unresolved bundles (see below for the method's code). The method I used is a modified version of the equinox's own way of detecting unresolved bundles.

4 - Invoke the adaptor hook's detectAllUnresolvedBundles method from the service's getBundleResolutionErrors method and return the result.

5 - Then create another bundle (I'll just call it X), non-fragment, which obtains a reference to the IBundleResolutionErrorService service. This bundle listens for an event that is raised by my application class (the class implementing IApplication) so it knows when the startup has finished. At this point it knows that the framework has finished starting up and all bundle resolution errors can be access by invoking the getBundleResolutionErrors method from the service IBundleResolutionErrorService.

6 - The last step is to raise an event from your application class from within it's start method bundle X can query for bundle resolution errors.


Here is the method that does the trick, and is placed inside the adaptor hook (as I said before this method is heavily based on how Equinox detects unresolved bundles):

    Collection<BundleResolutionError> getAllBundleResolutionErrors() {
        Bundle[] bundles = systemContext.getBundles();
        State state = baseAdaptor.getState();
        StateHelper stateHelper = baseAdaptor.getPlatformAdmin().getStateHelper();

        VersionConstraint[] leafConstraints = stateHelper.getUnsatisfiedLeaves(state.getBundles());
        // hash the missing leaf constraints by the declaring bundles
        Map<BundleDescription, BundleResolutionError> missing = new HashMap<BundleDescription, BundleResolutionError>();
        for (int i = 0; i < leafConstraints.length; i++) {
            // only include non-optional and non-dynamic constraint leafs
            if (leafConstraints[i] instanceof BundleSpecification && ((BundleSpecification) leafConstraints[i]).isOptional())
                continue;
            if (leafConstraints[i] instanceof ImportPackageSpecification) {
                if (ImportPackageSpecification.RESOLUTION_OPTIONAL.equals(((ImportPackageSpecification) leafConstraints[i]).getDirective(Constants.RESOLUTION_DIRECTIVE)))
                    continue;
                if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(((ImportPackageSpecification) leafConstraints[i]).getDirective(Constants.RESOLUTION_DIRECTIVE)))
                    continue;
            }
            BundleDescription bundle = leafConstraints[i].getBundle();
            BundleResolutionError resolutionError = (BundleResolutionError)missing.get(bundle);
           
            if (resolutionError == null) {
                resolutionError = new BundleResolutionError(bundle);
                resolutionError.setRootError(true);
                missing.put(bundle, resolutionError);
            }
            resolutionError.getUnsatisfiedContraints().add(leafConstraints[i]);
        }

        // There may be some bundles unresolved for other reasons, causing the system to be unresolved
        for (int i = 0; i < bundles.length; i++) {
            if (bundles[i].getState() == Bundle.INSTALLED) {
                BundleDescription description = state.getBundle(bundles[i].getBundleId());
               
                // for some reason, the state does not know about that bundle
                if (description == null)
                    continue;
               
                BundleResolutionError resolutionError = missing.get(description);
                if (resolutionError == null) {
                    resolutionError = new BundleResolutionError(description);
                    missing.put(description, resolutionError);
                }
               
                VersionConstraint[] unsatisfied = stateHelper.getUnsatisfiedConstraints(description);
                if (unsatisfied.length > 0) {
                    // the bundle wasn't resolved due to some of its constraints were unsatisfiable
                    for (int j = 0; j < unsatisfied.length; j++) {
                        resolutionError.getUnsatisfiedContraints().add(unsatisfied[j]);
                    }

                } else {
                    ResolverError[] resolverErrors = state.getResolverErrors(description);
                    for (int j = 0; j < resolverErrors.length; j++)
                        resolutionError.getResolverErrors().add(resolverErrors[j]);

                }
            }
        }
        return missing.values();
    }


The BundleResolutionError class is simple:

public class BundleResolutionError {
    // root resolution errors are those that cause other errors (non root).
    // fixing root errors will cause non-root errors to get fixed as well.
    private boolean isRootError = false;
    private final BundleDescription bundleDescription;
    private Set<VersionConstraint> unsatisfiedContraints = new HashSet<VersionConstraint>();
    private Set<ResolverError> resolverErrors = new HashSet<ResolverError>();

     /* .... getters and setters .... */

}


I hope this is useful to someone else as it took me a almost an hour to get all working (after I figured how to do it).

Thank you!
Eduardo Born



2010/8/16 Eduardo Born <nosachamos@xxxxxxxxx>
I also tried creating a FrameworkLog and returning it using an AdaptorHook as well, but no log entries are passed to it by the framework. I think this one is really just mean to be used by my own adaptor hook and will not receive log entries from other parts of the framework.

2010/8/16 Eduardo Born <nosachamos@xxxxxxxxx>

Hi!
Thank you for your reply!

I have tried it, but I don't get FrameworkEvents for bundles not resolved due to missing constraints. To unsure I was registering the listener as soon as possible I created an AdaptorHook and registered the listener on the system bundle context within the startFramework method. I got one BundleException due to missing classpath entry for one of the bundles:
org.osgi.framework.BundleException: The bundle class path entry "runtime_registry_compatibility.jar" could not be found for the bundle "reference:file:/D:/eclipse/plugins/org.eclipse.equinox.registry_3.5.0.v20100503.jar"

...but for all non-resolved bundles there were not events raised.

I saw for example in Equinox's log entries like:

!MESSAGE Bundle reference:file:/D:/Scitec/automation/old tests/Target Platform/Third Party Plugins/plugins/org.hibernate_3.3.0.jar was not resolved.
!SUBENTRY 2 org.hibernate 2 0 2010-08-16 11:36:28.475
!MESSAGE Missing required bundle antlr_2.7.6.

Which I caused on purpose by removing bundle antlr_2.7.6 to test this error listening mechanisms. For this and other problems there were no framework events generated.

Any other ideas? I'll keep trying and will let you know if I find the answer, but at this point I'm running out of ideas where to look at.

Thank you!
Eduardo Born

 I was able to capture one BundleException



2010/8/16 Fabian Pecher <fabian.pecher@xxxxxxxxxxxxxx>

Hi Eduardo,

have you looked into OSGi's own asynchronous FrameworkEventListener? If
you solely need to capture the BundleException, you should get it by
invoking FrameworkEvent.getThrowable() on ERROR events (yet, I haven't
tried it myself :) ).

Cheers,
Fabian

Eduardo Born wrote:
> Hi!
> I'm developing on top of Equinox and couldn't find a way to capture or
> listen to BundleExceptions raised by the framework. More specifically, I
> want to capture bundle exceptions thrown when bundles cannot be resolved due
> to any cause, for example missing constraints. Later on, I'll display a
> dialog with all bundle exceptions to help me debug these issues.
>
> I've used equinox outside Eclipse and was able to capture all bundle
> exceptions there since I was the one discovering bundles, installing and
> starting some of them, but now that I'm lauching a product from within
> eclipse I can't find a way to listen for those exceptions... I've looked
> into adaptohooks but none of them seem to help.
>
> Please let me know how to accomplish this, I have looked into this
> extensively and would greatly appreciate any clues you might have.
>
> Thank you!
> Eduardo Born
>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> equinox-dev mailing list
> equinox-dev@xxxxxxxxxxx
_______________________________________________
equinox-dev mailing list
equinox-dev@xxxxxxxxxxx