Announcement Announcement Module
Collapse
No announcement yet.
gateway method with multiple parameters Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • gateway method with multiple parameters

    Hi,

    I was looking through the forums and I was under the impression that it's possible to have a gateway method with multiple parameters as long as you use the payload expression or @Payload.

    I have the following interface:


    Code:
    public interface RequestGateway {
    
    	public String echo(String request);
    
    	public String echo(String request, String anotherRequest);
    }
    and the gateway defined in xml:

    Code:
    <int:gateway id="requestGateway" 
    	         service-interface="com.test.RequestGateway"
    	         default-request-channel="requestChannel">
    	         <int:method name="echo" payload-expression="#args[0] + #args[1]"/>
    	  </int:gateway>
    I have two questions. One is it even possible to have two methods with the same name but a different arguments list?
    Regardless of the answer to the first question (I can always rename one of the methods), should this work? Right now while evaluating the echo method with 2 strings there is an exception:

    HTML Code:
    Found more than one parameter type candidate...
    Thanks for the help
    Netta

  • #2
    What version of Spring Integration are you using? I just ran a test with 2.1.2 (and 2.0.6) and it worked fine for me.

    Regarding having two gateway methods with the same name but different signatures; that won't work; in my test, I got what I would have expected (out of bounds on the second #args reference).

    Here's the config I used that worked just fine...

    Code:
    <int:gateway id="gw"
    		service-interface="org.springframework.integration.samples.tcpclientserver.SimpleGateway"
    		default-request-channel="input">
    	<int:method name="send" payload-expression="#args[0] + #args[1]" />
    </int:gateway>
    
    public interface SimpleGateway {
    
    	public String send(String text, String extra);
    
    }

    Comment


    • #3
      The problem was the other side. How do you get the two parameters back as is? Like if I have a service activator on the other side that I want to look like this:

      Code:
      public String handleRequest(String s, String s2)
      Does that make sense?

      Comment


      • #4
        That's a completely different question; in your original question you concatenated the arguments into one.

        There are a number of ways to do it.

        For example, define a simple bean, and use

        payload-expression="new com.foo.Holder(#args[0], #args[1])" and define your service method to take a holder

        Code:
        public String handleRequest(Holder holder) {...}
        Another technique would be to put the second argument in a header (and the first in the payload) and then annotate your service method:

        Code:
        public String handleRequest(@Payload String s, @Header("foo") String s2)

        Comment


        • #5
          Sorry I'll clarify. What if the server is the one giving the interface, communication between us. I can't just change the payload like in the first example. If I were using httpInvoker as opposed to spring integration I would just use the interface directly (using HttpInvokerProxyFactoryBean). What's the best way to do the same thing with spring integration, taking into consideration that the interface's methods have multiple parameters.

          Comment


          • #6
            The HttpInvoker is an RPC mechanism where the client is tightly coupled to the service.

            Spring Integration facilitates components sending messages to one another without any regard for their respective APIs. In many cases, service APIs take a single object as a parameter; let's say one component creates an order object and sends it to another service that needs a different order object, you simply insert a transformer to transform from one object to the next. These two services know nothing about each other's API.

            For more complex APIs, however, you need an adapter to adapt a message to the required API.

            Spring Integration provides a convenient way to define such an adapter, using just configuration, with a SpEL expression...

            Code:
            <int:service-activator input-channel="inbound"
            	expression="@myService.handleRequest(payload, headers.foo)" />
            or

            Code:
            <int:service-activator input-channel="inbound"
            	expression="@myService.handleRequest(payload.firstArg, payload.secondArg)" />
            where '@myService' refers to a <bean /> declared elsewhere in the application context.

            Comment


            • #7
              I'm confused, is this service activator on the client side or on the server? Can you give me an end to end flow example for this scenario?

              Thanks again.

              Comment


              • #8
                The server.

                ...Like if I have a service activator on the other side ...
                That statement made be believe you wanted to use S.I. on the server

                Why don't you tell us exactly what you are trying to do?

                If you are trying to replace the HttpInvoker with Spring Integration and make no changes on the server then none of this applies.

                Exactly what is your objective?

                Comment


                • #9
                  Sorry for being confusing. To be honest, I'm curious about both of those but let's start with the service activator on the server side. I have a given interface let's say as an example:

                  Code:
                  public interface Foo {
                      Object send(Bar b, Collection<String> s);
                  }
                  Let's say I have some service on the server implementing this interface.
                  How do I configure the gateway on the client to handle the "send request" and how do I configure the server's service activator. When I have one parameter it's easy. I just don't understand how to handle the multiple parameter aspect of this.

                  I hope that's a little more clear. Thanks again for all the help.

                  Netta

                  Comment


                  • #10
                    Again, you are mixing the client and server. The goal is to separate the concerns. Each should be considered in isolation.

                    A client doesn't talk directly to a "service activator" on a server; you have to choose some wire protocol together with adapters or gateways, say json over REST, AMQP, or TCP, or ...

                    Example topologies might be

                    gw->http-outbound-gateway -> http-inbound-gateway->service-activator

                    or

                    gw->amqp-outbound-gateway -> amqp-inbound-gateway->service-activator

                    or

                    gw->object-to-json-transformer->tcp-outbound-gateway -> tcp-inbound-gateway->json-to-object-transformer->service-activator

                    Where

                    1. The gw would be configured as in my earlier example to create an object containing the arguments.
                    2. The http/amqp gateways would be configured to use Object To/From JSON MessageConverters.
                    3. The service activator would use a SpEL expression to parse out the arguments to pass to your service bean as in the example above.


                    The key take-aways are that the client and server are loosely coupled and it doesn't matter what transport or serialization technique you choose to use.


                    Hope that's clear.

                    Comment


                    • #11
                      Right I understand that. I was just referring to the beginning and end of the process. The beginning is the gateway on the client side and the end is the service activator on the server side. Just for clarification, the client and server will use http gateways.

                      So the question again is how do I configure the gateway when I have an interface with a method that has two or more arguments (all of which I want as payloads). If I only had one argument the gateway configuration is easy. That one argument is the payload. It's the multiple argument payload I am having trouble with.

                      Once the gateway is configured (and the http inbound gateway receives the message on the server), how do I configure the service activator that needs to handle a multiple parameter method.

                      Sorry if I'm not explaining myself well. I understand the flow you described above and have successfully implemented it on one (or none) parameter methods. I just need help with the multiple parameter scenario.

                      Thanks again.

                      Comment


                      • #12
                        I understand the flow I was only referring to the endpoints (gateway on the client and service activator on the server). Sorry if that wasn't clear.
                        I've successfully implemented this flow using both http and amqp gateways. The only thing is that the interface I was using for the gateway had a single argument. Can you show me how to configure the gateway to accept a method with multiple arguments (like the Foo interface shown 2 posts above, as well as configuring the service activator to access the two argument payload.

                        I understand the concept, it's just the interface methods with multiple parameters I'm having a problem implementing.
                        So to sum up here is what I need to understand how to do:

                        Code:
                        clent:
                        <int:gateway ...>
                        <int:method name="..." payload-expression="????"/>
                        </gateway>
                        <channel ...>
                        <http:outbound-gateway ...>
                        
                        server:
                        <http:inbound-gateway .../>
                        <channel ..>
                        <service-activator ?????/>
                        The areas with the question marks are where I need the help (for multiple parameter methods where the parameters are all payloads).

                        Thanks again
                        Netta

                        Comment


                        • #13
                          I already explained several ways to do that; let me try one more time...

                          Code:
                          public class TwoStrings {
                          
                          	private String string1;
                          	
                          	private String string2;
                          
                          	public TwoStrings(String string1, String string2) {
                          		this.string1 = string1;
                          		this.string2 = string2;
                          	}
                          
                          	public String getString1() {
                          		return string1;
                          	}
                          
                          	public String getString2() {
                          		return string2;
                          	}
                          }
                          
                          ...
                          
                          <int:method name="request" payload-expression="new com.foo.TwoStrings(#args[0], #args[1])" />
                          
                          ...
                          
                          <int:service-activator ... expression="@service.handleRequest(payload.string1, payload.string2)"
                          or

                          add one of the arguments as a header on the client side, add that header to the mapping on the outbound and inbound gateways and then use

                          Code:
                          <int:service-activator ... expression="@service.handleRequest(payload, headers.fooHeader)"

                          Comment


                          • #14
                            Doesn't that create a coupling between the client and the server? The server now has to know the names of the variables it is interacting with.

                            Comment


                            • #15
                              So, use an ArrayList<String> instead. It just needs a little more work on the client side; to transform the arguments into a List<String>...

                              Code:
                              <int:gateway id="gw"
                              		service-interface="com.foo.SimpleGateway"
                              		default-request-channel="input">
                              	<int:method name="send" payload-expression="new java.util.ArrayList()" >
                              		<int:header name="foo" expression="#args[0]" />
                              		<int:header name="bar" expression="#args[1]" />
                              	</int:method>
                              </int:gateway>
                              
                              <int:transformer input-channel="input" output-channel="next">
                              	<int-groovy:script>
                              		payload.add(headers.foo)
                              		payload.add(headers.bar)
                              		payload
                              	</int-groovy:script>
                              </int:transformer>
                              Then, on the server...

                              Code:
                              <int:service-activator ... expression="@service.handleRequest(payload.get(0), payload.get(1))"

                              Comment

                              Working...
                              X