views:

444

answers:

2

I'd like to configure a process that looks something like:

Method Call -> Dynamic Proxy Gateway -> Channel -> Service Activator -> Method Call
             ^---------- Transformer <- Channel <-             [return value]

Effectively, I'd like to somehow access the hidden channel Spring Integration creates and transform the return message payload into a different message type.

A simple solution may at first appear to be configuring a default-reply-channel on the gateway, the issue is that I'm sharing the channel between bundles using OSGi. The Service Activator is provided by Bundle "B", and offers a shared channel for incoming requests (it acts as a data provider service). Bundle "A" requires some data, so it requests it, but needs the result in an alternate format. Note that if Bundle "B" were to be able to use the default-reply-channel specified by Bundle "A" then Bundle "A" must share it. That's all fair and well, but then I have a circular dependency in OSGi, and nothing would start.

It seems like another solution here would be to define an output-channel on the Service Activator, but this suffers from a slightly different problem. Assuming I share the output-channel from Bundle "B" I've mitigated the circular dependency issue, but now anytime someone requests something from Bundle "A" the reply goes to everyone attached to the output channel -- this, too, is undesirable.

[Edit: What I mean here is that if "B" shares both an input and an output channel for its service activator then anyone bound to "B"'s output channel will receive the result of anyone's request to "B"'s input channel -- and the desired behavior is that the replies are directed at the requestors.

I should note that the transformer here makes sense only in the context of Bundle A. Bundle B provides a service (for all intents and purposes one that I have no control over). The transformation, specific to the needs of Bundle A should reside in Bundle A.]

So, what I think I really need is to be able to configure a transformer on replies to a dynamic proxy gateway, but try as I might I can't find such a device in the Spring Integration manual. As always, help would be greatly appreciated.

--

Edit 2: I attempted two other tactics here:

  1. Use a service-activator that refers to the OSGi shared channel from Bundle B

    The result was that the returned element was a GenericMessageType -- which it could be possible to transform. The GenericMessageType is really the boolean result of the "send" method which the service-activator must point to, not the reply message. So this method does not work.

  2. Use a header-enricher to set REPLY_CHANNEL and pass the reply channel as a reference rather than a value.

    This technique did not work, the REPLY_CHANNEL header element appears to be ignored when the default-reply-channel of the gateway is set (and the default-reply-channel must be set).

A: 

I'm a bit confused by your problem description. I understand the circular dependency aspect, and the transformer aspect, but I'm not so sure about "the reply goes to everyone attached to A".

It kind of sounds like you might want to have two service activators for B. Your existing one in B would stay, and most clients would use it. The other one would go in A, and would only use channels defined in A. This would prevent requests from A to B from resulting in responses being received by components outside A.

This should make the problem of transformation easier. Transformers take a message from one channel, transform it, and place it on another. Just add one in A and you should be good.

So in A you would have these components, only useable by A:

  • a gateway
  • an input channel
  • a service activator for B
  • an output channel
  • a transformer
  • a transformed output channel

In B you would have, useable by anyone:

  • an input channel
  • a service activator for B
  • an output channel

A depends on B, but B does not depend on A.

Will that work?

SingleShot
It sounds like that might solve the problem, but not in a meaningful way (sorry, I understand that my description is somewhat contrived). Ideally, B shouldn't need to know that A needs a transformer, only A should know that such a transformer is required.I'll clarify about the reply going to everyone attached to A in the main post.
Mark E
I know my description is not very clear, but the point of it is that B knows nothing about A's needs. It does not have knowledge of a transformer. Only A does.
SingleShot
Wouldn't B then need to share the service bean through OSGi? How else would A be able to create a service activator for B?
Mark E
Perhaps I am missing the point (not being an OSGi wiz), but I thought you didn't want B depending on A, which in my suggestion it does not. However, A does depend on B. There has to be a dependency somewhere.
SingleShot
The point of using Spring and OSGi is removing significant dependencies and piping data between services. Here, adding a dependency on the service itself requires that both services be running not only on the same computer but on the same JVM. You have given me an idea though, in curious now if a service activator can refer to a channel...
Mark E
Let us know how it goes ;-)
SingleShot
No success -- I tried to set the REPLY_CHANNEL as a reference too. I'm thinking if I could find the right element for a <chain> that this could work, but there's no way to put a channel in the chain sequence (I suppose that wouldn't make sense anyway).
Mark E
At the end of the day I read through your post again, I think it's almost right, but have posted my own answer for clarity. Thanks for your help.
Mark E
+1  A: 

In theory the real answer here is to use a chain.

The configuration for Bundle A will look something like

<si:gateway id="gw" default-request-channel="xyz" />
<si:channel id="xyz" />
<si:chain input-channel="xyz" />
    <si:service-activator />
    <si:transformer />
</si:chain>

Note for Bundle B the configuration is unchanged and only a single channel is shared through OSGi for access by Bundle A or any tertiary bundles.

Two options for the service-activator are available:

  1. Shared service through OSGi
  2. Custom service that simply invokes a proxy-gateway for the pre-transformation returned data type.

The proxy-gateway in Bundle A will inject into some input-channel "xyz" and ultimately the implied return channel will contained the transformed content as desired.

This solution is nearly the same as the one proposed by SingleShot, however here we prevent the sharing of a real service through OSGi, maintaining bundle boundaries.

Mark E
Thanks for posting your solution.
SingleShot