Hello Martin,

If you are interested, I can describe here DM aspects, which is a feature
that might be interesting in your case.
DM aspects are kind of (non functional) interceptors at the OSGi service
level. DM aspects are real components, they can have a lifecycle,
can be started/stopped at anytime, and they can declare service
dependencies, if they need some.

Basically, you can programatically (or using annotations) define some
aspect services that will be applied to any relevant original services
matching some given service properties. Multiple aspects can be applied to
the same original service and in such case, the aspects are chained using
some ranking order.

Regarding the start ordering, if a client is already bound to a given
original service, then any post-registered aspects will replace the
original service already bound to the client (either the volatile field in
the client pointing to the original service will be updated or a "swap"
callback will be invoked in order to replace the original service with the
new aspects). If you have a start ordering requirement (that is: you need
any client to be injected at the first time with the aspects and not with
the original service), then as Raymond said you will have to arrange to
register the aspects before the original services.

Let's take an example using the dm-lambda library (but the original DM api
or the DM annotations also work):

Assuming you have an original log service with a property "vendor=apache",
and you want to apply a chain of two aspects on that log service (which has
a vendor service property). Let's assume the first aspect is an
"UpperCaseLogService" which rewrites each log message to uppercase, and the
second one: "ErrorLogEventLogService" is an aspect which triggers an OSGi
event (using EventAdmin) in case the log is emitted using an ERROR level.

Then you would declare those two aspects like this:

public class Activator extends DependencyManagerActivator {
    public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
        // Create the first aspect which is applied to any LogService
having a "service=vendor" service property.
        // Notice the "rank(1)" which means this aspect will be the first
in the chain

        aspect(LogService.class, aspect -> aspect
            .impl(UpperCaseLogService.class)
            .filter("(vendor=apache)")
            .rank(1));

        // Create the second aspect which is applied to any LogService
having a "service=vendor" service property
        // Notice that this aspect has a dependency required on an
EventAdmin service
        // Also notice the "rank(2)" which means this aspect will be the
second one in the chain

        aspect(LogService.class, aspect -> aspect
            .impl(ErrorLogEventLogService.class)
            .filter("(vendor=apache)")
            .rank(2)
            .withSvc(EventAdmin.class, true));
   }
}

Now the UpperCaseLogService aspect class, which convert all logs to upper
case:

public class UppercaseLogAspect implements LogService{
    private volatile LogService logService; // injected, possibly the
original LogService, or the next aspect in the chain

    void start() {
        // optional lifecycle start callback: our aspect is starting
    }

    void stop() {
        // optional lifecycle stop callback: our aspect is stopping
    }

    public void log(int level, String message) {
        logService.log(level, message.toUpperCase());
        ...
    }
}

And the ErrorLogEventLogService aspect, which is injected with the original
LogService , as well as with
the EventAdmin service: it will trigger an event in case the log level is
LOG_ERROR:

public class UppercaseLogAspect implements LogService{
    private volatile LogService logService; // injected, possibly the
original LogService, or the next aspect in the chain
    private volatile EventAdmin eventAdmin; // injected, used to trigger an
event when the logged message level is in error

    void start() {
        // optional lifecycle start callback: our aspect is starting
    }

    public void log(int level, String message) {
        if (level == LogService.LOG_ERROR) {
            // fire an event using the EventAdmin service injected in this
class
        }
        logService.log(level, message.toUpperCase());
    }
}

So, now, if a client is already injected with a log service, then it will
be swapped at the time the aspect chain is registered.
The aspect chain will be injected automatically in the volatile field which
points to the original service, or you can define a swap callback.

let's take an example, using a volatile field:

first the client:

class Client {
   volatile LogService logService; // the original LogService, or the
aspect chain

   void doLog() {
         logService.log(...);
   }
}

and the corresponding client activator:

public class ClientActivator extends DependencyManagerActivator {

    public void init(BundleContext ctx, DependencyManager dm) throws
Exception {
        component(Client.class, comp -> comp
            .impl(Client.class.class)
            .withSrv(LogService.class, true));
    }

}

And if you want the client to define a swap callback, you can then define
some the callback like this:

class Client implements Runnable {
   volatile LogService logService; // the original LogService, or the
aspect chain

   // bind the required dependency (possibly the original service, or an
aspect)
   void set(LogService logService) {
         this.logService = logService;
   }

   // called if the original service is swapped with an aspect
   void swap(LogService oldLog, LogService newLog) {
       // handle the swapped service
       this.logService = newLog;
   }

   public void run() {
         logService.log(...);
   }
}

