Showing posts with label SBT. Show all posts
Showing posts with label SBT. Show all posts

Monday, February 22, 2016

Spring Boot with Scala

A while back I had tried out a small Spring Boot based sample with Scala as the language and found that the combination works out quite nicely - no big surprises there actually as Scala programs ultimately run in the JVM. I have now updated the sample with the latest version of Spring Boot and some of the supporting libraries.

To very quickly revisit the sample, it is a very simple web application with a UI to manage a "Hotel" domain object managed via JPA, represented in scala the following way:

import javax.persistence.Id
import javax.persistence.GeneratedValue
import java.lang.Long
import javax.persistence.Entity
import scala.beans.BeanProperty
import org.hibernate.validator.constraints.NotEmpty

@Entity
class Hotel {

  @Id
  @GeneratedValue
  @BeanProperty
  var id: Long = _

  @BeanProperty
  @NotEmpty
  var name: String = _

  @BeanProperty
  @NotEmpty
  var address: String = _

  @BeanProperty
  @NotEmpty
  var zip: String = _
}

JPA annotations carry over quite well, one wrinkle may be the additional @BeanProperty annotation though, this is required for JPA implementations as this makes the scala compiler generate the normal Java Beans type getters and setters instead of the scala default getters and setters which don't follow the Java Bean conventions.

Spring Data makes it ridiculously simple to manage this domain type, all it requires is a marker interface and it generates a runtime implementation:

import org.springframework.data.repository.CrudRepository
import mvctest.domain.Hotel
import java.lang.Long

trait HotelRepository extends CrudRepository[Hotel, Long]

Now I have a toolkit available for managing the Hotel domain:

//save or update a hotel
hotelRepository.save(hotel)

//find one hotel
hotelRepository.findOne(id)

//find all hotels
val hotels = hotelRepository.findAll()

//delete a hotel
hotelRepository.delete(id)


And finally a controller to manage the UI flow with this repository:

@Controller
@RequestMapping(Array("/hotels"))
class HotelController @Autowired()(private val hotelRepository: HotelRepository) {

  @RequestMapping(method = Array(RequestMethod.GET))
  def list(model: Model) = {
    val hotels = hotelRepository.findAll()
    model.addAttribute("hotels", hotels)
    "hotels/list"
  }

  @RequestMapping(Array("/edit/{id}"))
  def edit(@PathVariable("id") id: Long, model: Model) = {
    model.addAttribute("hotel", hotelRepository.findOne(id))
    "hotels/edit"
  }

  @RequestMapping(method = Array(RequestMethod.GET), params = Array("form"))
  def createForm(model: Model) = {
    model.addAttribute("hotel", new Hotel())
    "hotels/create"
  }

  @RequestMapping(method = Array(RequestMethod.POST))
  def create(@Valid hotel: Hotel, bindingResult: BindingResult) = {
    if (bindingResult.hasErrors()) {
      "hotels/create"
    } else {
      hotelRepository.save(hotel)
      "redirect:/hotels"
    }
  }

  @RequestMapping(value = Array("/update"), method = Array(RequestMethod.POST))
  def update(@Valid hotel: Hotel, bindingResult: BindingResult) = {
    if (bindingResult.hasErrors()) {
      "hotels/edit"
    } else {
      hotelRepository.save(hotel)
      "redirect:/hotels"
    }
  }

  @RequestMapping(value = Array("/delete/{id}"))
  def delete(@PathVariable("id") id: Long) = {
    hotelRepository.delete(id)
    "redirect:/hotels"
  }
}

There are some wrinkles here too but should mostly make sense, the way the repository is autowired is a little non-intuitive and the way an explicit Array type has to be provided for request mapping paths and methods may be confusing.

Beyond these small concerns the code just works, do play with it, I would love any feedback on ways to improve this sample. Here is the git location of this sample - https://github.com/bijukunjummen/spring-boot-scala-web

Tuesday, August 7, 2012

Mergesort using Fork/Join Framework

The objective of this entry is to show a simple example of a Fork/Join RecursiveAction, not to delve too much into the possible optimizations to merge sort or the relative advantages of using Fork/Join Pool over the existing Java 6 based implementations like ExecutorService.

The following is a typical implementation of a Top Down Merge sort algorithm using Java:

import java.lang.reflect.Array;

public class MergeSort {
 public static <T extends Comparable<? super T>> void sort(T[] a) {
  @SuppressWarnings("unchecked")
  T[] helper = (T[])Array.newInstance(a[0].getClass() , a.length);
  mergesort(a, helper, 0, a.length-1);
 }
 
