list+org.o...@io7m.com a écrit le 07/08/2016 14:01 :
> Hello.

Hi,

> As a learning exercise, I've put together a very contrived example of a
> "reverse" service (like an echo service, except that each returned line
> comes back reversed). Mostly, I'm using it as a learning example of how
> to build a small TCP/IP client/server in OSGi.

You hit a limitation in DS, and a very common one because often
components might need to run blocking code (e.g some SQL queries to make
sure a database is ready/with the right model) and make sure the
services it provides are not exposed before that.

It is indeed part of the spec that a component should avoid blocking in
activate/deactivate callbacks: a DS implementation might actually skip
and/or blacklist a component that takes too much time to activate or
deactivate.

Felix SCR does it, and we have run into this before.

A bad workaround for us was to increase the time limit in SCR, which can
be set using a framework property.

A better workaround is not to block at all, but avoid using DS for these
blocking calls. You can for instance define a DatabaseReady or
TCPServerReady service and have it as a reference in your DS component.
You then go on to programatically register that service.

Your problem with port reuse would remain, because activate() can be
called before an async task called by deactivate() has finished.
A solution would be to make sure the same thread is reused for all
TCPServer binding and closing.

You then have a Setup DS component (it could be in the activator as
well, but would make it difficult to depend on other services):

@Component
public final class TCPSetup {

   private BundleContext context;
   private ServiceRegistration reg;
   private TCPServer tcpServer;

   @Reference
   private TCPServerManager srvManager;


   @Activate
   public void activate(BundleContext context) {
      this.context = context;
      Promise<TCPServer> tcpServerPms = srvManager.newServer(port);
      tcpServerPms.onResolve(() -> register(tcpServerPms.getValue()));
   }

   @Deactivate
   public void deactivate() {
        if (tcpServer != null) {
                Promise<Void> tcpServerClosed = srvManager
                                                    .close(tcpServer);
                tcpServerClosed.onResolve(() -> reg.unregister());
        }
        tcpServer = null;
   }

   public void register(TCPServer tcpServer) {

      this.tcpServer = tcpServer;

      // blocking code.
      reg = context.registerService(TCPServer.class, tcpServer, null);

      // this thread will potentially activate components depending
      // on the service.
   }

}

TCPServerManager holds a single thread executor that is used for
creating TCPServer instances.

This way, whatever happens with the lifecycle of your components, you
are sure the bind/close method runs sequentially. There might still be a
problem with the sequence close / restart manager component / bind, but
you get the kind of trickery required to deal with these issues...

Ideally, TCPServerManager's thread dealing with binding/closing sockets
should not be held statically, but it's a bit tricky to deal with the
restarting of TCPServerManager instances otherwise.

You'll notice I use the OSGi Promise API rather than callbacks in my
example. It's similar, but I suggest you explore it (if you haven't done
so yet) in your experiments because it gets very handy when dealing with
state, IO and blocking code.

Things would be much better if DS provided adequate support, for
instance for controlling whether we expose or not services (using the
component context). Other OSGi component frameworks provide this, so I
hope this will eventually get included in DS 1.4.

Hope this helps,
Simon

_______________________________________________
OSGi Developer Mail List
osgi-dev@mail.osgi.org
https://mail.osgi.org/mailman/listinfo/osgi-dev

Reply via email to