From 30553dabdcff4bd4f5c25ac7c389a08fe072d064 Mon Sep 17 00:00:00 2001 From: mrFlick72 Date: Tue, 28 Jan 2020 00:36:18 +0100 Subject: [PATCH 1/7] rsocket endpoint --- pom.xml | 5 +++++ .../messages/MessageRSocketEndPoint.kt | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt diff --git a/pom.xml b/pom.xml index 3666748..95b4a09 100644 --- a/pom.xml +++ b/pom.xml @@ -36,6 +36,11 @@ spring-boot-starter-webflux + + org.springframework.boot + spring-boot-starter-rsocket + + org.springframework.boot spring-boot-starter-actuator diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt new file mode 100644 index 0000000..0aaefa7 --- /dev/null +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt @@ -0,0 +1,16 @@ +package it.valeriovaudi.i18nmessage.messages + +import org.springframework.messaging.handler.annotation.DestinationVariable +import org.springframework.messaging.handler.annotation.MessageMapping +import org.springframework.stereotype.Controller +import java.util.* + +@Controller +class MessageRSocketEndPoint(private val messageRepository: MessageRepository) { + + @MessageMapping("/messages/{application}") + fun messageEndPointRoute( + @DestinationVariable("application") application: String, + lang: String? + ) = messageRepository.find(application, Optional.ofNullable(lang).map { Locale(it) }.orElse(Locale.ENGLISH)) +} \ No newline at end of file From 5cc79e980b1af6f35eab8c5e3a04780f5c6ef54e Mon Sep 17 00:00:00 2001 From: mrFlick72 Date: Sat, 1 Feb 2020 13:47:26 +0100 Subject: [PATCH 2/7] rsocket endpoint --- .../valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt index 0aaefa7..6af5797 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/MessageRSocketEndPoint.kt @@ -8,7 +8,7 @@ import java.util.* @Controller class MessageRSocketEndPoint(private val messageRepository: MessageRepository) { - @MessageMapping("/messages/{application}") + @MessageMapping("messages.{application}") fun messageEndPointRoute( @DestinationVariable("application") application: String, lang: String? From 43b60d4425d087b509cbf06efd2b415d2b1b6f45 Mon Sep 17 00:00:00 2001 From: mrFlick72 Date: Sun, 2 Feb 2020 14:38:03 +0100 Subject: [PATCH 3/7] sqs listener for push message changing --- pom.xml | 29 +++++++++- .../i18nmessage/I18nMessageApplication.kt | 53 ++++++++++++++++--- .../messages/AwsS3MessageRepository.kt | 12 ++--- .../i18nmessage/messages/AwsSQSListener.kt | 13 +++++ 4 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt diff --git a/pom.xml b/pom.xml index 95b4a09..41addfc 100644 --- a/pom.xml +++ b/pom.xml @@ -26,10 +26,35 @@ com.amazonaws - aws-java-sdk-s3 - 1.11.509 + aws-java-sdk + 1.11.714 + + + + javax.jms + javax.jms-api + 2.0.1 + + + + + com.amazonaws + amazon-sqs-java-messaging-lib + 1.0.8 + + + + + org.springframework + spring-jms + org.springframework.boot diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt index c0f312f..ae1a354 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt @@ -1,33 +1,74 @@ package it.valeriovaudi.i18nmessage +import com.amazon.sqs.javamessaging.ProviderConfiguration +import com.amazon.sqs.javamessaging.SQSConnectionFactory +import com.amazonaws.auth.AWSCredentialsProvider import com.amazonaws.auth.AWSStaticCredentialsProvider import com.amazonaws.auth.BasicAWSCredentials import com.amazonaws.services.s3.AmazonS3ClientBuilder +import com.amazonaws.services.sqs.AmazonSQSClientBuilder import it.valeriovaudi.i18nmessage.messages.AwsS3MessageRepository import it.valeriovaudi.i18nmessage.messages.MessageRepository import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean +import org.springframework.jms.annotation.EnableJms +import org.springframework.jms.config.DefaultJmsListenerContainerFactory +import org.springframework.jms.core.JmsTemplate +import org.springframework.jms.support.destination.DynamicDestinationResolver +import javax.jms.ConnectionFactory +import javax.jms.Session + +@EnableJms @SpringBootApplication class I18nMessageApplication { @Bean - fun messageRepository(@Value("\${aws.s3.access-key}") accessKey: String, - @Value("\${aws.s3.secret-key}") awsSecretKey: String, - @Value("\${aws.s3.region}") awsRegion: String, - @Value("\${aws.s3.bucket}") awsBucket: String): MessageRepository { - val credentials = BasicAWSCredentials(accessKey, awsSecretKey) + fun sqsConnectionFactory(@Value("\${aws.s3.region}") awsRegion: String, + awsCredentialsProvider: AWSCredentialsProvider): SQSConnectionFactory { + return SQSConnectionFactory( + ProviderConfiguration(), + AmazonSQSClientBuilder + .standard() + .withCredentials(awsCredentialsProvider) + .withRegion(awsRegion) + .build() + ) + + } + + @Bean + fun jmsListenerContainerFactory(sqsConnectionFactory : ConnectionFactory): DefaultJmsListenerContainerFactory { + val factory = DefaultJmsListenerContainerFactory() + factory.setConnectionFactory(sqsConnectionFactory) + factory.setDestinationResolver(DynamicDestinationResolver()) + factory.setConcurrency("3-10") + factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE) + return factory + } + + + @Bean + fun messageRepository(@Value("\${aws.s3.region}") awsRegion: String, + @Value("\${aws.s3.bucket}") awsBucket: String, + awsCredentialsProvider: AWSCredentialsProvider): MessageRepository { val s3client = AmazonS3ClientBuilder .standard() - .withCredentials(AWSStaticCredentialsProvider(credentials)) + .withCredentials(awsCredentialsProvider) .withRegion(awsRegion) .build() return AwsS3MessageRepository(s3client, awsBucket) } + + @Bean + fun awsCredentialsProvider(@Value("\${aws.s3.access-key}") accessKey: String, + @Value("\${aws.s3.secret-key}") awsSecretKey: String): + AWSCredentialsProvider = AWSStaticCredentialsProvider(BasicAWSCredentials(accessKey, awsSecretKey)) + } fun main(args: Array) { diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsS3MessageRepository.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsS3MessageRepository.kt index 801c4f6..266a486 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsS3MessageRepository.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsS3MessageRepository.kt @@ -20,11 +20,9 @@ open class AwsS3MessageRepository(private val s3client: AmazonS3, private fun s3MessageBundleFor(message: ObjectListing, application: String, language: String) = Mono.defer { - messageKeyFor(application) - .let { messagesKey -> - Optional.ofNullable(message.objectSummaries.find(resourceBundleFinderPredicate(messagesKey, language))) - .orElse(message.objectSummaries.find(defaultResourceBundleFinderPredicate(messagesKey))) - }.toMono() + Optional.ofNullable(message.objectSummaries.find(resourceBundleFinderPredicate(application, language))) + .orElse(message.objectSummaries.find(defaultResourceBundleFinderPredicate(application))) + .toMono() } private fun defaultResourceBundleFinderPredicate(messagesKey: String): S3ObjectSummaryPredicate = @@ -45,8 +43,6 @@ open class AwsS3MessageRepository(private val s3client: AmazonS3, .map { it.toMap() as Map } - private fun messageKeyFor(application: String) = "i18n-messages/$application" - - private fun getAllS3MessagesBundleFor(application: String) = Mono.fromCallable { s3client.listObjects(bucketName, messageKeyFor(application)) } + private fun getAllS3MessagesBundleFor(application: String) = Mono.fromCallable { s3client.listObjects(bucketName, "$application") } } \ No newline at end of file diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt new file mode 100644 index 0000000..d7b2f27 --- /dev/null +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt @@ -0,0 +1,13 @@ +package it.valeriovaudi.i18nmessage.messages + +import org.springframework.jms.annotation.JmsListener +import org.springframework.stereotype.Component + +@Component +class AwsSQSListener { + + @JmsListener(destination = "i18n-messages-updates") + fun onMessage(message: String) { + println(message) + } +} \ No newline at end of file From 90662b06b1381ef82f89671bae4c5490caa283b4 Mon Sep 17 00:00:00 2001 From: mrFlick72 Date: Sun, 2 Feb 2020 18:02:52 +0100 Subject: [PATCH 4/7] bundle push notification --- pom.xml | 10 +++++----- .../i18nmessage/I18nMessageApplication.kt | 19 +++++++++++++++++-- .../i18nmessage/messages/AwsSQSListener.kt | 18 +++++++++++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 41addfc..a639f1e 100644 --- a/pom.xml +++ b/pom.xml @@ -31,12 +31,12 @@ - + javax.jms javax.jms-api diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt index ae1a354..fe234c0 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt @@ -7,6 +7,7 @@ import com.amazonaws.auth.AWSStaticCredentialsProvider import com.amazonaws.auth.BasicAWSCredentials import com.amazonaws.services.s3.AmazonS3ClientBuilder import com.amazonaws.services.sqs.AmazonSQSClientBuilder +import io.rsocket.transport.netty.client.TcpClientTransport import it.valeriovaudi.i18nmessage.messages.AwsS3MessageRepository import it.valeriovaudi.i18nmessage.messages.MessageRepository import org.springframework.beans.factory.annotation.Value @@ -15,8 +16,11 @@ import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.jms.annotation.EnableJms import org.springframework.jms.config.DefaultJmsListenerContainerFactory -import org.springframework.jms.core.JmsTemplate import org.springframework.jms.support.destination.DynamicDestinationResolver +import org.springframework.messaging.rsocket.RSocketRequester +import org.springframework.messaging.rsocket.RSocketStrategies +import reactor.core.publisher.Mono +import java.net.InetSocketAddress import javax.jms.ConnectionFactory import javax.jms.Session @@ -40,7 +44,7 @@ class I18nMessageApplication { } @Bean - fun jmsListenerContainerFactory(sqsConnectionFactory : ConnectionFactory): DefaultJmsListenerContainerFactory { + fun jmsListenerContainerFactory(sqsConnectionFactory: ConnectionFactory): DefaultJmsListenerContainerFactory { val factory = DefaultJmsListenerContainerFactory() factory.setConnectionFactory(sqsConnectionFactory) factory.setDestinationResolver(DynamicDestinationResolver()) @@ -49,6 +53,17 @@ class I18nMessageApplication { return factory } + @Bean + fun requester(rSocketStrategies: RSocketStrategies, + @Value("\${i18n-messages.rsocket.host}") i18nHost: String, + @Value("\${i18n-messages.rsocket.port}") i18nPort: Int, + builder: RSocketRequester.Builder) : Mono { + val address = InetSocketAddress(i18nHost, i18nPort) + val clientTransport: TcpClientTransport = TcpClientTransport.create(address) + return builder.rsocketStrategies(rSocketStrategies) + .connect(clientTransport) + + } @Bean fun messageRepository(@Value("\${aws.s3.region}") awsRegion: String, diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt index d7b2f27..e0bf536 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt @@ -1,13 +1,29 @@ package it.valeriovaudi.i18nmessage.messages +import com.jayway.jsonpath.JsonPath import org.springframework.jms.annotation.JmsListener +import org.springframework.messaging.rsocket.RSocketRequester import org.springframework.stereotype.Component +import reactor.core.publisher.Mono +import java.util.* @Component -class AwsSQSListener { +class AwsSQSListener(private val messageRepository: MessageRepository, + private val requester: Mono) { @JmsListener(destination = "i18n-messages-updates") fun onMessage(message: String) { + val s3key = JsonPath.read(message, "$.detail.requestParameters") as String + val applicationName = s3key.split("//").first() + messageRepository.find(applicationName, Locale.ENGLISH) + .flatMap { bundle -> + requester.flatMap { req -> + req.route("messages.$applicationName") + .data(bundle) + .send() + } + }.subscribe() + println(message) } } \ No newline at end of file From 5cdeca1573f6d3bf1c897b5431db61fc1f96e6e6 Mon Sep 17 00:00:00 2001 From: mrFlick72 Date: Sun, 2 Feb 2020 18:30:58 +0100 Subject: [PATCH 5/7] bundle push notification --- .../i18nmessage/messages/AwsSQSListener.kt | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt index e0bf536..7363615 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt @@ -6,6 +6,8 @@ import org.springframework.messaging.rsocket.RSocketRequester import org.springframework.stereotype.Component import reactor.core.publisher.Mono import java.util.* +import java.util.regex.Pattern +import kotlin.collections.LinkedHashMap @Component class AwsSQSListener(private val messageRepository: MessageRepository, @@ -13,17 +15,20 @@ class AwsSQSListener(private val messageRepository: MessageRepository, @JmsListener(destination = "i18n-messages-updates") fun onMessage(message: String) { - val s3key = JsonPath.read(message, "$.detail.requestParameters") as String - val applicationName = s3key.split("//").first() - messageRepository.find(applicationName, Locale.ENGLISH) - .flatMap { bundle -> - requester.flatMap { req -> - req.route("messages.$applicationName") - .data(bundle) - .send() - } - }.subscribe() - - println(message) + Optional.ofNullable(JsonPath.read(message, "$.detail.requestParameters.key") as String?) + .map { + val applicationName = it.split("/").first() + messageRepository.find(applicationName, Locale.ENGLISH) + .flatMap { bundle -> + Optional.ofNullable(bundle) + .map { + requester.flatMap { req -> + req.route("messages.$applicationName") + .data(bundle) + .send() + } + }.orElse(Mono.empty()) + }.subscribe() + } } } \ No newline at end of file From 26700ce5b57204d2f0424c98ec580d11ee1a6ab2 Mon Sep 17 00:00:00 2001 From: mrFlick72 Date: Sun, 2 Feb 2020 18:33:31 +0100 Subject: [PATCH 6/7] bundle push notification --- .../it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt index 7363615..0eda382 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt @@ -18,6 +18,7 @@ class AwsSQSListener(private val messageRepository: MessageRepository, Optional.ofNullable(JsonPath.read(message, "$.detail.requestParameters.key") as String?) .map { val applicationName = it.split("/").first() + println("application $applicationName bundle are refreshing") messageRepository.find(applicationName, Locale.ENGLISH) .flatMap { bundle -> Optional.ofNullable(bundle) From 3443da657660326fd84df7d5cff5c4c312c53f74 Mon Sep 17 00:00:00 2001 From: mrFlick72 Date: Mon, 3 Feb 2020 18:54:28 +0100 Subject: [PATCH 7/7] bundle push notification --- pom.xml | 7 ++++ .../i18nmessage/I18nMessageApplication.kt | 28 +++++++++----- .../i18nmessage/messages/AwsSQSListener.kt | 37 ++++++++++++------- 3 files changed, 49 insertions(+), 23 deletions(-) diff --git a/pom.xml b/pom.xml index a639f1e..4451dbf 100644 --- a/pom.xml +++ b/pom.xml @@ -23,6 +23,13 @@ + + + io.arrow-kt + arrow-core + 0.10.4 + + com.amazonaws diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt index fe234c0..734c066 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/I18nMessageApplication.kt @@ -12,6 +12,9 @@ import it.valeriovaudi.i18nmessage.messages.AwsS3MessageRepository import it.valeriovaudi.i18nmessage.messages.MessageRepository import org.springframework.beans.factory.annotation.Value import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.boot.context.properties.ConstructorBinding +import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.runApplication import org.springframework.context.annotation.Bean import org.springframework.jms.annotation.EnableJms @@ -27,6 +30,7 @@ import javax.jms.Session @EnableJms @SpringBootApplication +@EnableConfigurationProperties(value = [RSocketApplicationClientApps::class]) class I18nMessageApplication { @Bean @@ -54,16 +58,16 @@ class I18nMessageApplication { } @Bean - fun requester(rSocketStrategies: RSocketStrategies, - @Value("\${i18n-messages.rsocket.host}") i18nHost: String, - @Value("\${i18n-messages.rsocket.port}") i18nPort: Int, - builder: RSocketRequester.Builder) : Mono { - val address = InetSocketAddress(i18nHost, i18nPort) - val clientTransport: TcpClientTransport = TcpClientTransport.create(address) - return builder.rsocketStrategies(rSocketStrategies) - .connect(clientTransport) + fun requesters(rSocketStrategies: RSocketStrategies, + rSocketApplicationClientApps: RSocketApplicationClientApps, + builder: RSocketRequester.Builder): Map> = - } + rSocketApplicationClientApps.clients.map { + val address = InetSocketAddress(it.host, it.port) + val clientTransport: TcpClientTransport = TcpClientTransport.create(address) + mapOf(it.id to builder.rsocketStrategies(rSocketStrategies) + .connect(clientTransport)) + }.reduce { acc, map -> acc + map } @Bean fun messageRepository(@Value("\${aws.s3.region}") awsRegion: String, @@ -86,6 +90,12 @@ class I18nMessageApplication { } +@ConstructorBinding +@ConfigurationProperties(prefix = "rsocket") +data class RSocketApplicationClientApps(val clients: List) + +data class RSocketApplicationClientApp(val id: String, val host: String, val port: Int) + fun main(args: Array) { runApplication(*args) } diff --git a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt index 0eda382..0b7945d 100644 --- a/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt +++ b/src/main/kotlin/it/valeriovaudi/i18nmessage/messages/AwsSQSListener.kt @@ -6,30 +6,39 @@ import org.springframework.messaging.rsocket.RSocketRequester import org.springframework.stereotype.Component import reactor.core.publisher.Mono import java.util.* -import java.util.regex.Pattern -import kotlin.collections.LinkedHashMap @Component class AwsSQSListener(private val messageRepository: MessageRepository, - private val requester: Mono) { + private val requesters: Map>) { @JmsListener(destination = "i18n-messages-updates") fun onMessage(message: String) { - Optional.ofNullable(JsonPath.read(message, "$.detail.requestParameters.key") as String?) - .map { - val applicationName = it.split("/").first() + applicationNameFor2(message) + .map { applicationName -> + println("application $applicationName bundle are refreshing") + messageRepository.find(applicationName, Locale.ENGLISH) .flatMap { bundle -> - Optional.ofNullable(bundle) - .map { - requester.flatMap { req -> - req.route("messages.$applicationName") - .data(bundle) - .send() - } - }.orElse(Mono.empty()) + sendBundleToClient(bundle, applicationName) }.subscribe() } } + + private fun applicationNameFor2(message: String) = + Optional.ofNullable(JsonPath.read(message, "$.detail.requestParameters.key") as String?) + .map { key -> key.split("/").first() } + + + private fun sendBundleToClient(bundle: Messages, applicationName: String): Mono? { + return Optional.ofNullable(bundle) + .map { requesters[applicationName] } + .map { applicationRequester -> + applicationRequester?.flatMap { req -> + req.route("messages.$applicationName") + .data(bundle) + .send() + } + }.orElse(Mono.empty()) + } } \ No newline at end of file