 private static <T extends Comparable<? super T>> void mergesort(T[] a, T[] helper, int lo, int hi){
  if (lo>=hi) return;
  int mid = lo + (hi-lo)/2;
  mergesort(a, helper, lo, mid);
  mergesort(a, helper, mid+1, hi);
  merge(a, helper, lo, mid, hi);  
 }

 private static <T extends Comparable<? super T>> void merge(T[] a, T[] helper, int lo, int mid, int hi){
  for (int i=lo;i<=hi;i++){
   helper[i]=a[i];
  }
  int i=lo,j=mid+1;
  for(int k=lo;k<=hi;k++){
   if (i>mid){
    a[k]=helper[j++];
   }else if (j>hi){
    a[k]=helper[i++];
   }else if(isLess(helper[i], helper[j])){
    a[k]=helper[i++];
   }else{
    a[k]=helper[j++];
   }
  }
 }

 private static <T extends Comparable<? super T>> boolean isLess(T a, T b) {
  return a.compareTo(b) < 0;
 }
}

To quickly describe the algorithm -
The following steps are performed recursively:

  1.  The input data is divided into 2 halves
  2.  Each half is sorted
  3. The sorted data is then merged

Merge sort is a canonical example for an implementation using Java Fork/Join pool, and the following is a blind implementation of Merge sort using the Fork/Join framework:

The  recursive task in Merge sort can be succinctly expressed as an implementation of RecursiveAction -


 private static class MergeSortTask<T extends Comparable<? super T>> extends RecursiveAction{
  private static final long serialVersionUID = -749935388568367268L;
  private final T[] a;
  private final T[] helper;
  private final int lo;
  private final int hi;
  
  public MergeSortTask(T[] a, T[] helper, int lo, int hi){
   this.a = a;
   this.helper = helper;
   this.lo = lo;
   this.hi = hi;
  }
  @Override
  protected void compute() {
   if (lo>=hi) return;
   int mid = lo + (hi-lo)/2;
   MergeSortTask<T> left = new MergeSortTask<>(a, helper, lo, mid);
   MergeSortTask<T> right = new MergeSortTask<>(a, helper, mid+1, hi);
   invokeAll(left, right);
   merge(this.a, this.helper, this.lo, mid, this.hi);
   
   
  }
  private void merge(T[] a, T[] helper, int lo, int mid, int hi){
   for (int i=lo;i<=hi;i++){
    helper[i]=a[i];
   }
   int i=lo,j=mid+1;
   for(int k=lo;k<=hi;k++){
    if (i>mid){
     a[k]=helper[j++];
    }else if (j>hi){
     a[k]=helper[i++];
    }else if(isLess(helper[i], helper[j])){
     a[k]=helper[i++];
    }else{
     a[k]=helper[j++];
    }
   }
  }
  private boolean isLess(T a, T b) {
   return a.compareTo(b) < 0;
  }
 }

MergeSortTask above implements a compute method, which takes in a array of values, split it up into two parts, creates a MergeSortTask out of each of the parts and forks off two more tasks(hence it is called RecursiveAction!). The specific API used here to spawn of the task is invokeAll which returns only when the submitted subtasks are marked as completed. So once the left and right subtasks return the result is merged in a merge routine.

Given this the only work left is to use a ForkJoinPool to submit this task. ForkJoinPool is analogous to the ExecutorService used for distributing tasks in a threadpool, the difference to quote the ForkJoinPool's API docs:

A ForkJoinPool differs from other kinds of ExecutorService mainly by virtue of employing work-stealing: all threads in the pool attempt to find and execute subtasks created by other active tasks (eventually blocking waiting for work if none exist)

This is how the task of submitting the task to the Fork/Join Pool looks like:

 public static <T extends Comparable<? super T>> void sort(T[] a) {
  @SuppressWarnings("unchecked")
  T[] helper = (T[])Array.newInstance(a[0].getClass() , a.length);
  ForkJoinPool forkJoinPool = new ForkJoinPool(10);
  forkJoinPool.invoke(new MergeSortTask<T>(a, helper, 0, a.length-1));
 }

A complete sample is also available here: https://github.com/bijukunjummen/algos/blob/master/src/main/java/org/bk/algo/sort/algo04/merge/MergeSortForkJoin.java

Saturday, September 10, 2011

Using SBT 0.10.1, Eclipsify for a new Scala project

Run sbt(0.10.1) in a new folder:

D:\samplescala>sbt

D:\samplescala>set SCRIPT_DIR=C:\util\sbt\

D:\samplescala>java -Dfile.encoding=UTF8 -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -jar "C:\util\sbt\sbt
-launch.jar"
Getting net.java.dev.jna jna 3.2.3 ...
:: retrieving :: org.scala-tools.sbt#boot-app
        confs: [default]
        1 artifacts copied, 0 already retrieved (838kB/46ms)
