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
Tweet

More Decks by Mario Arias

Other Decks in Programming

Transcript

  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)