javaosgiapache-felixservice-factoryembedded-osgi

OSGi ServiceFactory returns an uncasteable Object


I've been trying to make this work for the whole day now and it's frustating cause i dont see anything wrong with my code.

Here are my classes:

DisplayerServiceFactory.java

package com.lincesoft.imperator.displayer.provider;

import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;

import com.lincesoft.imperator.displayer.api.Displayer;

public class DisplayerServiceFactory implements ServiceFactory<Displayer> {

    public static final String NAME = "displayer_service_factory";

    @Override
    public void ungetService(Bundle bundle, ServiceRegistration<Displayer> registration, Displayer service) {
        // TODO Auto-generated method stub
    }
    @Override
    public Displayer getService(Bundle bundle, ServiceRegistration<Displayer> registration) {
        return new DisplayerImplementation();
    }
}

This is my Activator.java for the bundle containing the DisplayerServiceFactory

package com.lincesoft.imperator.displayer.launcher;

import java.util.Hashtable;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;

import com.lincesoft.imperator.displayer.api.Displayer;
import com.lincesoft.imperator.displayer.provider.DisplayerServiceFactory;

public class Activator implements BundleActivator {

    private ServiceRegistration<?> DisplayerFactoryRegistration;

    @Override
    public void start(BundleContext bundle_context) throws Exception {
        System.out.println("Starting the Displayer Provider Bundle");
        ServiceFactory<Displayer> displayer_service_factory = new DisplayerServiceFactory();
        Hashtable<String,String> properties = new Hashtable<String,String>();
        properties.put("service.vendor", DisplayerServiceFactory.NAME);
        DisplayerFactoryRegistration = bundle_context.registerService(Displayer.class.getName(), displayer_service_factory,properties);
    }

    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("Stopping the Displayer Provider Bundle");
        DisplayerFactoryRegistration.unregister();
    }
}

and here's part the code of the embedded osgi container that runs the previus bundle

/* Get a displayer instance from the service factory provided by the displayer provider bundle */
        ServiceReference<?>[] service_references = null;
        try {
            service_references = my_bundle_context.getServiceReferences(Displayer.class.getName(),"(service.vendor=displayer_service_factory)");
        } catch (InvalidSyntaxException e) {
            System.out.println("Sintaxis para obtener displayer_service_factory_reference invalida.");
            e.printStackTrace();
        }
        ServiceReference<?> displayer_service_factory_reference = null;
        if (service_references!=null && service_references.length==1) {
             displayer_service_factory_reference = service_references[0];
        } else {
            //Throw new NoDisplayerFactoryException();
        }
        Displayer bundle_displayer_instance = (Displayer) my_bundle_context.getService(displayer_service_factory_reference);

when i run this code, it gives me this exception

Exception in thread "main" java.lang.ClassCastException: com.lincesoft.imperator.displayer.provider.DisplayerImplementation cannot be cast to com.lincesoft.imperator.displayer.api.Displayer
at com.lincesoft.imperator.procurator.launcher.Procurator.main(Procurator.java:94)

why is this happening? it is clear that DisplayerImplementation is an instance of Displayer.

Also i have done some debugging and in the ServiceFactory class (DisplayerImplementation instanceof Displayer) returns true, however, when i get a displayer implementation from the ServiceFactory registered as a service, (DisplayerImplementation instanceof Displayer) returns false.

could this be a bug in the framework implementation i'm using? i'm using felix btw.

if you got here, thank you for reading! and i'll really apreciate it if you try to help me. Have a nice day! or night!


Solution

  • You have a special case here as you want to access an OSGi service from outside the OSGi container. This can only be done with an API package from outside the container as you can not access packages from bundles.

    You already have the com.lincesoft.imperator.displayer.api package in the classpath outside the container. So you simply have to add this package to the framework property org.osgi.framework.system.packages.extra. This way the container publishes the api package inside osgi.

    Like Balazs wrote you should also make sure that you do not have the api package inside one of the bundles you deploy. This makes sure that OSGi does not accidentally choose the wrong api package.