Announcement Announcement Module
Collapse
No announcement yet.
How do I do this with annotations? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How do I do this with annotations?

    I'm trying to do the equivalent to the following using annotations:
    <service-activator input-channel="exampleChannel" output-channel="replyChannel" ref="somePojo" method="someMethod"/>

    I can't use the Handler annotation in conjunction with MessageEndpoint because I'm going to be connecting several channels to the one POJO, and I can't use the Subscribe or Publish methods because I want it to send a reply. Is it even possible to do this with the annotations?

  • #2
    This sounds like the classic limitation of annotations - namely that the metadata is provided at class-level, whereas XML enables instance-level configuration. A compromise would be to use @Handler for the method but don't use @MessageEndpoint. Then, you can define multiple <service-activator/> elements in XML each with its own input and output channel. It's a rather small thing I guess, but at least the "method" attribute would not be required each time since the @Handler annotation is used instead. For example:
    Code:
    <service-activator ref="somePojo" input-channel="in1" output-channel="out1"/> 
    
    <service-activator ref="somePojo" input-channel="in2" output-channel="out2"/>
    Just out of curiosity, is there different behavior associated with the different channels in some other way? I'm just wondering if there is a use-case here that we should consider more closely.

    Regards,
    Mark

    Comment


    • #3
      Actually it's even more complex than that. What I would really like to be able to do is have one POJO that uses annotations on its methods, as well as the type of the method arguments to route messages. The POJO I made is acting as a bridge right now between a remote service using Springs HttpInvoker and my client. Messages come in on various channels and with various POJOs as the payloads, and I have several methods that accept the POJOs pull some values out of them, do one or more RMI calls, and then return the results, which are then picked up in other methods subscribed to those channels.

      Unfortunately it seems the architecture of Spring Integration really isn't designed around this sort of usage, at least not with annotations. If I only wanted to start the RMI, and not return the results, Subscribe would work perfectly. Likewise for the response, Publish would work. But since I want to do both it's no good. I also can't use multiple payloads on the same channel with the routing determined by the method argument like I would like. I can sort of simulate it by putting a router in in front of the POJO that then redirects to individual channels based on the content type, but I still need to dedicate a channel for each method on my POJO. I just tried using selectors to do this as well, but I don't think I'm declaring them right in spring as they don't seem to be blocking inappropriate messages (I get NoSuchMethodException complaining about the method but with an argument of type org.springframework.integration.message.GenericMes sage).

      Comment


      • #4
        If I get this straight you want two things:
        Code:
        @Publisher(channel="output")@Subscriber(channel="input") public Output rmiCallingMethod(Input i){..}
        And you want automatic routing on payload of the input channel (which will contain messages for numerous rmiCallingMethods.

        I really don't see why you wouldn't want to route the messages based on type, for the second requirement. The double annotation (@Publisher @Subscriber) should just work (I haven't tried this ).

        Comment


        • #5
          Well, two things. First of all the javadocs for @Subscriber would seem to indicate that putting both Publisher and Subscriber on the same method wouldn't work as Subscriber says that the annotated method "must return void".

          Despite that, the solution you proposed was exactly what I tried at first, and it works fine with 1 method. It even works fine if you have more methods so long as each method has their own input channel. The problem is, the type based routing seems to happen at the object level near as I can tell so as soon as you have two methods subscribed to the same channel with different payload types you get an exception and the whole thing blows up.

          Specifically if you have something like
          Code:
          @Subscriber(channel="input")
          @Publisher(channel="output1")
          public POJO doSomething(MyObject1 arg) {
              return new POJO();
          }
          
          @Subscriber(channel="input")
          @Publisher(channel="output2")
          public AnotherPOJO doSomethingElse(MySecondObject arg) {
              return new AnotherPOJO();
          }
          You'll get an exception as soon as you put a new message into the "input" channel. Specifically, lets say you put a MyObject1 payload into the "input" channel, the exception you get will look something like this
          Code:
          24 Jul 2008 09:03:54,397 [message-bus-1] WARN - MessagePublishingErrorHandler.handle(57) | failure occurred in messaging task with message: [ID=504a9062-b1ca-4b46-832d-b8c932e14a32][Header=[CorrelationID=null][Properties={}][Attributes={}][Timestamp=Thu Jul 24 09:03:53 EDT 2008][Expiration=null][Priority=NORMAL][Sequence #1 (of 1)]][Payload='MyObject1@1a93f38']
          org.springframework.integration.message.MessageHandlingException: Failed to invoke handler method 'doSomethingElse' with arguments: {MyObject1@1a93f38}
          	at org.springframework.integration.handler.AbstractMessageHandlerAdapter.handle(AbstractMessageHandlerAdapter.java:120)
          	at org.springframework.integration.endpoint.HandlerEndpoint$HandlerInvokingTarget.send(HandlerEndpoint.java:136)
          	at org.springframework.integration.endpoint.TargetEndpoint.handleMessage(TargetEndpoint.java:103)
          	at org.springframework.integration.endpoint.TargetEndpoint.poll(TargetEndpoint.java:111)
          	at org.springframework.integration.endpoint.EndpointPoller.visitEndpoint(EndpointPoller.java:25)
          	at org.springframework.integration.endpoint.AbstractEndpoint.doSend(AbstractEndpoint.java:252)
          	at org.springframework.integration.endpoint.AbstractEndpoint.send(AbstractEndpoint.java:225)
          	at org.springframework.integration.endpoint.AbstractEndpoint.send(AbstractEndpoint.java:213)
          	at org.springframework.integration.dispatcher.AbstractDispatcher.sendMessageToTarget(AbstractDispatcher.java:74)
          	at org.springframework.integration.dispatcher.BroadcastingDispatcher.send(BroadcastingDispatcher.java:44)
          	at org.springframework.integration.dispatcher.PollingDispatcher.dispatch(PollingDispatcher.java:116)
          	at org.springframework.integration.dispatcher.PollingDispatcher.run(PollingDispatcher.java:102)
          	at org.springframework.integration.scheduling.SimpleTaskScheduler$TaskRunner.run(SimpleTaskScheduler.java:206)
          	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
          	at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
          	at java.util.concurrent.FutureTask.run(FutureTask.java:138)
          	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:98)
          	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:207)
          	at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:885)
          	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
          	at java.lang.Thread.run(Thread.java:619)
          Caused by: java.lang.NoSuchMethodException: $Proxy15.doSomethingElse(org.springframework.integration.message.GenericMessage)
          	at java.lang.Class.getMethod(Class.java:1605)
          	at org.springframework.util.MethodInvoker.prepare(MethodInvoker.java:180)
          	at org.springframework.integration.util.NameResolvingMethodInvoker.invokeMethod(NameResolvingMethodInvoker.java:53)
          
          	at org.springframework.integration.util.AbstractMethodInvokingAdapter.invokeMethod(AbstractMethodInvokingAdapter.java:130)
          	at org.springframework.integration.handler.AbstractMessageHandlerAdapter.handle(AbstractMessageHandlerAdapter.java:107)
          	... 20 more
          Near as I can tell it looks like using @Subscriber with payload type based routing puts a selector at the object level and not the method level, and then routes any messages on that channel to all the methods on the object subscribed to it, regardless of what the object type of the argument is. When it can't find a handler of the proper type on one of the methods it falls back to checking for a overloaded method that takes a type of GenericMessage, and when it can't find one it blows up.

          Edit: FYI, this was done on M5
          Last edited by kmurphy; Jul 24th, 2008, 10:19 AM. Reason: Fixed a mistake, thought of something

          Comment


          • #6
            Thanks for trying it. I think that the javadoc for subscriber could change. There is no reason the aspect can't be applied to non void returning methods.

            From a first glance the problem with having multiple methods stems from the fact that it was assumed that you would create separate classes for separate subscribers. Mark can better answer if there is a fundamental reason for this or if it could change.

            Just out of interest, can you explain why you wouldn't want to create separate objects for this? It seems to me that there are multiple responsibilities here.

            Comment


            • #7
              Well, I've got an object that I want to function as a bridge between the local instance of Spring Integration, and several existing HttpInvoker instances providing RMI services on a server. The object really is more of a thin adapter than it is anything else, and it's primary responsibility is to listen on a few channels for some specific message payloads which essentially correspond to one or more RMI requests. I could implement MessageTarget and manually analyze the Message objects, essentially performing the routing by hand, but I was hoping to avoid coupling my object that closely to Spring Integration (not to mention having to write code that essentially performs payload routing, which is already part of Spring Integration). I also don't want to break it into one object per subscriber because that would mean I'd have about 12 objects that are all nearly identical and each have only one method, which is just rather silly.

              Comment


              • #8
                It sounds like you have a reasonable requirement and Spring Integration being unreasonable about it. Can you create a Jira issue for this?

                Just one more question though, the workaround of using the namespace, is that reasonable enough for you?
                Last edited by iwein; Jul 26th, 2008, 02:35 AM.

                Comment


                • #9
                  Yes, doing the config through xml is an acceptable workaround and what I'm doing currently.

                  I created the JIRA issue (INT-320).

                  Comment

                  Working...
                  X