Merge pull request #45 from ghillert/INTSAMPLES-64

INTSAMPLES-64 Poller Sample using custom Trigger
This commit is contained in:
Gunnar Hillert
2012-02-16 08:31:26 -08:00
7 changed files with 418 additions and 0 deletions

View File

@@ -47,6 +47,7 @@ This is a good place to get started. The samples here are technically motivated
This category targets developers who are already more familiar with the Spring Integration framework (past getting started), but need some more guidance while resolving more advanced technical problems that you have to deal with when switching to a Messaging architecture. For example, if you are looking for an answer on how to handle errors in various scenarios, or how to properly configure an **Aggregator** for the situations where some messages might not ever arrive for aggregation, or any other issue that goes beyond a basic understanding and configuration of a particular component to address "what else you can do?" types of problems, this would be the right place to find relevant examples.
* **dynamic-poller** - Example shows usage of a **Poller** with a custom **Trigger** to change polling periods at runtime
* **async-gateway** - Example shows usage of an **Asynchronous Gateway**
* **errorhandling** - Demonstrates basic **Error Handling** capabilities of Spring Integration
* **file-processing** - Sample demonstrates how to wire a message flow to process files either sequentially (maintain the order) or concurrently (no order).

View File

@@ -0,0 +1,40 @@
Dynamic Poller Sample
=====================
By default this application will (poll) print out the current system time every 5 seconds. From the command line you can enter a non-negative numeric value to change the polling period (in milliseconds) at runtime.
Under the cover an **Inbound Channel Adapter** polls for the current system time. The **Poller**, which is used by the **Inbound Channel Adapter**, is configured with a custom Trigger (*org.springframework.integration.samples.poller.DynamicPeriodicTrigger*). The resulting message contains as payload the time in milliseconds and the message is sent to a **Logging Channel Adapter**, which will print the time to the command prompt.
When changing the polling period, the change to the trigger will occur after the NEXT poll at the current rate. Therefore, if the current polling period is 60 seconds and you change it to 1 second, it can take up to 60 seconds to take effect, depending on when in the polling cycle you make the change.
### Running the Application
You can run the application by either:
* running the "Main" class from within STS (Right-click on Main class --> Run As --> Java Application)
* or from the command line using the [Exec Maven Plugin](http://mojo.codehaus.org/exec-maven-plugin/):
- mvn package exec:java
You should see output like the following:
INFO : org.springframework.integration.samples.poller.Main -
==========================================================
Welcome to the Spring Integration Dynamic Poller Sample!
For more information please visit:
http://www.springsource.org/spring-integration
==========================================================
INFO : org.springframework.integration.samples.poller.Main -
=========================================================
Please press 'q + Enter' to quit the application.
=========================================================
Please enter a non-negative numeric value and press <enter>:
INFO : org.springframework.integration.samples.poller - 2012-02-15 12:14:37.503
INFO : org.springframework.integration.samples.poller - 2012-02-15 12:14:42.504
INFO : org.springframework.integration.samples.poller - 2012-02-15 12:14:47.505

View File

@@ -0,0 +1,70 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.integration.samples</groupId>
<artifactId>dynamic-poller</artifactId>
<version>2.1.0.BUILD-SNAPSHOT</version>
<packaging>jar</packaging>
<name>dynamic-poller</name>
<url>http://www.springsource.org/spring-integration</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.integration.version>2.1.0.RELEASE</spring.integration.version>
<log4j.version>1.2.16</log4j.version>
<junit.version>4.10</junit.version>
<java.main.class>org.springframework.integration.samples.poller.Main</java.main.class>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
<version>${spring.integration.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- test-scoped dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<mainClass>${java.main.class}</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>repo.springsource.org.milestone</id>
<name>Spring Framework Maven Milestone Repository</name>
<url>https://repo.springsource.org/milestone</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.poller;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.support.PeriodicTrigger;
import org.springframework.util.Assert;
/**
* This is a dynamically changeable {@link Trigger}. It is based on the
* {@link PeriodicTrigger} implementations. However, the fields of this dynamic
* trigger are not final and the properties can be inspected and set via
* explicit getters and setters.
*
* @author Gunnar Hillert
*
*/
public class DynamicPeriodicTrigger implements Trigger {
private volatile long period;
private volatile TimeUnit timeUnit;
private volatile long initialDelay = 0;
private volatile boolean fixedRate = false;
/**
* Create a trigger with the given period in milliseconds. The underlying
* {@link TimeUnit} will be initialized to TimeUnit.MILLISECONDS.
*
* @param period Must not be negative
*/
public DynamicPeriodicTrigger(long period) {
this(period, TimeUnit.MILLISECONDS);
}
/**
* Create a trigger with the given period and time unit. The time unit will
* apply not only to the period but also to any 'initialDelay' value, if
* configured on this Trigger later via {@link #setInitialDelay(long)}.
*
* @param period Must not be negative
* @param timeUnit Must not be null
*
*/
public DynamicPeriodicTrigger(long period, TimeUnit timeUnit) {
Assert.isTrue(period >= 0, "period must not be negative");
Assert.notNull(timeUnit, "timeUnit must not be null");
this.timeUnit = timeUnit;
this.period = this.timeUnit.toMillis(period);
}
/**
* Specify the delay for the initial execution. It will be evaluated in
* terms of this trigger's {@link TimeUnit}. If no time unit was explicitly
* provided upon instantiation, the default is milliseconds.
*/
public void setInitialDelay(long initialDelay) {
Assert.isTrue(initialDelay >= 0, "initialDelay must not be negative");
this.initialDelay = this.timeUnit.toMillis(initialDelay);
}
/**
* Specify whether the periodic interval should be measured between the
* scheduled start times rather than between actual completion times.
* The latter, "fixed delay" behavior, is the default.
*/
public void setFixedRate(boolean fixedRate) {
this.fixedRate = fixedRate;
}
/**
* Returns the time after which a task should run again.
*/
public Date nextExecutionTime(TriggerContext triggerContext) {
if (triggerContext.lastScheduledExecutionTime() == null) {
return new Date(System.currentTimeMillis() + this.initialDelay);
}
else if (this.fixedRate) {
return new Date(triggerContext.lastScheduledExecutionTime().getTime() + this.period);
}
return new Date(triggerContext.lastCompletionTime().getTime() + this.period);
}
public long getPeriod() {
return period;
}
/**
* Specify the period of the trigger. It will be evaluated in
* terms of this trigger's {@link TimeUnit}. If no time unit was explicitly
* provided upon instantiation, the default is milliseconds.
*
* @param period Must not be negative
*/
public void setPeriod(long period) {
Assert.isTrue(period >= 0, "period must not be negative");
this.period = this.timeUnit.toMillis(period);
}
public TimeUnit getTimeUnit() {
return timeUnit;
}
public void setTimeUnit(TimeUnit timeUnit) {
Assert.notNull(timeUnit, "timeUnit must not be null");
this.timeUnit = timeUnit;
}
public long getInitialDelay() {
return initialDelay;
}
public boolean isFixedRate() {
return fixedRate;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DynamicPeriodicTrigger)) {
return false;
}
DynamicPeriodicTrigger other = (DynamicPeriodicTrigger) obj;
return this.fixedRate == other.fixedRate
&& this.initialDelay == other.initialDelay
&& this.period == other.period;
}
@Override
public int hashCode() {
return (this.fixedRate ? 14 : 41) +
(int) (38 * this.period) +
(int) (43 * this.initialDelay);
}
}

View File

@@ -0,0 +1,99 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.samples.poller;
import java.util.Scanner;
import org.apache.log4j.Logger;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Starts the Spring Context and will initialize the Spring Integration routes.
*
* @author Gunnar Hillert
* @version 1.0
*
*/
public final class Main {
private static final Logger LOGGER = Logger.getLogger(Main.class);
private Main() { }
/**
* Load the Spring Integration Application Context
*
* @param args - command line arguments
*/
public static void main(final String... args) {
LOGGER.info("\n=========================================================="
+ "\n "
+ "\n Welcome to the Spring Integration Dynamic Poller Sample! "
+ "\n "
+ "\n For more information please visit: "
+ "\n http://www.springsource.org/spring-integration "
+ "\n "
+ "\n==========================================================" );
final AbstractApplicationContext context =
new ClassPathXmlApplicationContext("classpath:META-INF/spring/integration/*-context.xml");
context.registerShutdownHook();
final Scanner scanner = new Scanner(System.in);
final DynamicPeriodicTrigger trigger = context.getBean(DynamicPeriodicTrigger.class);
LOGGER.info("\n========================================================="
+ "\n "
+ "\n Please press 'q + Enter' to quit the application. "
+ "\n "
+ "\n=========================================================" );
System.out.print("Please enter a non-negative numeric value and press <enter>: ");
while (true) {
final String input = scanner.nextLine();
if("q".equals(input.trim())) {
break;
}
try {
int triggerPeriod = Integer.valueOf(input);
System.out.println(String.format("Setting trigger period to '%s' ms", triggerPeriod));
trigger.setPeriod(triggerPeriod);
} catch (Exception e) {
LOGGER.error("An exception was caught: " + e);
}
System.out.print("Please enter a non-negative numeric value and press <enter>: ");
}
LOGGER.info("Exiting application...bye.");
System.exit(0);
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:task="http://www.springframework.org/schema/task">
<int:inbound-channel-adapter expression="new java.text.SimpleDateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(new java.util.Date())" channel="logger">
<int:poller max-messages-per-poll="1" trigger="dynamicTrigger" />
</int:inbound-channel-adapter>
<bean id="dynamicTrigger" class="org.springframework.integration.samples.poller.DynamicPeriodicTrigger">
<constructor-arg name="period" value="5000"/>
</bean>
<int:logging-channel-adapter id="logger" logger-name="org.springframework.integration.samples.poller"/>
<task:executor id="executor" queue-capacity="20" pool-size="5-20"/>
</beans>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<!-- Loggers -->
<logger name="org.springframework">
<level value="warn" />
</logger>
<logger name="org.springframework.integration.samples">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>