Getting Scala 2.8.1 (for sbt)...
:: retrieving :: org.scala-tools.sbt#boot-scala
        confs: [default]
        3 artifacts copied, 0 already retrieved (15178kB/235ms)
Getting org.scala-tools.sbt sbt_2.8.1 0.10.1 ...
:: retrieving :: org.scala-tools.sbt#boot-app
        confs: [default]
        36 artifacts copied, 0 already retrieved (6414kB/965ms)
[info] Set current project to default-097978 (in build file:/D:/samplescala/)

The following folders will show up under the root folder:

Create the default source/test/resource structure:
mkdir src\main\resources
mkdir src\main\scala
mkdir src\main\java
mkdir src\test\resources
mkdir src\test\scala
mkdir src\test\java

Create a build configuration file build.sbt, and place it in the root of the project with the following content:

name:="samplescala"

version:="1.0"

scalaVersion := "2.9.0-1"

libraryDependencies ++= Seq(
    "junit" % "junit" % "4.8" % "test",
    "org.scalatest" % "scalatest_2.9.0" % "1.6.1"
)

defaultExcludes ~= (filter => filter || "*~")

Add, one sample test in the file src\test\scala\gcdtests.scala, just to test out the sbt configuration:
package com.sample

object Gcd{
    def gcd(a:Int, b:Int):Int = if (b==0) a else gcd(b, a%b)
}


import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers


class GcdTests extends FlatSpec with ShouldMatchers{
    "GCD of 1440, 408" should  "be 24" in {
        Gcd.gcd(1440, 408) should equal (24)
    }
}


Invoke sbt, and run test, if everything is configured correctly the following output will be displayed:
D:\samplescala>sbt

D:\samplescala>set SCRIPT_DIR=C:\util\sbt\

D:\samplescala>java -Dfile.encoding=UTF8 -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -jar "C:\util\sbt\sbt
-launch.jar"
[info] Set current project to default-097978 (in build file:/D:/samplescala/)
> test
[info] Updating {file:/D:/samplescala/}default-097978...
[info] Done updating.
[info] GcdTests:
[info] GCD of 1440, 408
[info] - should be 24
[info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0
[success] Total time: 1 s, completed Sep 10, 2011 7:00:28 PM

Now, to import this project into eclipse. Create a build.sbt file with the following contents and place in the project/plugins folder:
libraryDependencies <+= (libraryDependencies, sbtVersion) { (deps, version) => 
    "de.element34" %% "sbt-eclipsify" % "0.10.0-SNAPSHOT"
}

Restart sbt, if the plugin is configured correctly, eclipse will be a valid task in sbt, running this will create the .project and .classpath files for Eclipse:
> eclipse
[info] Starting eclipse
[info] written .project for samplescala
[info] written .classpath for samplescala
[info] * Don't forget to install the Scala IDE Plugin from http://www.scalaide.org/
[info] You may now import your projects in Eclipse

Assuming that Scala IDE for Eclipse is installed, import this new project into Eclipse:

Sunday, June 12, 2011

IntelliJ IDEA for Scala/SBT projects

I have found it easier to use Intellij IDEA with Scala plugins, for Scala learnings. This is the way I normally spin up a scala project.

1. Use sbt to first create a shell of a project:
D:\learn\shell-project>sbt

D:\learn\shell-project>set SCRIPT_DIR=C:\util\sbt\

D:\learn\shell-project>java -Xmx512M -jar "C:\util\sbt\sbt-launch-0.7.5.jar"
Project does not exist, create new project? (y/N/s) y
Name: shellproject
Organization: org.bk
......

2. Add eclipsify plugin - this is done by creating a sbt ProjectDefinition under project/plugins folder:
import sbt._

 class MySbtProjectPlugins(info: ProjectInfo) extends PluginDefinition(info) {
       lazy val eclipse = "de.element34" % "sbt-eclipsify" % "0.7.0"
 }


3. Add scalatest to the dependency - this is done by creating a file of the following type under project/build folder:

import sbt._
import de.element34.sbteclipsify._

class MySbtProject(info: ProjectInfo) extends DefaultProject(info) with Eclipsify {
    override def libraryDependencies = Set(
       "org.scalatest" % "scalatest" % "1.3" % "test->default"
    ) ++ super.libraryDependencies
}

4. reload sbt(run "reload" in the sbt console)

5. A new action "eclipse" should now be available. Run "eclipse"

6. That's it. Now a new project should be available for IntelliJ idea to import as a project.