Is there a simply way to list of consumer components for an Interface?

675 Views Asked by At

Fellow coders,

I'm currently trying to find a simple and concise way to get a listing of of Services/Components that use a given Interface. I'm using the gogo-shell of a running Liferay 7.1.x server and can't seem to find an easy and direct way to to just that.

We want to override references to the used service via OSGI-configuration, but first need to find all components using it. As there are static reluctant references to the service component, simply providing an alternative with a higher ranking is not a viable solution.

Here are the gogo related bundles I'm using:

   35|Active     |    6|Apache Felix Gogo Command (1.0.2)|1.0.2
   36|Active     |    6|Apache Felix Gogo Runtime (1.1.0.LIFERAY-PATCHED-2)|1.1.0.LIFERAY-PATCHED-2
   72|Active     |    6|Apache Felix Gogo Shell (1.1.0)|1.1.0
  542|Active     |   10|Liferay Foundation - Liferay Gogo Shell - Impl (1.0.13)|1.0.13
  543|Active     |   10|Liferay Gogo Shell Web (2.0.25)|2.0.25

So far I've been able to list all providers of an interface via se (interface=com.liferay.saml.runtime.servlet.profile.WebSsoProfile):

{com.liferay.saml.runtime.profile.WebSsoProfile, com.liferay.saml.runtime.servlet.profile.WebSsoProfile}={service.id=6293, service.bundleid=79, service.scope=bundle, component.name=com.liferay.saml.opensaml.integration.internal.servlet.profile.WebSsoProfileImpl, component.id=3963}
  "Registered by bundle:" de.haufe.leong.com.liferay.saml.opensaml.integration [79]
  "Bundles using service"
    com.liferay.saml.web_2.0.11 [82]
    com.liferay.saml.impl_2.0.12 [78]

See all bundle requirements via: inspect cap service:

com.liferay.saml.impl_2.0.12 [78] requires:
...
service; com.liferay.saml.runtime.profile.WebSsoProfile, com.liferay.saml.runtime.servlet.profile.WebSsoProfile provided by:
   de.haufe.leong.com.liferay.saml.opensaml.integration [79]
...

But listing the actual services from within these bundles that use the given interface (or the service component) has eluded me so far.

The only solution I see so far is listing all provided services of these bundles via scr:list bid and then check each service with scr:info componentId to see if it uses the WebSsoProfile service.

Do you guys know a faster way to find the services using the WebSsoProfile-service?


[EDIT]: We solved the problem without having to provide config overrides for all consumers of the WebSsoProfile service but rather ensure that our implementation is used by deactivating the default service on Server startup. You can see the approach described here.

Anyways for debugging purposes this kind of lookup would be very useful. So if anyone knows a way to retrieve the list of all consumers of an interface then please post your solution!

1

There are 1 best solutions below

4
On

The standard solution is using the inspect command. It has a special namespace for services. Since a service registration is a capability, you can use inspect capability service:

g! inspect c service
org.apache.felix.framework [0] provides:
----------------------------------------
service; org.osgi.service.resolver.Resolver with properties:
   service.bundleid = 0
   service.id = 1
   service.scope = singleton
service; org.osgi.service.packageadmin.PackageAdmin with properties:
   service.bundleid = 0
   service.id = 2
   service.scope = singleton
service; org.osgi.service.startlevel.StartLevel with properties:
   service.bundleid = 0
   service.id = 3
   service.scope = singleton

....

However, I find this command seriously useless. The command is inflexible and has a horrible output.

However, Gogo is way more powerful than people know. For one, you can use all the methods on the bundle context.

g! servicereferences org.osgi.service.startlevel.StartLevel null
000003   0 StartLevel

If you want to see the properties of each service:

g! each (servicereferences org.osgi.service.startlevel.StartLevel null) { $it properties }
[service.id=3, objectClass=[Ljava.lang.String;@4acd14d7, service.scope=singleton, service.bundleid=0]

You can make this into a built-in function:

g! srv = { servicereferences $1 null }
servicereferences $1 null
g! srv org.osgi.service.startlevel.StartLevel
000003   0 StartLevel                               

Unfortunately, the OSGi added an overloaded method in the Bundle Context for getServiceReferences() that throws an NPE when called with null. Gogo is awful with overloaded methods :-(

However, it is trivial to add your own command with a declarative service component. You could use the following:

@GogoCommand(scope="service", function="srv")
@Component(service=ServiceCommand.class)
public class ServiceCommand {

    @Activate
    BundleContext context;        

    @Descriptor("List all services")
    public ServiceReference<?>[] srv() throws InvalidSyntaxException {
        return context.getAllServiceReferences(null, null);
    }

    @Descriptor("List all services that match the name")
    public ServiceReference<?>[] srv(
            String... names)
            throws InvalidSyntaxException {
        ServiceReference<?>[] allServiceReferences = 
            context.getAllServiceReferences(null,null);
        if ( allServiceReferences==null)
            return new ServiceReference[0];
        return Stream.of(allServiceReferences)
                .filter(r -> {
                    String[] objectClass = (String[]) r.getProperty(Constants.OBJECTCLASS);
                    for (String oc : objectClass) {
                        for (String name : names)
                            if (oc.contains(name))
                                return true;
                    }
                    return names.length == 0;
                }).sorted().toArray(ServiceReference[]::new);
    }
}

This adds the srv command to Gogo:

g! srv Help Basic
000004   1 Basic                                    
000005   1 Inspect   

Update If you want to find which bundles are using a specific service, you could use:

g! each (srv X) { $it usingbundles }

Make sure you got the following dependencies on your classpath:

-buildpath: \
    org.osgi.service.component.annotations,\
    org.apache.felix.gogo.runtime, \
    org.osgi.framework