then here is the ClientActivor, defining the swap callback like this:

        component(Client.class, comp -> comp
            .impl(Client.class.class)
            .provides(Runnable.class)
            .withSrv(LogService.class, srv -> srv.required().add(Client::
set).swap(Client::swap)));


here, the initial LogService is first injected using the "set" callback,
and if it's the original service, then the swap callback will be invoked
if (ever) an aspect has to replace the original bound service.

finally, you might also take a look at [1]. I never looked at it, but
aspecio seems to be another solution allowing to manage osgi aspect
services.

Hope this helps;

cheers;
/Pierre

[1] http://www.mail-archive.com/users@felix.apache.org/msg17262.html


On Thu, Oct 6, 2016 at 4:04 PM, Raymond Auge <raymond.a...@liferay.com>
wrote:

> The problem with the above solution is that you will end up in a race where
> a service might already be registered without a proxy, then what? So you
> should try to avoid ordering issues in your design otherwise you will
> regret it later.
>
> What really should happen is that the thing creating the service should add
> the proxy around the service before the service is ever published. An
> extender is an ideal place to do this. Felix DM, and IPojo are ideal for
> this use case since they imperative APIs for managing services and can be
> "trained" to add proxies based on arbitrary heuristics.
>
> It's a wish I have also for Felix SCR.
>
> Sincerely,
> - Ray
>
> On Thu, Oct 6, 2016 at 6:45 AM, Martin Nielsen <mny...@gmail.com> wrote:
>
> > I think what i wrote might have made more sense in my own head:) I
> > apologize for the lousy problem description.
> > Maybe an actual use case would help:
> >
> > I would like to use the OSGi serviceregistry to proxy specific
> interfaces.
> > For example:
> > Say that i attempt to get a serviceReference for an interface with a
> > mybatis annotation on it. What i would like is that the service registry
> > returns a proxy that i define (My idea was through something like a
> > ServiceBindingInterceptor). The proxy should then be closed down again
> when
> > get unget method is called on the service.
> >
> > Basically, i would like to supply a proxy implementation of interfaces in
> > certain situations, but still allow for the serviceregistry lifecycle
> > (get/unget) to handle opening and closing the proxies.
> >
> > It doesn't have to use any specific part of the runtime, i just spotted
> the
> > interceptors and mistook their purpose a bit i think.
> >
> > On Thu, Oct 6, 2016 at 12:32 PM, Clement Escoffier <
> > clement.escoff...@gmail.com> wrote:
> >
> > > Hi,
> > >
> > > The service binding interceptor requires to have a “service” available.
> > > You should be able to do what you want by either:
> > >
> > > - combining a “default-implementation” strategy and a binding
> interceptor
> > > (it would require to have the dependency marked as optional)
> > > - or create your own handler that inject what you want to inject (
> > > http://felix.apache.org/documentation/subprojects/
> > > apache-felix-ipojo/apache-felix-ipojo-devguide/how-to-
> > > write-your-own-handler.html <http://felix.apache.org/
> > > documentation/subprojects/apache-felix-ipojo/apache-
> > > felix-ipojo-devguide/how-to-write-your-own-handler.html>)
> > >
> > > Clement
> > >
> > >
> > > > On 6 oct. 2016, at 04:02, Martin Nielsen <mny...@gmail.com> wrote:
> > > >
> > > > Hello everyone
> > > >
> > > > I am looking at the felix servicebinding interceptors with a certain
> > > amount
> > > > of enthusiasm, but i am having trouble figuring out if they can
> solve a
> > > > specific task.
> > > >
> > > > http://felix.apache.org/documentation/subprojects/
> > > apache-felix-ipojo/apache-felix-ipojo-userguide/ipojo-
> > > advanced-topics/service-binding-interceptors.html
> > > >
> > > > What i would like to do is the following: Whenever a ServiceReference
> > is
> > > > requested for an interface (No matter which one), i want an
> interceptor
> > > to
> > > > examine it. If the interface meets some criteria, the an interceptor
> > > should
> > > > create a proxy for that interface, regardless of a matching
> > > implementation
> > > > being registered.
> > > > So: Even if no object is actually registered as a service to that
> > > > interface, i want the interceptor to return a proxy anyway. Is that
> > > > possible to do in any way?
> > > >
> > > > Thank you in advance.
> > > >
> > > > -Martin
> > >
> > >
> >
>
>
>
> --
> *Raymond Augé* <http://www.liferay.com/web/raymond.auge/profile>
>  (@rotty3000)
> Senior Software Architect *Liferay, Inc.* <http://www.liferay.com>
>  (@Liferay)
> Board Member & EEG Co-Chair, OSGi Alliance <http://osgi.org>
> (@OSGiAlliance)
>

Reply via email to