Announcement Announcement Module
Collapse
No announcement yet.
jdbc inbound channel adapter for doing only a single query at runtime Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • jdbc inbound channel adapter for doing only a single query at runtime

    Upon startup, my app needs to quickly query an MS SQL database for records in 2 tables from the previous day. It's a fairly quick operation. I'm trying to figure out how I might do this with Spring Integration. I'm using an int-jdbc:inbound-channel-adapter to query my table and pass the message on to a channel to which I have a service-activator listening on. Whenever I run my app, it throws an exception that a poller has not been defined for my channel adapter. I understand the poller will do recursive queries to the database, but I'm only interested in making one initial query. How can I accomplish that? Should I just use JdbcTemplate directly? Thanks.

    Code:
    <beans:bean id="pfMessageHandler"
    	class="com.mycompany.phones.trace.PFTraceMessageHandler"/>
    
    <int-jdbc:inbound-channel-adapter query="select top 10 * from tblData_OutboundTrace"
    	channel="outboundTraceResults"
    	data-source="pfDataSource"/>
    		
    <channel id="outboundTraceResults"/>
    	
    <service-activator id="outboundTraceProcessor" input-channel="outboundTraceResults"
    	ref="pfMessageHandler" method="processOutboundTraceResults"/>

  • #2
    Hello.
    Yes, in this case you must use JdbcTemplate directly by implementation something like service who reads your DB and sends a message to the channel via gateway.

    But there is some trick. You must define bean of type
    org.springframework.beans.factory.config.MethodInv okingFactoryBean
    who invokes a method of your service who reads DB.
    And when the spring container is starting the method will be invoked once and only once.

    Good luck!
    Artem

    Comment


    • #3
      Actually, poller had two predefined triggers (interval based and Cron). Obviously for your case, non would work which means you have to implement your own.
      The Trigger interface is quite simple as you can see:
      Code:
      public interface Trigger {
      
      	Date nextExecutionTime(TriggerContext triggerContext);
      
      }
      All you need to do in the implementation is something like this:
      Code:
      public class MyTrigger implements Trigger {
      	private volatile boolean initiaized = false;
      	
      	public Date nextExecutionTime(TriggerContext triggerContext) {
      		if (!initiaized){
                              initialized = true;
      			return new Date(System.currentTimeMillis());
      		}
      		return null;
      	}
      }
      As you can see it will return Date only once which means it will only execute once.
      Then you inject the above bean via 'trigger' attribute.

      Comment


      • #4
        Thanks Oleg, I'll give that a try. Seems simple enough.

        Comment


        • #5
          Oleg, I've implemented the Trigger interface like you suggested and injected it into the poller. However when my app runs, it seems to be waiting after it completes the single execution. My MessageHandler method gets called just fine and the output is what I expect, but after the MessageHandler completes, that's when the app seems to be waiting for something. This is my new updated config.

          Code:
          	<int-jdbc:inbound-channel-adapter query="select * from tblData_OutboundTrace where datediff(d, MidnightStartDate, getdate()) = 1 and Extension != '' order by MidnightStartDate asc"
          		channel="outboundTraceResults"
          		data-source="pfDataSource">
          		<poller trigger="runOnceTrigger"/>
          	</int-jdbc:inbound-channel-adapter>
          		
          	<channel id="outboundTraceResults"/>
          	
          	<service-activator id="outboundTraceProcessor" input-channel="outboundTraceResults"
          		ref="pfMessageHandler" method="processOutboundTraceResults"/>
          		
          		
          	<!-- Message Handlers -->
          	
          	<beans:bean id="pfMessageHandler"
          		class="com.myco.phones.trace.PrairieFyreTraceMessageHandler"/>
          		
          	
          	<!-- Triggers -->
          	
          	<beans:bean id="runOnceTrigger" class="com.myco.scheduling.RunOnceTrigger"/>

          Comment


          • #6
            but I'm only interested in making one initial query
            So what you see is that your inbound adapter is triggered once and will never gets triggered again during that instance of the Application Context. Are you expecting something different?

            Comment


            • #7
              That's right, I see that my inbound adapter is triggered only once. After I've processed the Message payload in my MessageHandler, I expect the app to end, not just wait and hang at the console.

              Comment


              • #8
                Ok, the thing is that polling consumer is a Lifecycle object so all you need to do is execute stop() on ApplicationContext.

                Comment


                • #9
                  If the application really is so short-lived, it might make more sense to use JdbcTemplate directly within some code that implements SmartLifecycle or InitializingBean... or implement an ApplicationListener, parameterized with ContextRefreshedEvent.

                  Or... do you intend to add more logic to this application so that it will include other long-running processes?

                  Comment


                  • #10
                    Given that the poller runs in a task scheduler thread rather than the main thread, if I call context.stop() from my main method, then execution of the poller terminates before completion. I hope I'm explaining myself well.

                    My main method is just as simple as this. The inbound channel adapter is called as soon as the app starts.
                    Code:
                    	public static void main(String[] args) {
                    		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml", Client.class);
                    		context.stop();
                    	}

                    Comment


                    • #11
                      Mark - yes I do plan to add more logic to this application. I'm still in the early stages of the app right now which involves fetching some initial data from one datasource to then process it and eventually insert it into a second datasource.

                      Comment


                      • #12
                        If you define a "taskScheduler" bean explicitly, you can set its 'waitForTasksToCompleteOnShutdown' property to TRUE. Then when the context tries to stop the scheduler, it should wait until the currently running task (your poller) completes.

                        Comment


                        • #13
                          Would I then reference that bean in the "task-executor" attribute of my poller?

                          Comment


                          • #14
                            No, that's not necessary, if the bean's name is "taskScheduler" it will automatically replace the default instance.

                            Comment


                            • #15
                              Maybe I'm doing something wrong. I've added the following taskScheduler bean to my config, but the app still seems to terminate before the poller is done.

                              Code:
                              	<beans:bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
                              		<beans:property name="waitForTasksToCompleteOnShutdown" value="true"/>
                              	</beans:bean>

                              Comment

                              Working...
                              X