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 <[email protected]>

> 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 <[email protected]>
>
> 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 <[email protected]>
>>
>>  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
>>> > [email protected]
>>> > https://dev.eclipse.org/mailman/listinfo/equinox-dev
>>> >
>>>
>>> _______________________________________________
>>> equinox-dev mailing list
>>> [email protected]
>>> https://dev.eclipse.org/mailman/listinfo/equinox-dev
>>>
>>
>>
>
_______________________________________________
equinox-dev mailing list
[email protected]
https://dev.eclipse.org/mailman/listinfo/equinox-dev

Reply via email to