Monday, February 26, 2018

Spring Boot 2 Applications and OAuth 2 - Legacy Approach

This post is the second part of a 3 post series exploring ways to enable SSO with an OAuth2 provider for Spring Boot 2 based applications. The 3 posts are:

1. Ways to bootstrap an OpenID Connect compliant OAuth2 Authorization Server/OpenID Provider
2. Legacy Spring Boot/Spring 5 approach to integrating with an OAuth2 Authorization Server/OpenID Provider - this post
3. Newer Spring Boot 2/Spring 5 approach to integrating with an OAuth2 Authorization Server/OpenID Connect Provider


The post will explore a legacy Spring Boot 2/Spring Security 5 approach to enabling OAuth2 based authentication mechanism for an application, this post assumes that all the steps in the previous blog post have been followed and UAA is up and running.

A question that probably comes to mind is why I am talking about legacy in the context of Spring Boot 2/Spring Security 5 when this should have been the new way of doing SSO! The reason is, as developers we have been using an approach with Spring Boot 1.5.x that is now considered deprecated, there are features in it however that has not been completely ported over to the new approach(ability to spin up an OAuth2 authorization server and ability to create an OAuth2 resource server are examples), in the interim, Spring Security developers(thanks Rob Winch & Joe Grandja) provided a bridge to the legacy approach in the form of a spring-security-oauth2-boot project.

Approach

So what does the legacy approach look like - I have detailed it once before here, to recap it works on the basis of an annotation called @EnableOAuth2SSO and a set of properties supporting this annotation, a sample security configuration looks like this -

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@EnableOAuth2Sso
@Configuration
public class OAuth2SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);

        web.ignoring()
           .mvcMatchers("/favicon.ico", "/webjars/**", "/css/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();

        http.authorizeRequests()
                .antMatchers("/secured/**")
                    .authenticated()
                .antMatchers("/")
                    .permitAll()
                .anyRequest()
                    .authenticated();
    }

}

and the set of supporting properties to point to the UAA is the following:

ssoServiceUrl: http://localhost:8080/uaa

security:
  oauth2:
    client:
      client-id: client1
      client-secret: client1
      access-token-uri: ${ssoServiceUrl}/oauth/token
      user-authorization-uri: ${ssoServiceUrl}/oauth/authorize
    resource:
      jwt:
        key-uri: ${ssoServiceUrl}/token_key
      user-info-uri: ${ssoServiceUrl}/userinfo


With the spring-security-oauth2-boot project pulled in as a dependency:

compile 'org.springframework.cloud:spring-cloud-starter-oauth2'
compile("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.0.0.BUILD-SNAPSHOT")

these annotations just work for a Spring Boo2 application also. Note however Spring Boot 2 supports two distinct Web Frameworks - Spring Web and Spring Webflux, this approach pulls in Spring Web transitively which forces Spring Web as the default framework.

The sample in its entirety with ways to start it up is available in my github repo here - https://github.com/bijukunjummen/oauth2-boot2


Testing

Any uri starting with "/secured/**" is SSO enabled, if the index page is accessed it is displayed without needing any authentication:



Now, clicking through to a uri starting with "/secured/**" should trigger a OAuth2 Authorization Code flow:


and should present a login screen to the user via UAA:



Logging in with the credentials that were created before - user1/user1 should redirect the user back to the Spring Boot 2 legacy version of the app and should display the secured page:




This completes the legacy approach to SSO with Spring Boot 2. Note that this is just pseudo-authentication, OAuth2 is meant more for authorization to access a users resource than authentication the way it is used here. An article which clarifies this is available here. The next post with native Spring Security 5/Spring Boot2 will provide a cleaner authentication mechanism using OpenID Connect.

Sunday, February 11, 2018

Spring Boot 2 Applications and OAuth 2 - Setting up an Authorization Server

This will be a 3 post series exploring ways to enable SSO with an OAuth2 provider for Spring Boot 2 based applications. I will cover the following in these posts:

1. Ways to bootstrap an OpenID Connect compliant OAuth2 Authorization Server/OpenID Provider - this post
2. Legacy Spring Boot/Spring 5 approach to integrating with an OAuth2 Authorization Server/OpenID Provider
3. Newer Spring Boot 2/Spring 5 approach to integrating with an OAuth2 Authorization Server/OpenID Provider

