felix-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Raymond Auge <raymond.a...@liferay.com>
Subject Re: Using service interceptors to inject proxies
Date Thu, 06 Oct 2016 19:31:42 GMT
I think the point of the service registry is not to fool other code into
working when it really shouldn't. How would you ensure that the
implementation of such a proxies would be able to fool the clients?

If you could write a proxy generator that could write any proxy such that
clients simply work I'd like to invest in your tech because I'll be out of
a job ;)

In fact, I couldn't see any other way to implement proxies as you suggest
other than to simply block on any call until a real implementation arrives.
The closest thing I've seen to what you are describing are the blueprint
proxies which provide service damping, and let me tell you, you don't want
to go there. At least I would suggest not going there.

I personally think it's far better to expand your code's awareness of the
dynamics of OSGi services. And there is no, in my humble opinion, more
concise and simple way to achieve this than by using Declarative Services.

But implementing such an engine (the one which builds your proxied
services), if at all, certainly lives in the domain of more powerful
imperative solutions like Felix DM and/or iPojo.

Sincerely,
- Ray


On Thu, Oct 6, 2016 at 3:02 PM, Martin Nielsen <mnybon@gmail.com> wrote:

> Hello Pierre and Reymond
>
> First of all, thank you very much for taking the time to provide detailed
> and patient answers.
>
> My usecase was to somehow "Cheat" that service registry, so that a bundle
> getting a service reference (For example via
> BundleContext.getServiceReferences) would not actually be supplied by a
> service from the service registry, but rather a proxy i made, which fronts
> the interface in question.
> So:
> Bundle A calls
> BundleContext.getServiceReference(MyAnnotatedInterface.class) <Note that
> nothing is registered on this interface at the time of the call>
> Some interceptor logic (The component i am trying to make) registers that
> the interface is called and, instead of looking in the serviceregistry,
> creates a proxy which it returns to BundleA
> Bundle A happily uses the proxy it got back, completely oblivious to the
> fact that it never got to the service registry.
> Bundle A calls
> BundleContext.ungetService(myAnnotatedInterfaceServiceReference), at which
> point the proxy is told to close and dispose.
>
> So the point would be that the client bundle is completely oblivious to the
> "hack", and that the solution works without first having to register any
> service on the MyAnnotatedInterface interface.
>
> From the answers i have seen here, it looks like that usage is not
> possible, as any calls to getServiceReference requires something already be
> registered on that interface. Correct?
>
> In that case, it would seem that i would have to actually register
> MyAnnotatedInterface as a service, and then hot-swap it from there. DM
> aspects seems like a good place to start, but i can't quite figure out if
> you can use it without doing something more in the client than just
> registering the service?
>
> Again, thanks for your help, i you will suffer my ignorance for a wee bit
> longer :)
>
> -Martin
>
> On Thu, Oct 6, 2016 at 5:29 PM, Pierre De Rop <pierre.derop@gmail.com>
> wrote:
>
> > 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.auge@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 <mnybon@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.escoffier@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 <mnybon@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)
> > >
> >
>



-- 
*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)

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message