Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to RxKotlin

Introduction to RxKotlin

Kotlin Yorkshire Meetup Group talk

Mario Arias

June 15, 2016

Transcript

19 slides
  1. Software Engineer at Cake Solutions 10+ years of experience with

    JVM technologies Spring certified trainer 5+ years with Scala 3+ years with Kotlin funKTionale KotlinPrimavera RxKotlin original developer and team leader* NOT an expert on reactive programming @dh44t * I hit “Merge” and “Release” buttons
  2. scanner fun scanner(): ConnectableObservable<String> {
 return Observable.create<String> { subscriber 


    try {
 if (subscriber.isUnsubscribed) {
 return@create
 }
 
 val scanner = Scanner(System.`in`)
 while (true) {
 val line = scanner.nextLine()
 if (line.equals(":q!")) {
 break
 }
 subscriber.onNext(line)
 }
 } catch (e: IOException) {
 subscriber.onError(e)
 }
 
 if (!subscriber.isUnsubscribed) {
 subscriber.onCompleted()
 }
 }.publish()
 }
  3. general fun generalObservable(name: String, input: Observable<String>): Observable<ChatMessage> {
 return input.filter

    { s  !s.startsWith(PrivateMessage.AT) }
 .map { s  "${PrivateMessage.AT}$name says:$s" }
 .map { s  GeneralMessage(s) }
 }
  4. private fun privateObservable(name: String, input: Observable<String>): Observable<ChatMessage> {
 return input.filter

    { s  s.startsWith("@") }
 .map { s  s.split(PrivateMessage.SPACE_PATTERN, 2) }
 .filter { parts 
 val condition = parts.size  2
 if (!condition) {
 println("Wrong format")
 }
 condition
 }
 .filter { parts 
 val condition = !parts[0].equals("@")
 if (!condition) {
 println("Invalid user")
 }
 condition
 }
 .map { parts 
 PrivateMessage("chat.private.${parts[0].replace("@", "")}",
 name.says(parts[1]))
 }
 
 }
  5. chat class Chat(val context: ConfigurableApplicationContext,
 val name: String,
 general: Observable<ChatMessage>,


    priv: Observable<ChatMessage>) : Observer<ChatMessage> {
 val subs = Observable.merge(general, priv).subscribeOn(Schedulers.io()).subscribe(this)
 val latch = CountDownLatch(1);
 val template = context[RabbitTemplateclass.java]
 
 init {
 template.general(GeneralMessage("$name CONNECTED"))
 }
 
 override fun onNext(message: ChatMessage) {
 when(message){
 is GeneralMessage  template.general(message)
 is PrivateMessage  template.privately(message)
 }
 }
 
 override fun onError(e: Throwable) {
 println(e.message)
 }
 
 override fun onCompleted() {
 template.general(GeneralMessage("$name DISCONNECTED"))
 println("Bye")
 latch.countDown()
 subs.unsubscribe()
 context.close()
 }
 
 }
  6. main fun RabbitTemplate.general(message: GeneralMessage): Unit {
 convertAndSend(chatGeneral, "", message.body)
 }


    
 fun RabbitTemplate.privately(message: PrivateMessage): Unit {
 convertAndSend(message.routingKey, message.body)
 }
 
 fun main(args: Array<String>) {
 
 
 //val log = LoggerFactory.getLogger(RxChatApplicationclass.java)
 val context = SpringApplication.run(RxChatApplicationclass.java, *args)
 val props = context[ChatPropertiesclass.java]
 val name = props.name
 println("""
 $name, Welcome to the RxKotlin Chat
 -Every message that you send will published in the general chat
 -To send private messages, use '@' before your friend's alias (e.g.: @JohnDoe Hello)
 -To exit use the command ':q!'
 """)
 
 val scannerObservable = scanner()
 val general = generalObservable(name, scannerObservable)
 val priv = privateObservable(name, scannerObservable)
 
 val chat = Chat(context, name, general, priv)
 scannerObservable.connect()
 chat.latch.await()
 }
  7. scanner with RxKotlin fun scanner(): ConnectableObservable<String> = observable<String> { subscriber

    
 try {
 if (subscriber.isUnsubscribed) {
 return@observable
 }
 
 val scanner = Scanner(System.`in`)
 while (true) {
 val line = scanner.nextLine()
 if (line.equals(":q!")) {
 break
 }
 subscriber.onNext(line)
 }
 } catch (e: IOException) {
 subscriber.onError(e)
 }
 
 if (!subscriber.isUnsubscribed) {
 subscriber.onCompleted()
 }
 }.publish()

  8. chat with RxKotlin class Chat(val context: ConfigurableApplicationContext,
 val name: String,


    vararg obs: Observable<ChatMessage>) {
 
 val latch = CountDownLatch(1);
 val template = context[RabbitTemplateclass.java]
 
 init {
 template.general(GeneralMessage("$name CONNECTED"))
 obs.asIterable().merge().subscribeOn(Schedulers.io()).subscribeWith {
 onNext { message 
 when (message) {
 is GeneralMessage  template.general(message)
 is PrivateMessage  template.privately(message)
 }
 }
 
 onError { e 
 println(e.message)
 }
 
 onCompleted {
 template.general(GeneralMessage("$name DISCONNECTED"))
 println("Bye")
 latch.countDown()
 context.close()
 }
 }
 }
 }
  9. RxKotlin in Detail fun <T> observable(body : (s : Subscriber<in

    T>)  Unit) : Observable<T> = Observable.create(body) fun <T> Iterable<T>.toObservable() : Observable<T> = Observable.from(this) fun <T> Iterable<Observable<out T.merge() : Observable<T> = Observable.merge(this.toObservable()) inline fun <T> Observable<T>.subscribeWith( body : FunctionSubscriberModifier<T>.()  Unit ) : Subscription {
 val modifier = FunctionSubscriberModifier(subscriber<T>())
 modifier.body()
 return subscribe(modifier.subscriber)
 } 

  10. Appendix: Spring Configuration (I) @Configuration
 @EnableRabbit
 open class AmqpConfiguration :

    RabbitListenerConfigurer {
 
 
 override fun configureRabbitListeners(registrar: RabbitListenerEndpointRegistrar) {
 
 fun listener(prefix: String = ""): (Message)  Unit = { message: Message 
 println(prefix + String(message.body))
 }
 
 registrar.registerEndpoint(SimpleRabbitListenerEndpoint().apply {
 id = "generalEndpoint"
 setQueues(generalQueue())
 setMessageListener(listener())
 })
 registrar.registerEndpoint(SimpleRabbitListenerEndpoint().apply {
 id = "privateEndpoint"
 setQueues(privateQueue())
 setMessageListener(listener("[PRIVATE ]"))
 })
 }

  11. Appendix: Spring Configuration (II) @Bean
 open fun connectionFactory(props: RabbitProperties) =

    CachingConnectionFactory().apply {
 setAddresses(props.addresses)
 setUsername(props.username)
 setPassword(props.password)
 }
 
 @Bean
 open fun template(connectionFactory: ConnectionFactory) = RabbitTemplate(connectionFactory)
 
 @Bean
 open fun admin(connectionFactory: ConnectionFactory) = RabbitAdmin(connectionFactory).apply {
 afterPropertiesSet()
 }
 
 @Bean
 open fun privateQueue() = Queue("chat.private.${chatProperties().name}")
 
 @Bean
 open fun generalQueue() = Queue("chat.general.${chatProperties().name}")
 
 @Bean
 open fun chatProperties() = ChatProperties()
 
 @Bean
 open fun generalFanout() = FanoutExchange("chat.general")
 
 @Bean
 open fun binding(generalFanout: FanoutExchange,
 generalQueue: Queue): Binding = BindingBuilder.bind(generalQueue).to(generalFanout)

More Decks by Mario Arias

Other Decks in Programming