This post will cover ways to bootstrap an OpenID Connect compliant OAuth2 Authorization Server running on a local machine.

The post is essentially a rehash of an earlier post which went into details of bootstrapping an OAuth2 authorization server using the excellent Cloud Foundry UAA project. There are a few changes since my previous post and I wanted to capture afresh the steps to bring up an Authorization server with a little more emphasis on changes to make it OpenID Connect compliant.

The best way to get a local version of a robust OAuth2 Authorization server running is to use the excellent Cloud Foundry UAA project.

Step 1: Clone the project:

git clone https://github.com/cloudfoundry/uaa

Step 2: Generate a keypair
UAA can make use of an asymmetric RSA keypair for signing and let clients verify the signature. I have a handy script available here which generates a keypair and generates a configuration file that can used for bootstrapping UAA:



When run this creates a UAA configuration which looks like this:

jwt:
   token:
      signing-key: |
       -----BEGIN RSA PRIVATE KEY-----
       MIIEpAIBAAKCAQEAuE5Ds...5Nka1vOTnjDgKIfsN
       NTAI25qNNCZOXXnGp71gMWsXcLFq4JDJTovL4/rzPIip/1xU0LjFSw==
       -----END RSA PRIVATE KEY-----
      verification-key: |
       -----BEGIN PUBLIC KEY-----
       MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuE5DsCmjfvWArlCIOL6n
       ZwIDAQAB
       -----END PUBLIC KEY-----

Step 3: Use the configuration to start up the UAA server:

UAA_CONFIG_URL=file://$PWD/uaa_config.yml ./gradlew run 

Step 4: Validate
A quick way to validate if the UAA has started up is to check the JWKS_URI, this is an endpoint which exposes the set of verification keys that a client can use to validate the token. For UAA, this is available at "/token_keys" endpoint, with either curl or httpie this endpoint can be validated:

http GET http://localhost:8080/uaa/token_keys

# OR

curl http://localhost:8080/uaa/token_keys

if things are configured okay, an output of the following form is expected from this endpoint:

{
    "keys": [
        {
            "alg": "RS256",
            "e": "AQAB",
            "kid": "legacy-token-key",
            "kty": "RSA",
            "n": "APLeBV3dcUrWuVEXRyFzNaOTeKOLwFjscxbWFGofCkxrp3r0nRbBBb4ElG4qYzmbStg5o-zXAPCOu7Pqy2j4PtC3OxLHWnKsflNOEWTeXhLkPE0IptHPbc6zgVPP3EoiG_umpm0BYeJPZZc-7tA11uU_3NqidY9wnpOgKBuwNmdoyUrjb4fBDoMr_Wk2_sn_mtHSG8HaX8eJ9SbC9xRCJySjJDApOYR_dKjuwpbcM2ITfbTzD9M2J7yOtoJRkFhd1Ug2t_6AA_z47BBws-x9BBfSNbYGsVlDAbe6NK_jUE",
            "use": "sig",
            "value": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8t4FXd1xSta5URdHIXM1\no5N4o4vAWOxzFtYUah8KTGunevSdFsEFvgSUbipjOZtK2Dmj7NcA8I67s+rLaPg+\n0Lc7Esdacqx+U04RZN5eEuQ8TQim0c9tzrOBU8/cSiIb+6ambQF62glGQWF3VSDa3/oAD/PjsEHCz7H0EF9I1tgaxWUMBt7o0r+N\nQQIDAQAB\n-----END PUBLIC KEY-----"
        }
    ]
}



Step 5: Populate Data
UAA has a companion CLI application called uaac, available here. Assuming that you have the uaac cli downloaded and UAA started up at its default port of 8080, let us start by pointing the uaac to the uaa application:

uaac target http://localhost:8080/uaa

and log into it using one of the canned client credentials(admin/adminsecret):

uaac token client get admin -s adminsecret

Now that a client has logged in, the token can be explored using :

uaac token decode

which should display the details of the logged in client:

jti: 4457847692b7464ca0320f08271a9e98
  sub: admin
  authorities: clients.read clients.secret clients.write uaa.admin clients.admin scim.write scim.read
  scope: clients.read clients.secret clients.write uaa.admin clients.admin scim.write scim.read
  client_id: admin
  cid: admin
  azp: admin
  grant_type: client_credentials
  rev_sig: 3c12911
  iat: 1518332992
  exp: 1518376192
  iss: http://localhost:8080/uaa/oauth/token
  zid: uaa

the raw jwt token can be obtained using the following command:

uaac context


with an output which looks like this:

[3]*[http://localhost:8080/uaa]
  skip_ssl_validation: true

  [2]*[admin]
      client_id: admin
      access_token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImxlZ2FjeS10b2tlbi1rZXkiLCJ0eXAiOiJKV1QifQ.eyJqdGkiOiI0NDU3ODQ3NjkyYjc0NjRjYTAzMjBmMDgyNzFhOWU5OCIsInN1YiI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiY2xpZW50cy5yZWFkIiwiY2xpZW50cy5zZWNyZXQiLCJjbGllbnRzLndyaXRlIiwidWFhLmFkbWluIiwiY2xpZW50cy5hZG1pbiIsInNjaW0ud3JpdGUiLCJzY2ltLnJlYWQiXSwic2NvcGUiOlsiY2xpZW50cy5yZWFkIiwiY2xpZW50cy5zZWNyZXQiLCJjbGllbnRzLndyaXRlIiwidWFhLmFkbWluIiwiY2xpZW50cy5hZG1pbiIsInNjaW0ud3JpdGUiLCJzY2ltLnJlYWQiXSwiY2xpZW50X2lkIjoiYWRtaW4iLCJjaWQiOiJhZG1pbiIsImF6cCI6ImFkbWluIiwiZ3JhbnRfdHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyIsInJldl9zaWciOiIzYzEyOTExIiwiaWF0IjoxNTE4MzMyOTkyLCJleHAiOjE1MTgzNzYxOTIsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODA4MC91YWEvb2F1dGgvdG9rZW4iLCJ6aWQiOiJ1YWEiLCJhdWQiOlsic2NpbSIsImNsaWVudHMiLCJ1YWEiLCJhZG1pbiJdfQ.ZEcUc4SvuwQYwdE0OeG5-l8Jh1HsP0JFI3aCob8A1zOcGOGjqso4j1-k_Lzm__pGZ702v4_CkoXOBXoqaaRbfVgJybBvOWbWsUZupMVMlEsyaR_j8DWY8utFAIiN2EsQgjG3qLrsf0K8lm0I3_UIEjaNZhSkWSLDLyY9wr_2SRanSf8LkcEJoSTTgDdO0aP8MvwNpDG7iQ2Om1HZEN08Bed1hHj6e1E277d9Kw7gutgCBht5GZDPFnI6Rjn0O5wimgrAa6FEDjdCpR7hy2P5RiOTcTvjj3rXtVJyVcQcxGKymZrY2WOx1mIEzEIAj8NYlw0TLuSVVOiNZ9fKlRiMpw
      token_type: bearer
      expires_in: 43199
      scope: clients.read clients.secret clients.write uaa.admin clients.admin scim.write scim.read
      jti: 4457847692b7464ca0320f08271a9e98

Finally to add a client with creds of client1/client1 and a user with a creds of user1/user1:

uaac client add client1 \
   --name client1 \
   --scope resource.read,resource.write,openid,profile,email,address,phone \
   -s client1 \
   --authorized_grant_types authorization_code,refresh_token,client_credentials,password \
   --authorities uaa.resource \
   --redirect_uri http://localhost:8888/**


# Add a user called user1/user1
uaac user add user1 -p user1 --emails user1@user1.com


# Add two scopes resource.read, resource.write
uaac group add resource.read
uaac group add resource.write

# Assign user1 both resource.read, resource.write scopes..
uaac member add resource.read user1
uaac member add resource.write user1


At this point we have a working Authorization Server with a sample client and a sample user available. The subsequent posts will make use of this data to enable authentication for a Sample Spring Boot2 application. I will update the links in this post as I complete the newer posts.


Correction

Previously I had defined only "openid" scope as the scope that a client requests on behalf of a user. OpenID Connect requires other scopes to be able to call the userinfo endpoint, the updated client definition looks like this:

uaac client add client1 \
   --name client1 \
   --scope resource.read,resource.write,openid,profile,email,address,phone \
   -s client1 \
   --authorized_grant_types authorization_code,refresh_token,client_credentials,password \
   --authorities uaa.resource \
   --redirect_uri http://localhost:8888/**

Sunday, January 21, 2018

Kotlin - Reified type parameters sample

This post walks through a sample that demonstrates Kotlin's ability to cleverly reify generic type parameters.

So consider first a world where Kotlin does not support this feature, if we were using the Jackson library to convert a JSON to a Map with String based keys and Integer based values, I would use a code along these lines:

@Test
fun `sample parameterized retrieval raw object mapper`() {
    val objectMapper = ObjectMapper()
    val map: Map<String, Int> = objectMapper.readValue("""
        | {
        |   "key1": 1,
        |   "key2": 2,
        |   "key3": 3
        | }
        """.trimMargin(), object : TypeReference<Map<String, Int>>() {})

    assertThat(map).isEqualTo(mapOf("key1" to 1, "key2" to 2, "key3" to 3))
}

TypeReference used above implements a pattern called Super type token which allows the type of a parameterized type to be captured by sub-classing. Note the ugly way to creating an anonymous sub-class in Kotlin.

object : TypeReference<Map<String, Int>>() {}


What I would like to do is to invoke the ObjectMapper the following way instead:

@Test
fun `sample parameterized retrieval`() {
    val om = ObjectMapper()
    val map: Map<String, Int> = om.readValue("""
        | {
        |   "key1": 1,
        |   "key2": 2,
        |   "key3": 3
        | }
        """.trimMargin())

    assertThat(map).isEqualTo(mapOf("key1" to 1, "key2" to 2, "key3" to 3))
}

The generic type parameter is being inferred based on the type of what is to be returned (the left-hand side).


This can be achieved using an extension function on ObjectMapper which looks like this:

inline fun <reified T> ObjectMapper.readValue(s: String): T = 
        this.readValue(s, object : TypeReference<T>() {})

The inline function is the heart of the support for being able to reify generic type parameter here - after compilation, the function would be expanded out into any place this function is called and thus the second version is exactly same as the first version of the test but reads far better than before.


Note that Jackson already implements these Kotlin extension functions in the excellent jackson-module-kotlin library.

Thursday, January 4, 2018

Spring Based Application - Migrating to Junit 5

This is a quick write-up on migrating a Gradle based Spring Boot app from Junit 4 to the shiny new Junit 5. Junit 4 tests continue to work with Junit 5 Test Engine abstraction which provides support for tests written in different programming models, in this instance, Junit 5 supports a Vintage Test Engine with the ability to run JUnit 4 tests.


Here is a sample project with JUnit 5 integrations already in place along with sample tests in Junit 4 and Junit 5 - https://github.com/bijukunjummen/boot2-with-junit5-sample

Sample Junit 4 candidate test

As a candidate project, I have a Spring Boot 2 app with tests written in Kotlin using Junit 4 as the testing framework. This is how a sample test looks with all dependencies explicitly called out. It uses the Junit4's @RunWith annotation to load up the Spring Context:

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.reactive.server.WebTestClient
import java.nio.charset.StandardCharsets

@RunWith(SpringRunner::class)
@WebFluxTest(controllers = arrayOf(RouteConfig::class))
class SampleJunit4Test {

    @Autowired
    lateinit var webTestClient: WebTestClient

    @Test
    fun `get of hello URI should return Hello World!`() {
        webTestClient.get()
                .uri("/hello")
                .exchange()
                .expectStatus().isOk
                .expectBody()
                .consumeWith({ m ->
                    assertThat(String(m.responseBodyContent, StandardCharsets.UTF_8)).isEqualTo("Hello World!")
                })

    }

}

the Junit 4 dependencies are pulled in transitively via "spring-boot-starter-test" module:

testCompile('org.springframework.boot:spring-boot-starter-test')


Junit 5 migration


The first step to do is to pull in the Junit 5 dependencies along with Gradle plugin which enables running the tests:

Plugin:

buildscript {
 dependencies {
  ....
  classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2'
 }
}
apply plugin: 'org.junit.platform.gradle.plugin'

Dependencies:

testCompile("org.junit.jupiter:junit-jupiter-api")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
testRuntime("org.junit.vintage:junit-vintage-engine:4.12.2")

With these changes in place, all the Junit 4 tests will continue to run both in IDE and when the Gradle build is executed and at this point, the tests itself can be slowly migrated over.

The test which I had shown before looks like this with Junit 5 Jupiter which provides the programming model for the tests:

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.reactive.server.WebTestClient
import java.nio.charset.StandardCharsets

@ExtendWith(SpringExtension::class)
@WebFluxTest(controllers = arrayOf(RouteConfig::class))
class SampleJunit5Test {

    @Autowired
    lateinit var webTestClient: WebTestClient

    @Test
    fun `get of hello URI should return Hello World!`() {
        webTestClient.get()
                .uri("/hello")
                .exchange()
                .expectStatus().isOk
                .expectBody()
                .consumeWith({ m ->
                    assertEquals("Hello World!", String(m.responseBodyContent, StandardCharsets.UTF_8))
                })
    }

}

Note that now instead of using JUnit 4 @RunWith annotation, I am using the @ExtendWith annotation and providing SpringExtension as a parameter which is responsible for loading up the Spring Context like before. Rest of the Spring annotations will continue to work with JUnit 5. This way tests can be slowly moved over from JUnit 4 to JUnit 5.


Caveats

Not everything is smooth though, there are a few issues in migrating from JUnit 4 to JUnit 5, the biggest of them is likely the support for JUnit @Rule and @ClassRule annotation and the JUnit 5 documentation does go into details on how it can be mitigated.

Monday, January 1, 2018

Kotlin - Tuple type

It is very simple to write a Tuple type with the expressiveness of Kotlin. My objective expressed in tests is the following:

1. Be able to define a Tuple of up to 5 elements and be able to retrieve the elements using an index like placeholder, in a test expressed with 2 elements like this:

val tup = Tuple("elem1", "elem2")
assertThat(tup._1).isEqualTo("elem1")
assertThat(tup._2).isEqualTo("elem2")

2. Be able to de-construct the constituent types along the following lines:
val tup = Tuple("elem1", "elem2")
val (e1, e2) = tup

assertThat(e1).isEqualTo("elem1")
assertThat(e2).isEqualTo("elem2")


Implementation

The implementation for a tuple of 2 elements is the following in its entirity:

data class Tuple2<out A, out B>(val _1: A, val _2: B)

A Kotlin data class provides all the underlying support of being able to retrieve the individual fields and the ability to destructure using an expression like this:

val (e1, e2) = Tuple2("elem1", "elem2")

All that I need to do at this point is provide a helper that creates a tuple of appropriate size based on the number of arguments provided which I have defined as follows:

object Tuple {
    operator fun <A> invoke(_1: A): Tuple1<A> = Tuple1(_1)
    operator fun <A, B> invoke(_1: A, _2: B): Tuple2<A, B> = Tuple2(_1, _2)
    operator fun <A, B, C> invoke(_1: A, _2: B, _3: C): Tuple3<A, B, C> = Tuple3(_1, _2, _3)
    operator fun <A, B, C, D> invoke(_1: A, _2: B, _3: C, _4: D): Tuple4<A, B, C, D> = Tuple4(_1, _2, _3, _4)
    operator fun <A, B, C, D, E> invoke(_1: A, _2: B, _3: C, _4: D, _5: E): Tuple5<A, B, C, D, E> = Tuple5(_1, _2, _3, _4, _5)
}

which allows me to define tuples of different sizes using a construct which looks like this:

val tup2 = Tuple("elem1", "elem2")
val tup3 = Tuple("elem1", "elem2", "elem3")
val tup4 = Tuple("elem1", "elem2", "elem3", "elem4")

A little more twist, typically a Pair type is an alias for Tuple with 2 elements and Triple is an alias for a Tuple of 3 elements, this can be trivially defined in Kotlin the following way:

typealias Pair<A, B> = Tuple2<A, B>
typealias Triple<A, B, C> = Tuple3<A, B, C>

Simple indeed! a more filled-in sample is available in my github repo here - https://github.com/bijukunjummen/kfun

Tuesday, December 26, 2017

Kotlin - Try type for functional exception handling

Scala has a Try type to functionally handle exceptions. I could get my head around using this type using the excellent Neophyte's guide to Scala by Daniel Westheide. This post will replicate this type using Kotlin.


Background


Consider a simple function which takes two String, converts them to integer and then divides them(sample based on scaladoc of Try) :

fun divide(dividend: String, divisor: String): Int {
     val num = dividend.toInt()
     val denom = divisor.toInt()
     return num / denom
 }

It is the callers responsibility to ensure that any exception that is propagated from this implementation is handled appropriately using the exception handling mechanism of Java/Kotlin:

try {
    divide("5t", "4")
} catch (e: ArithmeticException) {
    println("Got an exception $e")
} catch (e: NumberFormatException) {
    println("Got an exception $e")
}

My objective with the "Try" code will be to transform the "divide" to something which looks like this:

fun divideFn(dividend: String, divisor: String): Try<Int> {
    val num = Try { dividend.toInt() }
    val denom = Try { divisor.toInt() }
    return num.flatMap { n -> denom.map { d -> n / d } }
}

A caller of this variant of "divide" function will not have an exception to handle through a try/catch block, instead, it will get back the exception as a value which it can introspect and act on as needed.

val result = divideFn("5t", "4")
when(result) {
    is Success -> println("Got ${result.value}")
    is Failure -> println("An error : ${result.e}") 
}

Kotlin implementation

The "Try" type has two implementations corresponding to the "Success" path or a "Failure" path and implemented as a sealed class the following way:

sealed class Try<out T> {}

data class Success<out T>(val value: T) : Try<T>() {}

data class Failure<out T>(val e: Throwable) : Try<T>() {}

The "Success" type wraps around the successful result of an execution and "Failure" type wraps any exception thrown from the execution.

So now, to add some meat to these, my first test is to return one of these types based on a clean and exceptional implementation, along these lines:

val trySuccessResult: Try<Int> = Try {
    4 / 2
}
assertThat(trySuccessResult.isSuccess()).isTrue()


val tryFailureResult: Try<Int> = Try {
    1 / 0
}
assertThat(tryFailureResult.isFailure()).isTrue()

This can be achieved through a "companion object" in Kotlin, similar to static methods in Java, it returns either a Success type or a Failure type based on the execution of the lambda expression:

sealed class Try<out T> {
    ...    
    companion object {
        operator fun <T> invoke(body: () -> T): Try<T> {
            return try {
                Success(body())
            } catch (e: Exception) {
                Failure(e)
            }
        }
    }
    ...
}

Now that a caller has a "Try" type, they can check whether it is a "Success" type or a "Failure" type using the "when" expression like before, or using "isSuccess" and "isFailure" methods which are delegated to the sub-types like this:

sealed class Try<out T> {
    abstract fun isSuccess(): Boolean
    abstract fun isFailure(): Boolean
}

data class Success<out T>(val value: T) : Try<T>() {
    override fun isSuccess(): Boolean = true
    override fun isFailure(): Boolean = false
}

data class Failure<out T>(val e: Throwable) : Try<T>() {
    override fun isSuccess(): Boolean = false
    override fun isFailure(): Boolean = true
}

in case of Failure a default can be returned to the caller, something like this in a test:

val t1 = Try { 1 }

assertThat(t1.getOrElse(100)).isEqualTo(1)

val t2 = Try { "something" }
        .map { it.toInt() }
        .getOrElse(100)

assertThat(t2).isEqualTo(100)

again implemented by delegating to the subtypes:
sealed class Try<out T> {
  abstract fun get(): T
  abstract fun getOrElse(default: @UnsafeVariance T): T
  abstract fun orElse(default: Try<@UnsafeVariance T>): Try<T>
}

data class Success<out T>(val value: T) : Try<T>() {
    override fun getOrElse(default: @UnsafeVariance T): T = value
    override fun get() = value
    override fun orElse(default: Try<@UnsafeVariance T>): Try<T> = this
}

data class Failure<out T>(val e: Throwable) : Try<T>() {
    override fun getOrElse(default: @UnsafeVariance T): T = default
    override fun get(): T = throw e
    override fun orElse(default: Try<@UnsafeVariance T>): Try<T> = default
}


The biggest advantage of returning a "Try" type, however, is in chaining further operations on the type.

Chaining with map and flatMap

"map" operation is passed a lambda expression to transform the value in some form - possibly even to a different type:

val t1 = Try { 2 }

val t2 = t1.map({ it * 2 }).map { it.toString()}

assertThat(t2).isEqualTo(Success("4"))

Here a number is being doubled and then converted to a string. If the initial Try were a "Failure" then the final value will simply return the "Failure" along the lines of this test:

val t1 = Try {
    2 / 0
}

val t2 = t1.map({ it * 2 }).map { it * it }

assertThat(t2).isEqualTo(Failure<Int>((t2 as Failure).e))


Implementing "map" is fairly straightforward:

sealed class Try<out T> {
    fun <U> map(f: (T) -> U): Try<U> {
        return when (this) {
            is Success -> Try {
                f(this.value)
            }
            is Failure -> this as Failure<U>
        }
    }
}


flatmap, on the other hand, takes in a lambda expression which returns another "Try" type and flattens the result back into a "Try" type, along the lines of this test:

val t1 = Try { 2 }

val t2 = t1
        .flatMap { i -> Try { i * 2 } }
        .flatMap { i -> Try { i.toString() } }

assertThat(t2).isEqualTo(Success("4"))

Implementing this is simple too, along the following lines:

sealed class Try<out T> {
    fun <U> flatMap(f: (T) -> Try<U>): Try<U> {
        return when (this) {
            is Success -> f(this.value)
            is Failure -> this as Failure<U>
        }
    }
}

The "map" and "flatMap" methods are the power tools of this type, allowing chaining of complex operations together focusing on the happy path.


Conclusion

Try is a powerful type allowing a functional handling of exceptions in the code. I have a strawman implementation using Kotlin available in my github repo here - https://github.com/bijukunjummen/kfun

Monday, December 18, 2017

Kotlin - tail recursion optimization

Kotlin compiler optimizes tail recursive calls with a few catches. Consider a rank function to search for the index of an element in a sorted array, implemented the following way using tail recursion and a test for it:

fun rank(k: Int, arr: Array<Int>): Int {
    tailrec fun rank(low: Int, high: Int): Int {
        if (low > high) {
            return -1
        }
        val mid = (low + high) / 2

        return when {
            (k < arr[mid]) -> rank(low, mid)
            (k > arr[mid]) -> rank(mid + 1, high)
            else -> mid
        }
    }

    return rank(0, arr.size - 1)
}

@Test
fun rankTest() {
    val array = arrayOf(2, 4, 6, 9, 10, 11, 16, 17, 19, 20, 25)
    assertEquals(-1, rank(100, array))
    assertEquals(0, rank(2, array))
    assertEquals(2, rank(6, array))
    assertEquals(5, rank(11, array))
    assertEquals(10, rank(25, array))
}

IntelliJ provides an awesome feature to show the bytecode of any Kotlin code, along the lines of the following screenshot:



A Kotlin equivalent of the type of bytecode that the Kotlin compiler generates is the following:

fun rankIter(k: Int, arr: Array<Int>): Int {
    fun rankIter(low: Int, high: Int): Int {
        var lo = low
        var hi = high
        while (lo <= hi) {
            val mid = (lo + hi)/2

            if (k < arr[mid]) {
                hi = mid
            } else if (k > arr[mid]){
                lo = mid + 1
            } else {
                return mid
            }

        }
        return -1
    }

    return rankIter(0, arr.size - 1)
}

the tail calls have been translated to a simple loop.


There are a few catches that I could see though:
1. The compiler has to be explicitly told which calls are tail-recursive using the "tailrec" modifier
2. Adding tailrec modifier to a non-tail-recursive function does not generate compiler errors, though a warning does appear during compilation step