spring-attic / spring-integration-dsl-groovy

Groovy DSL for Spring Integration
Apache License 2.0
56 stars 40 forks source link

Execute http module DSL from Java Class #5

Open sagarrao opened 10 years ago

sagarrao commented 10 years ago

Hi,

We are looking to add spring-integration functionality to an existing spring-boot app. We have a java config class which basically initializes the spring-boot parameters. What we are looking for is to use an http protocol adapter, define the flows in a separate groovy class in an outside package and somehow initiate the flows from within the java config.

Normally, creating an IntegrationBuilder object in the main() class and using it to create a MessageFlow object pointing to the groovy class should have done the trick(as the docs suggest),but to get the http module, the suggested way as per the docs is :

IntegrationBuilder builder = new IntegrationBuilder("http");
        MessageFlow flow = (MessageFlow) builder.build(new File("src/main/groovy/<package>/EventsFlow.groovy"));

But, doing this and running it using gradle run throws the following excpetion:

Exception in thread "main" java.lang.ClassCastException: org.springframework.integration.dsl.groovy.IntegrationContext cannot be cast to org.springframework.integration.dsl.groovy.MessageFlow

If I remove the http string from the IntegrationBuilder constructor then this excpetion is not thrown but how would I be able to register for the http flow?

Please help..

artembilan commented 10 years ago

Show your EventsFlow.groovy, please. Looks like it returns IntegrationContext, but not MessageFlow

artembilan commented 10 years ago

