Skip to content

Instantly share code, notes, and snippets.

@brandonlamb
Created November 12, 2016 19:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brandonlamb/f298207d6fba264972eb2b0d7689b702 to your computer and use it in GitHub Desktop.
Save brandonlamb/f298207d6fba264972eb2b0d7689b702 to your computer and use it in GitHub Desktop.
Kotlin, Akka, Ehcache
package com.sbux.gred.location.api.model
import io.swagger.annotations.ApiModelProperty
data class LocationTime(
@ApiModelProperty(example = "7339", name = "locationNumber")
var locationNumber: Int? = 0,
@ApiModelProperty(example = "01:01:01", name = "time")
var time: String? = ""
)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sbux.gred.api</groupId>
<artifactId>location-time</artifactId>
<name>GRED Location Time v1</name>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<parent>
<groupId>com.sbux.gred</groupId>
<artifactId>parent</artifactId>
<version>1</version>
</parent>
<properties>
<gred.finalName>ROOT</gred.finalName>
<findbugs.skip>false</findbugs.skip>
<skipTests>true</skipTests>
<version.tomee>7.0.1</version.tomee>
<version.log4j2>2.6.2</version.log4j2>
<version.owner-java8>1.0.9</version.owner-java8>
<version.mybatis>3.4.1</version.mybatis>
<version.mybatis-cdi>1.0.0-beta3</version.mybatis-cdi>
<version.orika>1.5.0-beta0</version.orika>
<version.geronimo-json-1.0-spec>1.0-alpha-1</version.geronimo-json-1.0-spec>
<version.johnzon>0.9.3-incubating</version.johnzon>
<version.junit>4.12</version.junit>
<version.baseplate>1.0-SNAPSHOT</version.baseplate>
<!--<version.kotlin>1.1-M02</version.kotlin>-->
<version.kotlin>1.0.5</version.kotlin>
<version.kdoc>0.12.1230</version.kdoc>
<version.kotlin-reflect>1.0.3</version.kotlin-reflect>
<version.rxkotlin>0.60.0</version.rxkotlin>
<version.kotlin-logging>1.3.2</version.kotlin-logging>
<version.kotlin-kovenant>3.3.0</version.kotlin-kovenant>
<version.kotlin-kwery>0.13</version.kotlin-kwery>
<version.db2>10.5.3</version.db2>
<version.akka_actor>2.4.12</version.akka_actor>
<version.quasar>0.7.6</version.quasar>
<docker.from>jboss/base-jdk:8</docker.from>
<docker.maven.plugin.version>0.13.9</docker.maven.plugin.version>
<docker.assemblyDescriptorRef>artifact</docker.assemblyDescriptorRef>
<docker.image>myfear/swarm-sample:latest</docker.image>
</properties>
<scm>
<connection>scm:git:git@github.starbucks.net:gred-platform/item-api-v1.git</connection>
<url>scm:git:git@github.starbucks.net:gred-platform/item-api-v1.git</url>
<developerConnection>scm:git:git@github.starbucks.net:gred-platform/item-api-v1.git</developerConnection>
<tag>HEAD</tag>
</scm>
<dependencies>
<!-- Java EE 7 dependency -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.2.0.beta1</version>
</dependency>
<!-- TomEE -->
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jaxrs</artifactId>
<version>${version.johnzon}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-mapper</artifactId>
<version>${version.johnzon}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-json_1.0_spec</artifactId>
<version>${version.geronimo-json-1.0-spec}</version>
<scope>provided</scope>
</dependency>
<!-- Kotlin -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${version.kotlin}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
<version>${version.kotlin-reflect}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>io.reactivex</groupId>-->
<!--<artifactId>rxkotlin</artifactId>-->
<!--<version>${version.rxkotlin}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>com.github.andrewoma.kwery</groupId>-->
<!--<artifactId>core</artifactId>-->
<!--<version>${version.kotlin-kwery}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.jetbrains.kotlinx</groupId>-->
<!--<artifactId>kotlinx-coroutines-generate</artifactId>-->
<!--<version>0.1-alpha</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.jetbrains.kotlinx</groupId>-->
<!--<artifactId>kotlinx-coroutines-async</artifactId>-->
<!--<version>0.1-alpha</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${version.kotlin}</version>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>org.jetbrains.kotlinx</groupId>-->
<!--<artifactId>kotlinx-coroutines-rx</artifactId>-->
<!--<version>0.1-alpha</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>nl.komponents.kovenant</groupId>-->
<!--<artifactId>kovenant</artifactId>-->
<!--<version>${version.kotlin-kovenant}</version>-->
<!--<type>pom</type>-->
<!--</dependency>-->
<!-- Logging -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${version.log4j2}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${version.log4j2}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jul</artifactId>
<version>${version.log4j2}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>${version.log4j2}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>${version.log4j2}</version>
<scope>provided</scope>
</dependency>
<!-- Akka -->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.12</artifactId>
<version>${version.akka_actor}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>co.paralleluniverse</groupId>-->
<!--<artifactId>quasar-actors</artifactId>-->
<!--<version>${version.quasar}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>co.paralleluniverse</groupId>-->
<!--<artifactId>quasar-core</artifactId>-->
<!--<version>${version.quasar}</version>-->
<!--<classifier>jdk8</classifier>-->
<!--<scope>provided</scope>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>co.paralleluniverse</groupId>-->
<!--<artifactId>quasar-kotlin</artifactId>-->
<!--<version>${version.quasar}</version>-->
<!--</dependency>-->
<!-- Swagger -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<version>1.5.10</version>
<exclusions>
<!-- API are useless since in javaee-api -->
<exclusion>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</exclusion>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
<!-- not useful for the GUI -> only json for us -->
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
<exclusion>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
</exclusion>
<!-- we don't use reflections so no need of javassist -->
<exclusion>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</exclusion>
<exclusion>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
</exclusion>
<!-- we'll reuse the container one or add an impl as well to your app -->
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>swagger-ui</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.8.3</version>
</dependency>
<!-- GRED common libs -->
<!--<dependency>-->
<!--<groupId>com.sbux.gred.baseplate</groupId>-->
<!--<artifactId>mybatis</artifactId>-->
<!--<version>${version.baseplate}</version>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>com.sbux.gred.baseplate</groupId>-->
<!--<artifactId>orika</artifactId>-->
<!--<version>${version.baseplate}</version>-->
<!--</dependency>-->
<dependency>
<groupId>com.sbux.gred.baseplate</groupId>
<artifactId>ping</artifactId>
<version>${version.baseplate}</version>
</dependency>
<!-- Third Party -->
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>6.13</version>
<scope>test</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>com.fasterxml.jackson.module</groupId>-->
<!--<artifactId>jackson-module-kotlin</artifactId>-->
<!--<version>2.8.3</version>-->
<!--</dependency>-->
</dependencies>
<build>
<finalName>${gred.finalName}</finalName>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/it/kotlin</testSourceDirectory>
<testResources>
<testResource>
<directory>src/it/resources</directory>
<filtering>true</filtering>
</testResource>
</testResources>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>conf/**.*</exclude>
</excludes>
</resource>
<resource>
<directory>${project.build.directory}/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<version>${version.kotlin}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kdoc-maven-plugin</artifactId>
<version>${version.kdoc}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>co.paralleluniverse</groupId>
<artifactId>quasar-core</artifactId>
<version>${version.quasar}</version>
<classifier>jdk8</classifier>
<type>jar</type>
<outputDirectory>target/dependency</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.sbux.gred</groupId>
<artifactId>gredconfig-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<mergeFiles>
<mergeFile>
<targetFile>${project.build.directory}/resources/conf/dev.properties</targetFile>
<propsFiles>
<propsFile>${project.basedir}/src/main/resources/conf/dev.properties/</propsFile>
<propsFile>${project.basedir}/src/main/resources/conf/base.properties</propsFile>
</propsFiles>
</mergeFile>
<mergeFile>
<targetFile>${project.build.directory}/resources/conf/test.properties</targetFile>
<propsFiles>
<propsFile>${project.basedir}/src/main/resources/conf/test.properties</propsFile>
<propsFile>${project.basedir}/src/main/resources/conf/base.properties</propsFile>
</propsFiles>
</mergeFile>
<mergeFile>
<targetFile>${project.build.directory}/resources/conf/cert.properties</targetFile>
<propsFiles>
<propsFile>${project.basedir}/src/main/resources/conf/cert.properties/</propsFile>
<propsFile>${project.basedir}/src/main/resources/conf/base.properties</propsFile>
</propsFiles>
</mergeFile>
<mergeFile>
<targetFile>${project.build.directory}/resources/conf/stage.properties</targetFile>
<propsFiles>
<propsFile>${project.basedir}/src/main/resources/conf/stage.properties/</propsFile>
<propsFile>${project.basedir}/src/main/resources/conf/base.properties</propsFile>
</propsFiles>
</mergeFile>
<mergeFile>
<targetFile>${project.build.directory}/resources/conf/prod.properties</targetFile>
<propsFiles>
<propsFile>${project.basedir}/src/main/resources/conf/prod.properties</propsFile>
<propsFile>${project.basedir}/src/main/resources/conf/base.properties</propsFile>
</propsFiles>
</mergeFile>
</mergeFiles>
</configuration>
<executions>
<execution>
<goals>
<goal>generateconfigs</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.tomee.maven</groupId>
<artifactId>tomee-maven-plugin</artifactId>
<version>${version.tomee}</version>
<configuration>
<systemVariables>
<openejb.classloader.forced-load>io.swagger.</openejb.classloader.forced-load>
<!--<log4j.configurationFile>${project.basedir}/src/main/resources/conf/log4j2.xml</log4j.configurationFile>-->
<!--<log4j.debug>true</log4j.debug>-->
<Xmx>128m</Xmx>
<Xms>64m</Xms>
</systemVariables>
<simpleLog>true</simpleLog>
<!--<tomeeGroupId>org.apache.tomee</tomeeGroupId>-->
<tomeeGroupId>com.sbux.gred</tomeeGroupId>
<tomeeArtifactId>apache-tomee</tomeeArtifactId>
<tomeeClassifier>plus</tomeeClassifier>
<tomeeVersion>${version.tomee}</tomeeVersion>
<tomeeHttpPort>8080</tomeeHttpPort>
<webappDefaultConfig>false</webappDefaultConfig>
<libs>
<!--<lib>remove:batchee</lib>-->
<!--<lib>com.ibm.db2.jcc:db2jcc4:${version.db2}</lib>-->
<!--<lib>com.ibm.db2.jcc:db2jcc_license_cisuz:${version.db2}</lib>-->
<!--<lib>org.apache.logging.log4j:log4j-core:${version.log4j2}</lib>-->
<!--<lib>org.apache.logging.log4j:log4j-jul:${version.log4j2}</lib>-->
<!--<lib>org.apache.logging.log4j:log4j-jcl:${version.log4j2}</lib>-->
<!--<lib>org.apache.logging.log4j:log4j-api:${version.log4j2}</lib>-->
<!--<lib>org.apache.logging.log4j:log4j-web:${version.log4j2}</lib>-->
<!--<lib>org.elasticsearch:elasticsearch:${version.elasticsearch}</lib>-->
</libs>
</configuration>
</plugin>
<!--<plugin>-->
<!--<groupId>org.jolokia</groupId>-->
<!--<artifactId>docker-maven-plugin</artifactId>-->
<!--<version>0.13.9</version>-->
<!--</plugin>-->
</plugins>
</build>
</project>
package com.sbux.gred.location.api.controllers
import akka.actor.ActorRef
import akka.actor.ActorSystem
import akka.pattern.PatternsCS.ask
import akka.routing.RoundRobinPool
import akka.util.Timeout
import com.sbux.gred.location.api.model.LocationTime
import com.sbux.gred.location.service.TimeService
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import org.ehcache.Cache
import org.ehcache.config.builders.CacheConfigurationBuilder
import org.ehcache.config.builders.CacheManagerBuilder
import org.ehcache.config.builders.ResourcePoolsBuilder
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import javax.annotation.PostConstruct
import javax.annotation.Resource
import javax.enterprise.context.ApplicationScoped
import javax.sql.DataSource
import javax.ws.rs.Consumes
import javax.ws.rs.GET
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.container.AsyncResponse
import javax.ws.rs.container.Suspended
import javax.ws.rs.core.MediaType.APPLICATION_JSON
import javax.ws.rs.core.Response
import javax.ws.rs.PathParam as p
@ApplicationScoped
@Path("/time")
@Consumes(APPLICATION_JSON)
@Produces(APPLICATION_JSON)
@Api(
value = "/time",
description = "Location Time Service",
tags = arrayOf("location", "time")
)
open class TimeController {
private val actorSystem = ActorSystem.create("locationTime")
private lateinit var timeService: ActorRef
private val executorService = Executors.newFixedThreadPool(5)
@Resource(name = "java:/comp/env/jdbc/IbmDb2") private lateinit var ds: DataSource
@PostConstruct
open fun init() {
val cacheManager = CacheManagerBuilder
.newCacheManagerBuilder()
.withCache(
"utcOffset",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
Any::class.java,
Any::class.java,
ResourcePoolsBuilder.heap(20000)
).build()
).build()
cacheManager.init()
val cache: Cache<Any, Any> = cacheManager.getCache("utcOffset", Any::class.java, Any::class.java)
timeService = actorSystem.actorOf(
RoundRobinPool(40).props(
TimeService
.props(ds, cache)
.withDispatcher("akka-time.blocking-dispatcher")
),
"TimeService"
)
}
@GET
@Path("{locationNumber: \\d+}")
@ApiOperation(
value = "Get the local time at the specified location",
notes = "Returns the local time at a location",
response = LocationTime::class
)
open fun getTime(@p("locationNumber") locationNumber: Int, @Suspended res: AsyncResponse) {
executorService.submit {
ask(timeService, TimeService.FindById(locationNumber), Timeout(5, TimeUnit.SECONDS)).thenAccept {
res.resume(Response.ok(it).build())
}
}
}
}
package com.sbux.gred.location.service
import akka.actor.AbstractLoggingActor
import akka.actor.Props
import akka.japi.pf.ReceiveBuilder
import com.sbux.gred.location.api.model.LocationTime
import org.ehcache.Cache
import java.time.OffsetDateTime
import java.time.ZoneOffset
import javax.sql.DataSource
class TimeService(private val ds: DataSource, private val cache: Cache<Any, Any>) : AbstractLoggingActor() {
data class FindById(internal val locationNumber: Int)
init {
receive(ReceiveBuilder.match(FindById::class.java, { findById(it) }).build())
}
private fun findById(message: FindById) {
val x = cache.get(message.locationNumber)
val offset = if (x != null) x as Int else getOffset(message.locationNumber)
sender().tell(
LocationTime(
message.locationNumber,
OffsetDateTime.now().atZoneSameInstant(
ZoneOffset.ofHoursMinutes(offset / 100, offset % 100)
).toString()
),
self()
)
}
private fun getOffset(locationNumber: Int): Int {
println("Cache miss $locationNumber")
val sql = """
SELECT utc_offset from location where location_number = ?
"""
val offset = trywr(ds.connection) {
val ps = it.prepareStatement(sql)
ps.setInt(1, locationNumber)
val rs = ps.executeQuery()
rs.next()
val offset = rs.getInt("UTC_OFFSET")
ps.close()
offset
}
cache.put(locationNumber, offset)
return offset
}
companion object {
/**
* Properties factory method
* @param ds DataSource
* @return Props
*/
fun props(ds: DataSource, cache: Cache<Any, Any>) = Props.create(TimeService::class.java, ds, cache)
}
}
inline fun <T : AutoCloseable, R> trywr(closeable: T, block: (T) -> R): R {
try {
return block(closeable)
} finally {
closeable.close()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment