Sunday, 1 May 2016

Scala, Maven, jOOQ, Scalatra

This post is about sharing some wonderful experience: using Scala, Scalatra, jOOQ altogether makes me a happy developer.
Now, there is nothing sexy about this post - it is not cutting edge, there is no big data involved, no AI, just simple tools that I think make a software engineer's life happier and fun.
I have embraced some parts of functional programming in Java 8 - sometimes it is quite difficult to read and debug - but it is clear and concise - less error prone - you know the usual shebang.
I still think that Scala has an edge over Java 8 - but I must say that I cannot wait for Scala 2.12 - as the binary compatibility and compilation speed are big issues.
The Scala IDE is great - but not there yet if you ask me, and I would hate to go and use IntelliJ - just not the tool for me.

This post is about using Scala with Maven, and some really cool stuff using Scalatra and jOOQ to build a simple RESTful server.

pom.xml

All you need for dependencies in one epic pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>Pento</groupId>
    <artifactId>PentoPay</artifactId>
    <version>1.0</version>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>net.alchim31.maven</groupId>
                    <artifactId>scala-maven-plugin</artifactId>
                    <version>3.2.1</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>2.0.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>scala-compile-first</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>add-source</goal>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>scala-test-compile</id>
                        <phase>process-test-resources</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.jooq</groupId>
            <artifactId>jooq</artifactId>
            <version>3.7.3</version>
        </dependency>

        <dependency>
            <groupId>org.jooq</groupId>
            <artifactId>jooq-meta</artifactId>
            <version>3.7.3</version>
        </dependency>

        <dependency>
            <groupId>org.jooq</groupId>
            <artifactId>jooq-codegen</artifactId>
            <version>3.7.3</version>
        </dependency>

        <dependency>
            <groupId>org.jooq</groupId>
            <artifactId>jooq-scala</artifactId>
            <version>3.7.3</version>
        </dependency>

        <dependency>
            <groupId>org.scalatra</groupId>
            <artifactId>scalatra_2.11</artifactId>
            <version>2.4.0</version>
        </dependency>

        <dependency>
            <groupId>com.google.inject</groupId>
            <artifactId>guice</artifactId>
            <version>4.0</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.3.8.v20160314</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>9.3.8.v20160314</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.21</version>
        </dependency>

        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>10.12.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derbyclient</artifactId>
            <version>10.12.1.1</version>
        </dependency>

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>2.4.5</version>
        </dependency>

    </dependencies>
    
</project>

Scalatra

Using Scalatra, the bootstrap class (with Guice Module for DI), the servlet definition and the launcher
import javax.servlet.ServletContext

import com.google.inject.Guice
import org.pento.services.{PingModule, PingService}
import org.scalatra.LifeCycle

class ScalatraBootstrap extends LifeCycle {
  private lazy val injector = Guice.createInjector(new PingModule())

  override def init(context: ServletContext): Unit = {
    context mount (injector.getInstance(classOf[PingService]), "/main-service/")
  }
}
package org.pento.services

import java.time.Clock

import com.google.inject.{AbstractModule, Provides, Singleton}

class PingModule extends AbstractModule {
  override def configure(): Unit = {
    bind(classOf[PingService]).asEagerSingleton()
  }

  @Provides @Singleton
  def provideClock() = Clock.systemUTC()
}
package org.pento.services

import java.time.Clock
import javax.inject.Inject

import org.scalatra.ScalatraServlet

class PingService @Inject() (val clock: Clock) extends ScalatraServlet {
  get ("/ping") {
    clock.millis()
  }
}
package org.pento.services

import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.DefaultServlet
import org.eclipse.jetty.webapp.WebAppContext
import org.scalatra.servlet.ScalatraListener

object PentoServer {
  val server = new Server(5899)
  val context = new WebAppContext()
  context.setContextPath("/")
  context.setResourceBase("src/main/scala")
  context.addEventListener(new ScalatraListener)
  context.addServlet(classOf[DefaultServlet], "/")
  server.setHandler(context)

  server.start()
  server.join()

  def main(args: Array[String]) {

  }
}
Neat. Simple.

Now to the database and jooq code generation

Who writes SQL those days? Well we have to.. but not in the code. jOOQ is probably one of the best libs I have used from Java and SCALA. Here is a simple jOOQ config that generates code from a given existing schema
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<configuration>
    <!-- Configure the database connection here -->
    <jdbc>
        <driver>org.apache.derby.jdbc.ClientDriver</driver>
        <url>jdbc:derby://localhost:1527/C:/Users/tj/Google Drive/IT-Projects/PentoPay/derby/PentoDb;create=true</url>
        <user>APP</user>
        <password>*</password>
    </jdbc>

    <generator>
        <name>org.jooq.util.DefaultGenerator</name>

        <database>
            <name>org.jooq.util.derby.DerbyDatabase</name>
            <inputSchema>APP</inputSchema>
            <includes>.*</includes>
        </database>

        <generate>
            <relations>true</relations>
            <deprecated>false</deprecated>
            <generateAnnotation>true</generateAnnotation>
            <records>true</records>
            <immutablePojos>true</immutablePojos>
            <globalObjectReferences>true</globalObjectReferences>
            <fluentSetters>false</fluentSetters>
        </generate>

        <target>
            <packageName>jooq.pento.db</packageName>
            <directory>C:/Users/tj/Google Drive/IT-Projects/PentoPay/src/main/java</directory>
        </target>
    </generator>
</configuration>
Some very nice utility classes to manage AutoCloseable and some fancy resource management (take back the jdbc connection to the pool post request). The code below allows automatically call close on an AutoCloseable
  def using[T <: AutoCloseable, R](resource: T)(block: T => R): R = {
    try {
      block(resource)
    } finally {
      if (resource != null) resource.close()
    }
  }
The one below deals specifically with jOOQ DSL Context:
  override def withSession[R](block: (DSLContext) => R): Option[R] = {
    import org.pento.utils.PentoUtils.using
    using(ds.getConnection) {connection =>
      try {
        Some(block(DSL.using(connection, sqlDialect, jooqSettings)))
      } catch  {
        case t: Throwable => None
      }
    }
  } // withSession
All in one, how to do a simple query. This is quite neat.
package org.pento.db

import com.google.inject.Inject
import com.zaxxer.hikari.HikariDataSource
import jooq.pento.db.tables.records.LastaccessRecord
import org.jooq.conf.Settings
import org.jooq.impl.DSL
import org.jooq.{DSLContext, SQLDialect}
import org.pento.utils.LastAccess

trait DataSource {
  def withSession[R](block: DSLContext => R): Option[R]
}

class DataSourceImpl @Inject() () extends DataSource {
  val ds = {
    val hds = new HikariDataSource()
    hds.setDriverClassName("")
    hds.setJdbcUrl("")
    hds.setUsername("")
    hds.setPassword("")
    hds
  }
  val sqlDialect = SQLDialect.valueOf("DERBY")
  val jooqSettings = new Settings()

  override def withSession[R](block: (DSLContext) => R): Option[R] = {
    import org.pento.utils.PentoUtils.using
    using(ds.getConnection) {connection =>
      try {
        Some(block(DSL.using(connection, sqlDialect, jooqSettings)))
      } catch  {
        case t: Throwable => None
      }
    }
  } // withSession
}

trait DB {
  def allAccesses: Option[List[LastAccess]]
}

class DBImpl @Inject() (val ds: DataSource) extends DB {
  override def allAccesses: Option[List[LastAccess]] = {
    import jooq.pento.db.tables.Lastaccess.LASTACCESS

    import scala.collection.JavaConversions._

    ds.withSession{ctx => {
      val rs = ctx.select(LASTACCESS.TS)
                  .from(LASTACCESS)
                  .fetchInto(classOf[LastaccessRecord])
      (for{r <- rs} yield LastAccess(r.value1())).toList
    }}
  }
}


No comments:

Blog Archive