diff --git a/basic/helloworld-groovy/src/main/groovy/org/springframework/integration/samples/helloworld/HelloWorldConfig.groovy b/basic/helloworld-groovy/src/main/groovy/org/springframework/integration/samples/helloworld/HelloWorldConfig.groovy
index f572d18ed..f186de378 100644
--- a/basic/helloworld-groovy/src/main/groovy/org/springframework/integration/samples/helloworld/HelloWorldConfig.groovy
+++ b/basic/helloworld-groovy/src/main/groovy/org/springframework/integration/samples/helloworld/HelloWorldConfig.groovy
@@ -43,7 +43,7 @@ class HelloWorldConfig {
@Bean
MessageChannel outputChannel() {
- new QueueChannel(10)
+ new QueueChannel()
}
@Bean
diff --git a/basic/helloworld-kotlin/README.md b/basic/helloworld-kotlin/README.md
new file mode 100644
index 000000000..ef6a70a9c
--- /dev/null
+++ b/basic/helloworld-kotlin/README.md
@@ -0,0 +1,38 @@
+Hello World Sample
+==================
+
+This is the Kotlin version of the helloworld Java sample using Kotlin DSL. This sample project contains 2 basic sample applications:
+
+* Hello World
+* Poller Application
+
+## Hello World
+
+The Hello World application demonstrates a simple message flow represented by the diagram below:
+
+ Message -> Channel -> ServiceActivator -> QueueChannel
+
+To run the sample simply execute **HelloWorldApp** in package **org.springframework.integration.samples.helloworld**.
+You can also execute that class using the [Gradle](https://www.gradle.org):
+
+ $ gradlew :helloworld-kotlin:runHelloWorldApp
+
+You should see the following output:
+
+ INFO : org.springframework.integration.samples.helloworld.HelloWorldApp - ==> HelloWorldDemo: Hello World
+
+## Poller Application
+
+This simple application will print out the current system time twice every 20 seconds.
+
+More specifically, an **Inbound Channel Adapter** polls for the current system time 2 times every 20 seconds (20000 milliseconds). 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.
+
+To run the sample simply execute **PollerApp** in package **org.springframework.integration.samples.helloworld**.
+You can also execute that class using the [Gradle](https://www.gradle.org):
+
+ $ gradlew :helloworld-kotlin:runPollerApp
+
+You should see output like the following:
+
+[task-scheduler-1][org.springframework.integration.samples.helloworld] GenericMessage [payload=1763478785243, headers={id=8f93b18a-063a-5e9f-4708-2ed1d04a1566, timestamp=1763478785244}]
+[task-scheduler-1][org.springframework.integration.samples.helloworld] GenericMessage [payload=1763478785248, headers={id=aa37e9c4-95d1-538c-a6cd-d400bb1474bf, timestamp=1763478785248}]
diff --git a/basic/helloworld-kotlin/pom.xml b/basic/helloworld-kotlin/pom.xml
new file mode 100644
index 000000000..2e8a454e7
--- /dev/null
+++ b/basic/helloworld-kotlin/pom.xml
@@ -0,0 +1,241 @@
+
+
+ 4.0.0
+ org.springframework.integration.samples
+ helloworld-kotlin
+ 7.0.0
+ https://github.com/spring-projects/spring-integration-samples
+
+ Spring IO
+ https://spring.io/projects/spring-integration
+
+
+
+ Apache License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ artembilan
+ Artem Bilan
+ artem.bilan@broadcom.com
+
+ project lead
+
+
+
+ garyrussell
+ Gary Russell
+ github@gprussell.net
+
+ project lead emeritus
+
+
+
+ markfisher
+ Mark Fisher
+ mark.ryan.fisher@gmail.com
+
+ project founder and lead emeritus
+
+
+
+ cppwfs
+ Glenn Renfro
+ glenn.renfro@broadcom.com
+
+ project committer
+
+
+
+
+ scm:git:scm:git:git://github.com/spring-projects/spring-integration-samples.git
+ scm:git:scm:git:ssh://git@github.com:spring-projects/spring-integration-samples.git
+ https://github.com/spring-projects/spring-integration-samples
+
+
+ GitHub
+ https://github.com/spring-projects/spring-integration-samples/issues
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ 2.2.21
+
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+ 2.2.21
+
+
+ org.junit
+ junit-bom
+ 6.0.0
+ import
+ pom
+
+
+ tools.jackson
+ jackson-bom
+ 3.0.0-rc9
+ import
+ pom
+
+
+ com.fasterxml.jackson
+ jackson-bom
+ 2.20.0
+ import
+ pom
+
+
+ org.springframework
+ spring-framework-bom
+ 7.0.0-SNAPSHOT
+ import
+ pom
+
+
+ org.springframework.integration
+ spring-integration-bom
+ 7.0.0-SNAPSHOT
+ import
+ pom
+
+
+
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.24.3
+ compile
+
+
+ org.springframework.integration
+ spring-integration-core
+ compile
+
+
+ org.jetbrains.kotlin
+ kotlin-stdlib
+ compile
+
+
+ org.jetbrains.kotlin
+ kotlin-reflect
+ compile
+
+
+ org.hamcrest
+ hamcrest-library
+ 2.2
+ test
+
+
+ org.mockito
+ mockito-core
+ 5.18.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.springframework.integration
+ spring-integration-test
+ test
+
+
+ org.apache.logging.log4j
+ log4j-core-test
+ 2.24.3
+ test
+
+
+ org.awaitility
+ awaitility
+ 4.2.2
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.27.6
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ runtime
+
+
+ org.junit.platform
+ junit-platform-launcher
+ runtime
+
+
+
+ 17
+
+
+
+ repo.spring.io.milestone
+ Spring Framework Maven Milestone Repository
+ https://repo.spring.io/milestone
+
+
+ repo.spring.io.snapshot
+ Spring Framework Maven Snapshot Repository
+ https://repo.spring.io/snapshot
+
+
+
+ ${project.basedir}/src/main/kotlin
+ ${project.basedir}/src/test/kotlin
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-plugin
+ 2.2.21
+
+
+ -Xjsr305=strict
+
+
+ spring
+
+
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ test-compile
+ test-compile
+
+ test-compile
+
+
+
+
+
+ org.jetbrains.kotlin
+ kotlin-maven-allopen
+ 2.2.21
+
+
+
+
+
+
diff --git a/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloService.kt b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloService.kt
new file mode 100644
index 000000000..d766202d9
--- /dev/null
+++ b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloService.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+/**
+ * Simple POJO to be referenced from a Service Activator.
+ *
+ * @author Glenn Renfro
+ */
+class HelloService {
+
+ fun sayHello(name: String) = "Hello $name"
+
+}
diff --git a/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloWorldApp.kt b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloWorldApp.kt
new file mode 100644
index 000000000..7a9c78d0f
--- /dev/null
+++ b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloWorldApp.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+import org.apache.commons.logging.LogFactory
+import org.springframework.context.annotation.AnnotationConfigApplicationContext
+import org.springframework.messaging.MessageChannel
+import org.springframework.messaging.PollableChannel
+import org.springframework.messaging.support.GenericMessage
+
+/**
+ * Demonstrates a basic Message Endpoint that simply prepends a greeting
+ * ("Hello ") to an inbound String payload from a Message.
+ *
+ * @author Glenn Renfro
+ */
+object HelloWorldApp {
+
+ private val logger = LogFactory.getLog(HelloWorldApp::class.java)
+
+ @JvmStatic
+ fun main(args: Array) {
+ val context = AnnotationConfigApplicationContext(HelloWorldConfig::class.java)
+ val inputChannel = context.getBean("inputChannel", MessageChannel::class.java)
+ val outputChannel = context.getBean("outputChannel", PollableChannel::class.java)
+ inputChannel.send(GenericMessage("World"))
+ logger.info("==> HelloWorldDemo: ${outputChannel.receive(0)?.payload}")
+ context.close()
+ }
+
+}
diff --git a/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloWorldConfig.kt b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloWorldConfig.kt
new file mode 100644
index 000000000..bbe2c5efa
--- /dev/null
+++ b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/HelloWorldConfig.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.integration.channel.DirectChannel
+import org.springframework.integration.channel.QueueChannel
+import org.springframework.integration.config.EnableIntegration
+import org.springframework.integration.dsl.integrationFlow
+import org.springframework.messaging.MessageChannel
+
+/**
+ * Configuration for the HelloWorld integration flow using Kotlin DSL.
+ *
+ * @author Glenn Renfro
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableIntegration
+class HelloWorldConfig {
+
+ /**
+ * Creates the input channel for inbound messages.
+ *
+ * A [DirectChannel] is used for synchronous, immediate message delivery.
+ * Messages arriving on this channel are processed on the sender's thread
+ * without any buffering or queuing.
+ *
+ * @return A [DirectChannel] instance for synchronous inbound message delivery
+ */
+ @Bean
+ fun inputChannel() = DirectChannel()
+
+ /**
+ * Creates the output channel for outbound messages.
+ *
+ * A [QueueChannel] with default capacity provides asynchronous,
+ * buffered message delivery. Results from the integration flow are queued
+ * and available for downstream consumption.
+ *
+ * @return A [QueueChannel] instance with default capacity.
+ */
+ @Bean
+ fun outputChannel() = QueueChannel()
+
+ /**
+ * Creates the Hello World business service.
+ *
+ * [HelloService] implements the core greeting logic that transforms
+ * input messages into personalized greeting responses.
+ *
+ * @return A [HelloService] instance
+ */
+ @Bean
+ fun helloService() = HelloService()
+
+ /**
+ * Defines the main integration flow for message processing.
+ *
+ * @param inputChannel The synchronous input channel receiving messages
+ * @param outputChannel The asynchronous output channel for results
+ * @param helloService The service implementing the greeting logic
+ * @return An IntegrationFlow representing the complete message flow
+ */
+ @Bean
+ fun helloWorldFlow(inputChannel: MessageChannel, outputChannel: MessageChannel, helloService: HelloService) =
+ integrationFlow(inputChannel) {
+ handle(helloService, "sayHello")
+ channel(outputChannel)
+ }
+}
diff --git a/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/PollerApp.kt b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/PollerApp.kt
new file mode 100644
index 000000000..11980cbbf
--- /dev/null
+++ b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/PollerApp.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+import org.springframework.context.annotation.AnnotationConfigApplicationContext
+
+/**
+ * Simple application that polls the current system time 2 times every
+ * 20 seconds (20000 milliseconds).
+ *
+ * The resulting message contains the time in milliseconds and the message
+ * is routed to a Logging Channel Adapter which will print the time to the
+ * command prompt.
+ *
+ * @author Glenn Renfro
+ */
+object PollerApp {
+
+ @JvmStatic
+ fun main(args: Array) {
+ AnnotationConfigApplicationContext(PollerConfig::class.java)
+ }
+
+}
diff --git a/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/PollerConfig.kt b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/PollerConfig.kt
new file mode 100644
index 000000000..1e136dec2
--- /dev/null
+++ b/basic/helloworld-kotlin/src/main/kotlin/org/springframework/integration/samples/helloworld/PollerConfig.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Configuration
+import org.springframework.integration.channel.NullChannel
+import org.springframework.integration.config.EnableIntegration
+import org.springframework.integration.dsl.integrationFlow
+import org.springframework.integration.handler.LoggingHandler
+
+/**
+ * Configuration for the Poller integration flow using the Kotlin DSL.
+ * This flow polls for the current system time every 20 seconds and logs it.
+ *
+ * @author Glenn Renfro
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableIntegration
+class PollerConfig {
+
+ /**
+ * Defines a polling-based integration flow for periodic message generation.
+ *
+ * This flow demonstrates a time-triggered, scheduled message processing pattern.
+ * A message source generates timestamps at fixed intervals, logs the values,
+ * and discards them via the null channel. This is useful for monitoring,
+ * health checks, or triggering periodic processing logic.
+ *
+ * @return An IntegrationFlow that periodically generates and logs timestamps, then discards them.
+ */
+ @Bean
+ fun pollerFlow() =
+ integrationFlow(
+ { System.currentTimeMillis() },
+ { poller { it.fixedDelay(20000).maxMessagesPerPoll(2) } }) {
+ log(LoggingHandler.Level.INFO, "org.springframework.integration.samples.helloworld")
+ channel(NullChannel())
+ }
+}
diff --git a/basic/helloworld-kotlin/src/main/resources/log4j2.xml b/basic/helloworld-kotlin/src/main/resources/log4j2.xml
new file mode 100644
index 000000000..7545f857d
--- /dev/null
+++ b/basic/helloworld-kotlin/src/main/resources/log4j2.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/HelloServiceTests.kt b/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/HelloServiceTests.kt
new file mode 100644
index 000000000..6b0ab5fd3
--- /dev/null
+++ b/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/HelloServiceTests.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+/**
+ * Unit tests for HelloService.
+ *
+ * @author Glenn Renfro
+ */
+class HelloServiceTests {
+
+ @Test
+ fun testSayHello() {
+ val service = HelloService()
+ val result = service.sayHello("World")
+ assertThat(result).isEqualTo("Hello World")
+ }
+
+}
diff --git a/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/HelloWorldConfigTests.kt b/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/HelloWorldConfigTests.kt
new file mode 100644
index 000000000..df5acb516
--- /dev/null
+++ b/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/HelloWorldConfigTests.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.messaging.MessageChannel
+import org.springframework.messaging.PollableChannel
+import org.springframework.messaging.support.GenericMessage
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig
+
+/**
+ * Integration tests for HelloWorld flow.
+ *
+ * @author Glenn Renfro
+ */
+@SpringJUnitConfig(HelloWorldConfig::class)
+class HelloWorldConfigTests {
+
+ @Autowired
+ lateinit var inputChannel: MessageChannel
+
+ @Autowired
+ lateinit var outputChannel: PollableChannel
+
+ @Test
+ fun testHelloWorldFlow() {
+ inputChannel.send(GenericMessage("World"))
+ val message = outputChannel.receive(1000)
+ assertThat(message).isNotNull
+ assertThat(message?.payload).isNotNull
+ }
+
+ @Test
+ fun testMultipleMessages() {
+ inputChannel.send(GenericMessage("Test1"))
+ inputChannel.send(GenericMessage("Test2"))
+
+ val message1 = outputChannel.receive(1000)
+ assertThat(message1).isNotNull
+ assertThat(message1?.payload).isEqualTo("Hello Test1")
+
+ val message2 = outputChannel.receive(1000)
+ assertThat(message2).isNotNull
+ assertThat(message2?.payload).isEqualTo("Hello Test2")
+ }
+
+}
diff --git a/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/PollerConfigTests.kt b/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/PollerConfigTests.kt
new file mode 100644
index 000000000..8b0dfe3de
--- /dev/null
+++ b/basic/helloworld-kotlin/src/test/kotlin/org/springframework/integration/samples/helloworld/PollerConfigTests.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2025-present 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
+ *
+ * https://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.helloworld
+
+import org.apache.logging.log4j.LogManager
+import org.apache.logging.log4j.core.LoggerContext
+import org.apache.logging.log4j.core.test.appender.ListAppender
+import org.assertj.core.api.Assertions.assertThat
+import org.awaitility.Awaitility.await
+import org.junit.jupiter.api.AfterAll
+import org.junit.jupiter.api.BeforeAll
+import org.junit.jupiter.api.Test
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.integration.dsl.IntegrationFlow
+import org.springframework.test.annotation.DirtiesContext
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig
+import java.time.Duration
+
+
+/**
+ * Integration tests for Poller flow.
+ * Tests verify that the poller actually polls and produces messages.
+ *
+ * @author Glenn Renfro
+ */
+@SpringJUnitConfig(PollerConfig::class)
+@DirtiesContext
+class PollerConfigTests {
+
+ @Autowired
+ lateinit var pollerFlow: IntegrationFlow
+
+ companion object {
+ lateinit var listAppender: ListAppender
+
+ @JvmStatic
+ @BeforeAll
+ fun setupLogger() {
+ val loggerContext = LogManager.getContext(false) as LoggerContext
+ listAppender = ListAppender("TestAppender")
+ listAppender.start()
+ loggerContext.configuration.addAppender(listAppender)
+ loggerContext.rootLogger.addAppender(listAppender)
+ loggerContext.updateLoggers()
+ }
+
+ @JvmStatic
+ @AfterAll
+ fun cleanupLogger() {
+ val loggerContext = LogManager.getContext(false) as LoggerContext
+ loggerContext.rootLogger.removeAppender(listAppender)
+ listAppender.stop()
+ }
+ }
+
+ @Test
+ fun testPollerFlowBeanExists() {
+ assertThat(pollerFlow).isNotNull
+ }
+
+ @Test
+ fun testPollerFlowConfiguration() {
+ val integrationComponents = pollerFlow.integrationComponents
+ assertThat(integrationComponents).isNotNull
+ assertThat(integrationComponents.size).isGreaterThan(0)
+ }
+
+ @Test
+ fun testPollerIsActiveAndRunning() {
+ await()
+ .atMost(Duration.ofSeconds(5))
+ .until { listAppender.events.isNotEmpty() }
+
+ assertThat(listAppender.events)
+ .anyMatch { event ->
+ event.toString()
+ .contains("org.springframework.integration.samples.helloworld Level=INFO " +
+ "Message=GenericMessage [payload=")}
+
+ }
+}
diff --git a/build.gradle b/build.gradle
index b48103284..16b23a149 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,4 +1,7 @@
buildscript {
+ ext {
+ kotlinVersion = '2.2.21'
+ }
repositories {
mavenCentral()
gradlePluginPortal()
@@ -9,6 +12,8 @@ buildscript {
classpath 'io.spring.gradle:dependency-management-plugin:1.1.7'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
classpath 'org.gretty:gretty:4.1.10'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
+ classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlinVersion"
}
}
@@ -177,6 +182,55 @@ subprojects { subproject ->
}
}
+ if (subproject.plugins.hasPlugin('kotlin')) {
+ def plugins = asNode().build?.find()?.plugins?.find()
+ if (!plugins) {
+ plugins = asNode().appendNode('build').with {
+ appendNode('sourceDirectory', '${project.basedir}/src/main/kotlin')
+ appendNode('testSourceDirectory', '${project.basedir}/src/test/kotlin')
+ appendNode('plugins')
+ }
+ }
+
+ plugins.appendNode('plugin')
+ .with {
+ appendNode('groupId', 'org.jetbrains.kotlin')
+ appendNode('artifactId', 'kotlin-maven-plugin')
+ appendNode('version', property('kotlinVersion'))
+ appendNode('configuration').with {
+ appendNode('args').with {
+ appendNode('arg', '-Xjsr305=strict')
+ }
+ appendNode('compilerPlugins').with {
+ appendNode('plugin', 'spring')
+ }
+ }
+ appendNode('executions').with {
+ appendNode('execution').with {
+ appendNode('id', 'compile')
+ appendNode('phase', 'compile')
+ appendNode('goals').with {
+ appendNode('goal', 'compile')
+ }
+ }
+ appendNode('execution').with {
+ appendNode('id', 'test-compile')
+ appendNode('phase', 'test-compile')
+ appendNode('goals').with {
+ appendNode('goal', 'test-compile')
+ }
+ }
+ }
+ appendNode('dependencies').with {
+ appendNode('dependency').with {
+ appendNode('groupId', 'org.jetbrains.kotlin')
+ appendNode('artifactId', 'kotlin-maven-allopen')
+ appendNode('version', property('kotlinVersion'))
+ }
+ }
+ }
+ }
+
def pomDeps = asNode().dependencies.find()
if (!pomDeps) {
pomDeps = asNode().appendNode('dependencies')
@@ -212,6 +266,13 @@ subprojects { subproject ->
targetCompatibility = JavaVersion.VERSION_17
}
+ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
+ kotlinOptions {
+ jvmTarget = '17'
+ }
+ }
+
+
compileTestJava {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
@@ -221,6 +282,8 @@ subprojects { subproject ->
ext {
artemisVersion = '2.41.0'
aspectjVersion = '1.9.24'
+ assertjVersion = '3.27.6'
+ awaitilityVersion = '4.2.2'
commonsDigesterVersion = '2.1'
commonsDbcpVersion = '2.13.0'
commonsFileUploadVersion = '1.6.0'
@@ -609,6 +672,35 @@ project('helloworld-groovy') {
api "org.apache.logging.log4j:log4j-core:$log4jVersion"
api "org.springframework.integration:spring-integration-groovy"
testImplementation "org.apache.logging.log4j:log4j-core-test:$log4jVersion"
+ testImplementation "org.awaitility:awaitility:$awaitilityVersion"
+ }
+
+ tasks.register ('runHelloWorldApp', JavaExec) {
+ mainClass = 'org.springframework.integration.samples.helloworld.HelloWorldApp'
+ classpath = sourceSets.main.runtimeClasspath
+ }
+
+ tasks.register ('runPollerApp', JavaExec) {
+ mainClass = 'org.springframework.integration.samples.helloworld.PollerApp'
+ classpath = sourceSets.main.runtimeClasspath
+ }
+
+}
+
+project('helloworld-kotlin') {
+ description = 'Hello World Sample for Kotlin Developers'
+
+ apply plugin: 'kotlin'
+ apply plugin: 'kotlin-spring'
+
+ dependencies {
+ api "org.apache.logging.log4j:log4j-core:$log4jVersion"
+ api "org.springframework.integration:spring-integration-core"
+ api "org.jetbrains.kotlin:kotlin-stdlib"
+ api "org.jetbrains.kotlin:kotlin-reflect"
+ testImplementation "org.apache.logging.log4j:log4j-core-test:$log4jVersion"
+ testImplementation "org.awaitility:awaitility:$awaitilityVersion"
+ testImplementation("org.assertj:assertj-core:$assertjVersion")
}
tasks.register ('runHelloWorldApp', JavaExec) {