I guess: you have something like doWithSpringIntegration { and exactly in this case IntegrationBuilder returns IntegrationContext. See https://github.com/spring-projects/spring-integration-dsl-groovy/blob/master/spring-integration-dsl-groovy-core/src/test/groovy/org/springframework/integration/dsl/groovy/builder/IntegrationBuilderTests.java

sagarrao commented 10 years ago

This is how the EventsFlow.groovy looks like(BTW, you are right :-)

doWithSpringIntegration {
    namespaces('int-http')
    springXml {
        'int-http:inbound-gateway'(id:'events-inbound-gateway',
        'request-channel':'events-request-inbound-channel',
        'reply-channel':'events-response-inbound-channel',
        'path':'/examples/events'
        )

        'si:channel'(id:'events-request-inbound-channel')
        'si:channel'(id:'events-response-inbound-channel')
        'si:channel'(id:'events-request-outbound-channel')
        'si:channel'(id:'events-response-outbound-channel')
    }

    flow = messageFlow{
        httpGet(url:{ "http:localhost:8080/examples/name" },requestChannel:'events-request-outbound-channel',replyChannel:'events-response-outbound-channel')
    }
}
sagarrao commented 10 years ago

I want to use http-inbound-gateway and a few channels. Hence I decided to place them within a doWithSpringIntegration block. What is it that I should use here instead of the block? As, for some items like http-outbound gateway or tranformer,poller etc, I found that there are direct methods like httpGet or poll but for the above mentioned items, I couldn't find one. Am I actually missing something here?

artembilan commented 10 years ago
  1. To fix your ClassCust:
IntegrationContext ic = (IntegrationContext) builder.build(new File("src/main/groovy/<package>/EventsFlow.groovy"));
MessageFlow flow = ic.getMessageFlows().get(0);
  1. Your script show both HTTP endpoint: HTTP-inbound (server), HTTP-outbound (httpGet - client). Is it intentional ?
  2. If your httpGet is really client it has to have an url http:localhost:8080/examples/events
  3. Actually it's not clear what really you want to do with your flow.
sagarrao commented 10 years ago

I was able to fix the ClassCust thing and was about to post the same. Thanks anyways..

Let me try and explain things a bit more clearly now. Our intent is to expose URLs to users, which I tried configuring through the http-inbound-gateway.

This gateway, would pass the request to an http-outbound-gateway. Now, what happens here is that the outbound endpoint passes the request to another URL which germinates from the original spring-boot app(as mentioned in the OP) . These have custom logics which further hit our EJB services down the line to fetch data and pass it back to the user via the 2 http endpoints. So, the localhost URL that you see is actually exposed via the spring-boot app. Lemme know if you need more clarity on this...

artembilan commented 10 years ago

OK, got it. Thanks We call it HTTP Proxy, when inbound endpoint shift the body to the HTTP outbound. But then your endpoints should have the same channels - events-request-inbound-channel. Or are you going to have some middle flow ? Now evrything looks good and actually there is no reason to get anything from builder.build(). HTTP inbound is active endpoint and it waits the message from HttpRequest. So, start your app and just send a request to the URL under path /examples/events

sagarrao commented 10 years ago

Well actually, yes once we get the data from the outbound endpoint, we are supposed to add a transformer logic , where language translation is supposed to happen.

As far as I understand, the replyChannel for the inbound endpoint and requestChannel for the http outbound should be the same isn't it? I might have made a mistake in the flows groovy file that I attached..

Also, when I hit the /examples/events after starting the app, the url doesn't seem to load. This could may also be because of the custom logic present in the original spring-boot app, but just as a precautionary measure, is there something that I should be mindful of?

artembilan commented 10 years ago
  1. No, request-channel of inbound endpoint is request-channel of outbound endpoint.
  2. reply-channel of inbound - where to wait a response. reply-channel of outbound - where to send a response.
  3. Any http-inbound endpoint should be exposed as Spring MVC handler within servlet container. So, without Web abilities in your application it won't work.
sagarrao commented 10 years ago

Thanks again for the info... I would try this tomorrow(it's midnight here in India..) and update the ticket tomorrow.

One last thing that I wanted to know was that we would have multiple flows and in separate groovy file. Right now all examples I saw had only one File object created. Is there a way of configuring multiple files at one go from the main Java config class?

Nonetheless, thanks for all the help.

artembilan commented 10 years ago

@dturanski, the last question is more up to you. Sorry, I'm not so well with Groovy DSL yet

sagarrao commented 10 years ago

Ok, so one quick update here. I added

bean(id:'uriPathHandlerMapping','class':'org.springframework.integration.http.inbound.UriPathHandlerMapping') to the springXml block. I read in the manuals that this helps map the URL to the inbound-gateway. During startup, I get the mesaage

Mapped URL path [/examples/eventsFlow] onto handler 'events-inbound-gateway' in the console.

Yet, the when I hit the URL, it throws a 404...

artembilan commented 10 years ago

Seems to me your main aplicationContext for ServletContext doesn't see that IntegrationContext from groovy script. @dturanski ?

sagarrao commented 10 years ago

Is it something that I need to add explicitly? Right now, the main method consists of the following:

SpringApplication app = new SpringApplication(Application.class);
        IntegrationBuilder builder = new IntegrationBuilder("http");
        IntegrationContext ic = (IntegrationContext) builder.build(new File("src/main/groovy/<package>/EventsFlow.groovy"));
        MessageFlow flow = ic.getMessageFlows().get(0);
        app.setShowBanner(true);
        app.setLogStartupInfo(true);
        app.run(args);

While the EventsFlow.groovy stays as is from what we discussed before.... Is there something more that I need to add in terms of config?

sagarrao commented 10 years ago

One more thing, I was going through the docs and found that we need to add a block in the web.xml. This should have the defining the .. In case of DSL, we should be doing this programatically. I couldn't find any good examples for the inbound gateway, so if you could provide this, it would be very helpful.

dturanski commented 10 years ago

IntegrationBuilder with multiple resources is not currently supported. If the main application context needs to see the IntegrationContext, you can set the IntegrationContext as a parent of the main context. Using the boot API, this kind of thing can be done by registering an ApplicationListener which will give you a handle to the main context after it is created but before it is refreshed.

sagarrao commented 10 years ago

Hi,

I made my Java config class to implement ApplicationListener and the event that I made to listen to ContextRefreshedEvent event. In the overloaded method onApplicationEvent, I was able to get the handle to the ApplicationContext , which I made as the parent for IntegrationContext. I understand that you had suggested to do this the other way round but I couldn't get a way to do it.

Here is the relevant bit of code:

@Override public void onApplicationEvent(ContextRefreshedEvent event) { // TODO Auto-generated method stub ArrayList modules = new ArrayList(); modules.add("http"); IntegrationBuilder builder = new IntegrationBuilder(modules, event.getApplicationContext()); builder.setAutoCreateApplicationContext(false); IntegrationContext ic = (IntegrationContext) builder.build(new File("src/main/groovy/package/EventsFlow.groovy")); MessageFlow flow = ic.getMessageFlows().get(0); }

However, the url when accessed still throws a 404...