2012-11-03

Spring Data MongoDB on Cloud Foundry tutorial Part 1

Hi, in this tutorial I will show you how to develop a Spring powered MongoDB project, with Gradle as dependency management, and how to deploy it on Cloud Foundry. The app will be a web app deployed on Tomcat.

Cloud Foundry allows you to run many instances of your application, add and remove services, when needed, and so on. You may want to read some info on getting started.

Register on Cloud Foundry

The first step is to register on Cloud Foundry. You will either manage your cloud app using a command line VMC tool, or using Cloud Foundry extension for Spring Tool Suite/Eclipse. I chose to use the IDE independent VMC.

The Spring Data MongoDB app

The Spring app I am using is available to download from my GitHub (specifically, the version for this article is tagged as BlogPost1 and you can also get it from the tags view). Below I will show you some of the steps you need to make to get your Spring MongoDB app up and running - locally for now. More information is available from the Cloud Foundry Spring Application Development page.

Things to remember:

  • Cloud Foundry technology support is limited, so you have to figure out beforehand whether your stack will even work.
  • At this moment, Java 7 is not supported on Cloud Foundry, so you need to downgrade your project to Java 6.
  • Similarly, only certain versions of Spring and Spring Data MongoDB are supported.
  • Only Tomcat 6 is supported at the moment. Locally I'm developing against Tomcat 7, but due to Cloud Foundry limitations it has to work on Tomcat 6 as well (no problems with that).
  • Servlet Spec 3.0 is not supported - back to 2.5.
First, the build.gradle file with dependencies. Gradle dependency resolution strategy is different from that of Maven, but if you prefer Maven, the dependencies should be similar enough.
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'war'
apply plugin: 'eclipse-wtp'

group = 'me.m1key.audiolicious'
version = '0.0.1-SNAPSHOT'

sourceCompatibility = 1.6
targetCompatibility = 1.6

repositories {
   mavenLocal()
   mavenCentral()
   mavenRepo name: "spring-test-mvc", url: 'http://repo.springsource.org/libs-milestone/'
   mavenRepo url:'http://maven.springframework.org/milestone/'
}

project.ext.finalName = 'audiolicious-cloud.war'

war { archiveName = finalName }
task deployment(type: Copy) {
   from('build/libs/' + finalName)
   into("$System.env.TOMCAT_HOME/webapps/")
}

project.ext.springVersion = '3.1.2.RELEASE'

dependencies {
   compile group: 'org.springframework', name: 'spring-beans', version: springVersion, force: true
   compile group: 'org.springframework', name: 'spring-context', version: springVersion, force: true
   compile group: 'org.springframework', name: 'spring-core', version: springVersion, force: true
   compile group: 'org.springframework', name: 'spring-webmvc', version: springVersion, force: true

   compile group: 'org.springframework.data', name: 'spring-data-mongodb', version: '1.0.4.RELEASE'
   compile group: 'org.mongodb', name: 'mongo-java-driver', version: '2.9.1'

   compile group: 'org.slf4j', name: 'slf4j-api', version: '1.6.6'
   compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.6.6'
   compile group: 'org.apache.velocity', name: 'velocity', version: '1.7'

   compile group: 'org.cloudfoundry', name: 'cloudfoundry-runtime', version: '0.8.2'

   testCompile group: 'org.springframework', name: 'spring-test', version: springVersion
   testCompile group: 'junit', name: 'junit', version: '4.+'
   testCompile 'org.hamcrest:hamcrest-all:1.3'
   testCompile 'javax.servlet:servlet-api:2.5'
   testCompile 'org.springframework:spring-test-mvc:1.0.0.M2'
}

configurations.all {
   resolutionStrategy {
       force group: 'org.springframework', name: 'spring-aop', version: springVersion
       force group: 'org.springframework', name: 'spring-expression', version: springVersion
       force group: 'org.springframework', name: 'spring-tx', version: springVersion
   }
}
Meanwhile, in your src/main/webapp/WEB-INF/web.xml...
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   id="AudioliciousCloud" version="2.5" metadata-complete="true">
   <display-name>Spring MVC tutorial</display-name>

   <servlet>
       <servlet-name>audiolicious</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
           <param-name>contextConfigLocation</param-name>
           <param-value>classpath:audiolicious-servlet.xml</param-value>
       </init-param>
   </servlet>

   <servlet-mapping>
       <servlet-name>audiolicious</servlet-name>
       <url-pattern>*.go</url-pattern>
   </servlet-mapping>
</web-app>
And the Spring servlet file (src/main/resources/audiolicious-servlet.xml).
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:mongo="http://www.springframework.org/schema/data/mongo"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
 http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd">

   <!-- Enabling Spring beans auto-discovery -->
   <context:component-scan base-package="me.m1key.audiolicious.cloud" />

   <!-- Enabling Spring MVC configuration through annotations -->
   <mvc:annotation-driven />

   <bean id="velocityConfig"
       class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
       <property name="resourceLoaderPath">
           <value>/</value>
       </property>
   </bean>

   <!-- Defining which view resolver to use -->
   <bean id="viewResolver"
       class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
       <property name="prefix">
           <value>/velocity/</value>
       </property>
       <property name="suffix">
           <value>.vm</value>
       </property>
   </bean>

   <mongo:db-factory id="mongoDbFactory" dbname="adlcs_cloud"
       host="127.0.0.1" port="27017" />

   <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
       <constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
   </bean>

   <mongo:repositories base-package="me.m1key.audiolicious.cloud.repositories" />
</beans>
What's important here is MongoDB configuration. If you configure it this way, it will allow Cloud Foundry to inject its own values for host name and port, so that it also works on the cloud.

MongoDB repository

One of the nice features of Spring Data MongoDB is that you can (just like in Grails) ask it to create simple repositories for you - you just define interfaces, Spring will provide the implementation.
package me.m1key.audiolicious.cloud.repositories;
import me.m1key.audiolicious.cloud.entities.Song;
import org.springframework.data.repository.CrudRepository;

public interface SongRepository extends CrudRepository<Song, Long> {
}
This gives me a CRUD repo that I don't have to implement myself. You can add methods to this interface, such as findByLastName, and Spring will know how to implement them. For more information on this, see the repositories reference. You can extend this behaviour by implementing your own, more sophisticated methods.

Entity. Notice no annotations:

package me.m1key.audiolicious.cloud.entities;

public class Song {

    private String name;
    private String albumName;
    private String artistName;
    private String songKey;

    public Song(String name, String albumName, String artistName, String songKey) {
        super();
        this.name = name;
        this.albumName = albumName;
        this.artistName = artistName;
        this.songKey = songKey;
    }

    public String getName() {
        return name;
    }
    // Omitted for brevity.
}

In part 2 of this tutorial I will show you how to deploy this on Cloud Foundry.

PS. Tomcat debugging

This is the stuff I added in my catalina.sh (this is Linux syntax then) file to get remote debugging from my IDE to work.
JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n $JAVA_OPTS"
JPDA_TRANSPORT=dt_socket
JPDA_ADDRESS=8000

2012-09-30

CDI @Produces not working?

I deployed my application to JBoss AS 7.1.1.Final and realised that my @Produces annotations were not really producing... But they were working fine with Arquillian. It was my programming error.

This does not work

@ApplicationScoped
public class TrackMappersFactory {

    @EJB
    private AudiobookMapper audiobookMapper;
    @EJB
    private PodcastMapper podcastMapper;
    @EJB
    private SongMapper songMapper;
    @EJB
    private VideoMapper videoMapper;

    @Produces
    @AggregateMapper
    public Map<Class<? extends TrackTo>, TrackMapper<? extends TrackTo>> getAggregateTrackMapper() {
The reason why is simple, this class is not detected by the container because it's not an EJB. The solution is to make it a @Singleton bean instead.

This works

@Singleton
public class TrackMappersFactory {

    @EJB
    private AudiobookMapper audiobookMapper;
    @EJB
    private PodcastMapper podcastMapper;
    @EJB
    private SongMapper songMapper;
    @EJB
    private VideoMapper videoMapper;

    @Produces
    @AggregateMapper
    public Map<Class<? extends TrackTo>, TrackMapper<? extends TrackTo>> getAggregateTrackMapper() {
Here is the diff for this change.

2012-09-29

Photos from Fez, Morocco

Have a minute to see my photos from Fez, Morocco?

Fez, Morocco

Arquillian and @Asynchronous

I have just learnt that Arquillian does not support @Asynchronous calls. So if you have @Asynchronous EJBs, then they might run fine on JBoss 7 (provided it's full profile and not just web profile, as far as I know), but not when you deploy it via Arquillian. The problem is, the errors this will produce are as misleading as it gets. I got NoClassDefFoundError for example.

Original situation

The bean.
@Stateless
@Asynchronous
@Local(LibraryImporter.class)
public class CacheAwareLibraryImporter implements LibraryImporter {

// … omitted for brevity

   @Override
   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public Future importLibrary(File libraryFile) {
// code...
   }
}
The test.
@RunWith(Arquillian.class)
public class DefaultSongServiceMacOsIT {

   @EJB
   private LibraryService libraryService;

// … omitted for brevity
This doesn't work with Arquillian.

Workaround

Here's my workaround. You may want to look at the diff file instead. The new bean (in src/test/java).
@Stateless
@Local({ LibraryImporter.class, SynchronousLibraryImporter.class })
public class SynchronousCacheAwareLibraryImporter extends
       CacheAwareLibraryImporter implements SynchronousLibraryImporter {

   @Override
   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public Future importLibrary(File libraryFile) {
       return super.importLibrary(libraryFile);
   }

}
The new interface (in src/test/java).
public interface SynchronousLibraryImporter extends LibraryImporter {

}
The test.
@RunWith(Arquillian.class)
public class DefaultSongServiceMacOsIT {

   @EJB
   private SynchronousLibraryService libraryService;

// … omitted for brevity
Nice...-ty. This is a known issue and they haven't done much about it so far.

Morphia and semicolons

I noticed a strange behaviour from Morphia when writing MongoDb group by equivalents. It doesn't seem to like semicolons in your queries. Please see the following example.
db.MongoDbSong.group(
    {key: {artistName: 1, albumName: 1},
    initial: {totalPlays: 0},
    reduce: function(obj, prev) { for(var i=0; i < obj.statsList.length; i++) { if(obj.statsList[i].libraryUuid == 'bf25ff51-da26-4c76-82dc-ada0e4a68c18'){ prev.totalPlays += obj.statsList[i].playCount}}}
})
This code is valid and runs fine inside MongoDb console. Not so in Morphia, it returns a null result.

Workaround

I had to change my query into this, so essentially iterate over the array without using semicolons:
db.MongoDbSong.group(" +
    {key: {artistName: 1, albumName: 1},
    initial: {totalPlays: 0},
    reduce: function(obj, prev) {for each (var item in obj.statsList) if(item.libraryUuid == 'bf25ff51-da26-4c76-82dc-ada0e4a68c18'){ prev.totalPlays += item.playCount}}" +
 "});
It could be that I'm wrong. Anyway, this works and I have submitted this as a defect.

See Scala code using this.

Search in Git

Hi, I will show you how to:
  1. search in git commits (the actual code committed), and
  2. search in commit messages.

Search in committed code

git log -S "whatever you are looking for"

Search in commit messages

git log --grep="whatever you are looking for"

2012-09-28

MongoDB with Morphia (and Scala)

I've been working on my Scala application that connects to MySQL on one end (using EclipseLink and JPA), and MongoDB on the other (using Morphia). The source code is available on GitHub. In this post I will show you my setup.

build.gradle

Dependencies...
// ...

repositories {
   mavenLocal()
   mavenCentral()
   mavenRepo name: "EclipseLink", url: "http://download.eclipse.org/rt/eclipselink/maven.repo/"
   mavenRepo name: "MongoDB", url: "http://morphia.googlecode.com/svn/mavenrepo"
   mavenRepo name: "Audiolicious", url: "https://github.com/m1key/repo/raw/master/releases"
}

dependencies {

   compile group: 'org.eclipse.persistence', name: 'eclipselink', version: '2.3.0'
   compile group: 'org.eclipse.persistence', name: 'javax.persistence', version: '2.0.0'
   compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.18'
   compile group: 'org.mongodb', name: 'mongo-java-driver', version: '2.7.3'
   compile group: 'com.google.code.morphia', name: 'morphia', version: '0.99'

   // …
}

// ...

Persistence provider

This is how you expose Mongo to your repositories (or DAOs).
package me.m1key.audioliciousmigration.persistence.mongodb

import com.google.code.morphia.Datastore
import com.mongodb.Mongo
import com.google.code.morphia.Morphia
import me.m1key.audioliciousmigration.entities.mongodb.MongoDbSong

class ProductionMorphiaMongoDbPersistenceProvider extends MorphiaMongoDbPersistenceProvider {

 private var datastore: Datastore = null
 private var mongo: Mongo = null

 def initialise(): Unit = {
    mongo = new Mongo("localhost", 27017)
    datastore = new Morphia().map(classOf[MongoDbSong]).createDatastore(mongo, "audiolicious")
    datastore.ensureIndexes()
 }

 def getDatastore(): Datastore = {
    return datastore
 }

 def getMongo(): Mongo = {
    return mongo
 }

}

Repository using getDatastore

This is a repository which uses the MongoDB datastore.
package me.m1key.audioliciousmigration.repository

import com.google.inject.Inject
import me.m1key.audioliciousmigration.persistence.mongodb.MorphiaMongoDbPersistenceProvider
import scalaj.collection.Imports._
import me.m1key.audioliciousmigration.entities.mongodb.MongoDbSong
import com.google.code.morphia.query.UpdateOperations
import com.google.code.morphia.query.Query

class MorphiaMongoDbRepository @Inject() (private val persistenceProvider: MorphiaMongoDbPersistenceProvider) {

 def save(song: MongoDbSong): Unit = {
    val datastore = persistenceProvider.getDatastore()

    val selectQuery = datastore.createQuery(classOf[MongoDbSong])
     .field("name").equal(song.name).field("albumName").equal(song.albumName)
     .field("artistName").equal(song.artistName).field("songKey").equal(song.songKey);
    val existingSong = selectQuery.get()

    if (existingSong != null) {
     var ops = datastore.createUpdateOperations(classOf[MongoDbSong])

     if (song.genre != null) {
       ops = ops.set("genre", song.genre)
     }
     if (song.year != 0) {
       ops = ops.set("year", song.year)
     }
     if (song.songArtistName != null) {
       ops = ops.set("songArtistName", song.songArtistName)
     }

     val stat = song.statsList.get(0)
     existingSong.addOrEditStats(stat.libraryUuid, stat.percentage, stat.playCount, stat.skipCount)
     ops.set("statsList", existingSong.statsList)

     val updatedCount = datastore.update(selectQuery, ops).getUpdatedCount()

     if (updatedCount != 1) {
       println("Warning. Song updated [%d] times while expected once: [%s]".format(updatedCount, song))
     }
    } else {
     datastore.save(List(song).asJava)
    }
 }

}

getMongo

Here's code that uses getMongo for executing a raw query.
 def mine(): Option[Int] = {
    val mongo = persistenceProvider.getMongo
    val result = mongo.getDB("audiolicious").eval(query)
    result match {
     case double: java.lang.Double => return Some(double.intValue())
     case _ =>
       println("Error while obtaining stats. Result of unknown type [%s].".format(result))
       return None;
    }
 }

Putting it all together...

Before code using datastore or mongo can be executed, the following call has to be made.
    val mongoDbPersistence = injector.getInstance(classOf[MorphiaMongoDbPersistenceProvider])
    mongoDbPersistence.initialise
(This example is using Guice) I don't know if this is the best way of doing things, but there's little documentation available. This way works. If you have a better idea - definitely let me know!

2012-09-25

No-args constructor in Scala for Morphia

Morphia (like Hibernate) requires a no-args constructor for instantiating objects. Here's how to do it in Scala, while keeping your proper constructor with arguments.
import com.google.code.morphia.annotations.Entity
import com.google.code.morphia.annotations.Indexes
import com.google.code.morphia.annotations.Index

@Entity
@Indexes(Array(new Index(value = "name, albumName, artistName, songKey", unique = true)))
class MongoDbSong(val name: String, val albumName: String, val artistName: String, val songKey: String) {

 // No-args constructor to be used by Morphia.
 def this() {
   this("name to be set", "albumName to be set", "artistName to be set", "songKey to be set")
 }

2012-08-29

GROUP BY with MongoDB (and Scala and Morphia)

This is an example of how to run a query similar to GROUP BY from the SQL world, but in MongoDB. MongoDB doesn't have GROUP BY, but it has a very similar group command.

In my example I want to return the number of songs per artist, therefore I'm grouping by artist name.

db.MongoDbSong.group(
    {key: {artistName: true},
    initial: {totalSongs: 0},
    reduce: function(obj, prev) { prev.totalSongs++;}
})

This example is from my MongoDB + Scala + Gradle + Morphia app. It turns out that Morphia does not help with that at the moment, so we have to run the query raw and then process the results.

class SongPerArtistMining @Inject() (private val persistenceProvider: MorphiaMongoDbPersistenceProvider) {

 private val query = "db.MongoDbSong.group("+
    "{key: {artistName: true},"+
    "initial: {totalSongs: 0},"+
    "reduce: function(obj, prev) { prev.totalSongs++;}"+
    "})";
 
 private val formatter = NumberFormat.getInstance(Locale.ENGLISH)


 def mine(maxResults: Int): Option[List[(String, Int)]] = {
   val mongo = persistenceProvider.getMongo
   val result = mongo.getDB("audiolicious").eval(query)
   result match {
     case list: BasicDBList => return Some(processAndRetrieveResults(list, maxResults))
     case _ => println("Error while obtaining stats. Result of unknown type [%s].".format(result))
         return None;
   }
 }

Essentially, this does it:

val result = mongo.getDB("audiolicious").eval(query)

Processing results

And here's how to process results.

private def processAndRetrieveResults(list: BasicDBList, maxResults: Int): List[(String, Int)] = {
   return processResults(list).sortWith(compareSecondValueInteger).slice(0, maxResults)
 }

 def compareSecondValueInteger(e1: (String, Int), e2: (String, Int)) = e1._2 > e2._2
 
 private def processResults(list: BasicDBList): List[(String, Int)] = {
   var results : List[(String, Int)] = List()
   for (i <- 0 until list.size()) {
     val item = list.get(i)
     item match {
       case dbObject: BasicDBObject => results ::= processResult(dbObject)
       case _ => println("Error while obtaining stats. Result item of unknown type [%s].".format(item.getClass()))
     }
   }
   return results
 }
 
 private def processResult(dbObject: BasicDBObject): (String, Int) = {
   val artistName = dbObject.get("artistName").toString()
   val totalSongs = parseDouble(dbObject.get("totalSongs").toString(), formatter).intValue()
   return (artistName, totalSongs)
 }

 // http://stackoverflow.com/a/9542323
 def parseDouble(s: String, nf: NumberFormat) = {
   val pp = new ParsePosition(0)
   val d = nf.parse(s, pp)
   if (pp.getErrorIndex == -1) d.doubleValue else 0
 }

Abandoned Sanatorium - Beelitz-Heilstätten (my pictures)

Hi, these pictures are from Beelitz-Heilstätten, the abandoned sanatorium in Germany, where Adolf Hitler once was a patient.

Beelitz-Heilstätten

2012-08-25

Unique compound index with Morphia (MongoDb) and Scala

Hi, here's how to create a unique compound index with Morphia and Scala. The example is from my Gradle + Scala + MongoDb application Also, here's Part 1 of my tutorial on building a Scala + Gradle + Guice applications.

package me.m1key.audioliciousmigration.entities.mongodb
import com.google.code.morphia.annotations.Id
import com.google.code.morphia.annotations.Entity
import org.bson.types.ObjectId
import com.google.code.morphia.annotations.Indexes
import com.google.code.morphia.annotations.Index

@Entity
@Indexes(Array(new Index(value = "name, albumName", unique = true)))
class MongoDbSong(val name: String, val albumName: String) {

 @Id
 var id: ObjectId = _
 
 override def equals(that: Any) = {
   that match {
     case that: MongoDbSong => that.name == this.name &&
             that.albumName == this.albumName
     case _ => false
   }
 }

 override def hashCode(): Int = name.hashCode()

}

Remember to run this code in your app:

datastore.ensureIndexes()

Also, my final note here is this: If you make any such changes to your MongoDb schema, you have to drop the collection from the Mongo client.

db.MongoDbSong.drop()

2012-04-03

Scala + Guice + Gradle: Integration tests (Part 5)

Previously I showed you how to add to your Scala + Guice project JPA 2 implemented by EclipseLink. In this article I will show you how to integration test it using the specs library.

build.gradle

Below are the relevant part of your build.gradle file.
test { exclude "**/*IT.class" }

task integrationTest(type: Test, dependsOn: testClasses) { include "**/*IT.class" }

check.dependsOn integrationTest

dependencies {
    testCompile group: 'org.scala-tools.testing', name: 'specs_2.9.1', version: '1.6.9'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}
(This obviously is not the complete file.)

We specified a new task called integrationTest which will run our integration tests. It will run during gradle build as well. gradle test alone will NOT run them.

Step by step - PersistenceLibraryRepositorySpecIT.scala

package me.m1key.audioliciousmigration.repository

import org.specs._
import org.specs.runner._
import org.junit.runner.RunWith
import me.m1key.audioliciousmigration.persistence.JpaPersistenceProvider
import me.m1key.audiolicious.domain.entities.Library
import java.util.Date

@RunWith(classOf[JUnitSuiteRunner])
class PersistenceLibraryRepositorySpecIT extends Specification with JUnit {

 val jpaPersistenceProvider = new JpaPersistenceProvider
 jpaPersistenceProvider.initialise
 val repository = new PersistenceLibraryRepository(jpaPersistenceProvider)
 val entityManager = jpaPersistenceProvider.getEntityManager

//...
This is the test declaration. It allows us to run the test as a JUnit test from the IDE. Notice that file name ends with ...IT.scala. This is how we differentiate integration tests.

Step by step - before

// ...
doBeforeSpec {
   deleteLibraries
 }
//...
This will run before the test (just once).

Step by step - test


 "Fetching latest library with no libraries" should {
    var library: Library = null

    doFirst {
     println("Preparing test 1...")
     deleteLibraries
     println("Test prepared. Libraries: %d".format(librariesCount))
    }

    "return None." in {
     entityManager.getTransaction().begin()
     repository.getLatestLibrary() mustBe None
     entityManager.getTransaction().commit()
    }

    doLast {
     println("Cleaning up...")
     deleteLibraries
     println("Cleaned up. Libraries: %d".format(librariesCount))
    }
 }
This is a test. Notice the BDD style. The assertion is this line: repository.getLatestLibrary() mustBe None. doFirst and doLast allow us to prepare the test and clean up.

Step by step - another test

 "Fetching library by UUID with three libraries" should {
    setSequential()
    var olderLibrary: Library = null
    var anotherOlderLibrary: Library = null
    var newerLibrary: Library = null

    doFirst {
     println("Preparing test 3...")
     deleteLibraries
     olderLibrary = insertLibrary
     Thread.sleep(1000)
     anotherOlderLibrary = insertLibrary
     Thread.sleep(1000)
     newerLibrary = insertLibrary
     println("Test prepared. Libraries: %d".format(librariesCount))
    }

    "return correct 1st library." in {
     entityManager.getTransaction().begin()
     val library = repository.getLibrary(olderLibrary.getUuid).get
     entityManager.getTransaction().commit()
     library mustBe olderLibrary
    }

    "return correct 2nd library." in {
     entityManager.getTransaction().begin()
     val library = repository.getLibrary(anotherOlderLibrary.getUuid).get
     entityManager.getTransaction().commit()
     library mustBe anotherOlderLibrary
    }

    "return correct 3rd library." in {
     entityManager.getTransaction().begin()
     val library = repository.getLibrary(newerLibrary.getUuid).get
     entityManager.getTransaction().commit()
     library mustBe newerLibrary
    }

    doLast {
     println("Cleaning up...")
     deleteLibraries
     println("Cleaned up. Libraries: %d".format(librariesCount))
    }
 }
setSequential allows us to keep variables (olderLibrary, anotherOlderLibrary, newerLibrary) in scope for all tests.

Step by step - final clean up

//...  
 doAfterSpec {
    deleteLibraries
 }
//...

You can see the whole source for this file here.

Final note

gradle build will NOT run tests that have already run since the last code change! If you want them to run again, use gradle clean build.

Download source code

Source code for this article can be obtained via GitHub. Please see the README file for building and running.

Scala + Guice + Gradle: JPA 2 & EclipseLink (Part 4)

Previously I showed you how to create a Singleton in Guice powered Scala project. In this article I will show you how you can (JPA 2) EclipseLink support.

This sample app is a console app, so we have to handle entity management ourselves.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">

<persistence-unit name="audioliciousPu">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

<class>me.m1key.audiolicious.domain.entities.Library</class>
<class>me.m1key.audiolicious.domain.entities.Artist</class>
<class>me.m1key.audiolicious.domain.entities.Album</class>
<class>me.m1key.audiolicious.domain.entities.Song</class>
<class>me.m1key.audiolicious.domain.entities.Stat</class>
<class>me.m1key.audiolicious.domain.entities.Rating</class>

<properties>
<property name="eclipselink.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="eclipselink.jdbc.url"
value="jdbc:mysql://localhost:3306/audiolicious_test" />
<property name="eclipselink.jdbc.user" value="root" />
<property name="eclipselink.jdbc.password" value="" />
<property name="eclipselink.target-database" value="MYSQL" />
<property name="eclipselink.logging.level" value="OFF" />
<property name="eclipselink.orm.throw.exceptions" value="true" />
</properties>
</persistence-unit>
</persistence>

EntityManager

package me.m1key.audioliciousmigration.persistence
import javax.persistence.EntityManager
import javax.persistence.EntityManagerFactory
import javax.persistence.Persistence

class JpaPersistenceProvider extends PersistenceProvider {

 private var factory: EntityManagerFactory = _
 private var entityManager: EntityManager = _

 @Override
 def initialise: Unit = {
   factory = Persistence.createEntityManagerFactory("audioliciousPu")
   entityManager = factory.createEntityManager();

 }

 @Override
 def getEntityManager: EntityManager = {
   return entityManager
 }

 @Override
 def close: Unit = {
   entityManager.close();
   factory.close();
 }

}

Usage

val persistenceProvider = injector.getInstance(classOf[PersistenceProvider])
persistenceProvider.initialise
val entityManager = persistenceProvider.getEntityManager
entityManager.getTransaction().begin()

//...

entityManager.getTransaction().commit()
persistenceProvider.close

build.gradle

repositories {
    // ...
    mavenRepo name: "EclipseLink", url: "http://download.eclipse.org/rt/eclipselink/maven.repo/"
}

dependencies {
    compile group: 'me.m1key.audiolicious', name: 'audiolicious-domain-objects', version: '0.1.0-SNAPSHOT'
    compile group: 'org.eclipse.persistence', name: 'eclipselink', version: '2.3.0'
    compile group: 'org.eclipse.persistence', name: 'javax.persistence', version: '2.0.0'
    compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.18'
    // …
}
We needed to explicitly add EclipseLink specific repository.

Conclusion

I did not present any entities here because in my app they come from another Java powered project. In the next article I will show you how to integration test this code.

Download source code

Source code for this article can be obtained via GitHub. Please see the README file for building and running.

2012-04-02

Scala + Guice + Gradle: Singleton (Part 3)

In the previous part of this tutorial I explained how to use Guice in your Gradle + Scala project. In this short post I will build up on that and show you how to create a singleton scoped instance, as the default scope in Grails is new every time requested.

Singleton

This is the singleton to be class:
class PersistenceLibraryRepository
@Inject() (private val persistenceProvider: PersistenceProvider)
extends LibraryRepository {
// ...
}
Nothing special here!

As far as I know, you cannot use the @Singleton annotation, so this is how you make this class a singleton:

class AudioliciousMigrationModule extends AbstractModule {

 @Override
 protected def configure() {
 bind(classOf[LibraryRepository])
   .to(classOf[PersistenceLibraryRepository])
   .in(Scopes.SINGLETON)  }
}
Voila!

In Part 4 I will show you how to connect to a MySQL database using JPA 2 implemented by EclipseLink.

Download source code

Source code for this article can be obtained via GitHub.

2012-04-01

Scala + Guice + Gradle (Part 2)

In Part 1 of this tutorial I showed you how to create a Gradle based Scala project. Now I will show you how to use Guice.

Basic example

The abstraction that defines the contract:
package me.m1key.audioliciousmigration

trait AudioliciousImporter {

def importLibrary(libraryUuid: String): Unit

}
The implementation:
package me.m1key.audioliciousmigration.importer

import me.m1key.audioliciousmigration.AudioliciousImporter

private[audioliciousmigration]
class RelativeDataImporter extends AudioliciousImporter {

def importLibrary(libraryUuid: String): Unit = {
    println("Importing library [%s]...".format(libraryUuid));
    println("Library [%s] imported.".format(libraryUuid));
}

}
Note package level private access - the implementation is not visible from the outside.

Here we bind implementations to abstractions:

package me.m1key.audioliciousmigration
import com.google.inject.AbstractModule
import com.google.inject.Provides
import me.m1key.audioliciousmigration.importer.RelativeDataImporter

class AudioliciousMigrationModule extends AbstractModule {

@Override
protected def configure() {
    bind(classOf[AudioliciousImporter]).to(classOf[RelativeDataImporter])
}

}
Finally, the bootstrap:
package me.m1key.audioliciousmigration
import com.google.inject.Guice

object Launcher {

def main(args: Array[String]): Unit = {
    println("Audiolicious Importer")
  
    val injector = Guice.createInjector(new AudioliciousMigrationModule)
    val importer = injector.getInstance(classOf[AudioliciousImporter])
    importer.importLibrary("UUID")
  
    println("Bye.")
}

}
If you're new to Guice and you're confused, you can read more about it here.

In Part 3 I will show you how to create a singleton.

Download source code

Source code for this article can be obtained via GitHub.

2012-03-31

Scala + Guice + Gradle Tutorial (Part 1)

Hi, in this tutorial I will show you how to create a Gradle based Scala project that uses Guice for dependency injection.

Create Gradle project in IDE from existing Gradle sources

If you already have a Gradle project, you can easily generate Eclipse or IntelliJ project files using available plugins (Eclipse plugin, IntelliJ plugin). I haven't found anything like that for NetBeans but I'm not a NetBeans user.

Create Gradle project from scratch

If you do not have Gradle sources and are starting a project from scratch, it's a bit more tiresome, because at this moment neither Eclipse nor IntelliJ support creating Gradle projects. NetBeans has a Gradle plugin, but I haven't tried it out.

(Option 1 - any IDE) In that case you can create a Gradle project skeleton by hand and then import it into your IDE using methods above (there seems to be no way to create a skeleton project automatically at this moment).

(Option 2 - just Eclipse) You can create a new Scala project in your IDE and add Gradle nature - but that at the moment only seems to work in Eclipse. Again, I haven't tried that with NetBeans.

Here's how to do it with SpringSource Tool Suite:
  1. Download and install STS if you haven't already.
  2. Launch STS.
  3. Install these extensions: Scala, Gradle, Groovy. Maven support is not needed.
  4. Create a new Scala Project.
  5. Create source folders: src/main/scala, src/main/resources, src/test/scala, src/test/resources.
  6. Right click on the project, Configure -> Convert to Gradle project.
  7. Create a file called build.gradle in the root project directory.
  8. Paste sample build.gradle content (see below).
  9. Right click on the build.gradle file and choose Gradle -> Refresh All. Give it a while...
  10. Right click on the build.gradle file and choose Run As -> Gradle Build. Tick build and hit Run.
  11. This should build the project and obtain all dependencies.
  12. Create a Scala object in a package inside src/main/scala. In my case the package is me.m1key.audioliciousmigration and the class name is Launcher. That corresponds to the build.gradle manifest Main-class declaration.
  13. Paste Launcher.scala content (from below).
  14. You can now right click on Launcher, choose Run As -> Scala Application and it will run.
  15. If you now run gradle install rather that gradle build, it will deploy the artifact to your local Maven (.m2) repository. The artifact name is deduced from the project name.
If you create your project like that, you can successfully import it into another STS instance or IntelliJ, I checked that.

build.gradle

apply plugin: 'scala'
apply plugin: 'maven'

sourceCompatibility = 1.6
version = '0.0.1-SNAPSHOT'
group = 'me.m1key.audiolicious-migration'

jar {
   manifest {
       attributes 'Implementation-Title': 'Audiolicious Migration', 'Implementation-Version': version,
               'Main-class': 'me.m1key.audioliciousmigration.Launcher'
   }
   from {
       configurations.compile.collect {
           it.isDirectory() ? it : zipTree(it)
       }
   }
}

repositories { mavenCentral() }

dependencies {
   compile group: 'com.google.inject', name: 'guice', version: '3.0'
   testCompile group: 'org.scala-tools.testing', name: 'specs_2.9.1', version: '1.6.9'
   testCompile group: 'junit', name: 'junit', version: '4.+'
   
   // Libraries needed to run the scala tools
   scalaTools 'org.scala-lang:scala-compiler:2.9.1'
   scalaTools 'org.scala-lang:scala-library:2.9.1'

//     Libraries needed for scala api
   compile 'org.scala-lang:scala-library:2.9.1'
}

Launcher.scala

package me.m1key.audioliciousmigration

object Launcher {

 def main(args: Array[String]): Unit = {
    println("Hello world!")
 }

}
Once it's built, you can run the jar like that from project root:

java -jar build\libs\audiolicious-migration-0.0.1-SNAPSHOT.jar (Windows)

java -jar build/libs/audiolicious-migration-0.0.1-SNAPSHOT.jar (Unix)

Unit Tests

Here's how to write a unit test. You can run it as a JUnit test in your IDE and it runs as a part of gradle build, which I think is quite cool.
package me.m1key.audioliciousmigration

import org.specs._
import org.specs.runner._
import org.junit.runner.RunWith

@RunWith(classOf[JUnitSuiteRunner])
class LauncherSpec extends Specification with JUnit {
   
   "2 + 2" should {    
       "equal 4" in {
           2 + 2 mustBe 4
       }
   }

}
gradle install does not run tests!

Download source code

Source code for this article can be obtained via GitHub.

Guice

In Part 2 of this tutorial I will describe how to use Guice in this project.

2012-01-16

Hibernate - collection was not processed by flush

Recently I got the following exception from Hibernate:
collection was not processed by flush
What happened? I have the following class structure.
Artist -> Album -> Song (parent -> child)

Library -> Stat (parent -> child)

Song - Stat (association)
Artist and Library are aggregates in DDD sense. Artist is the parent of Album, Album is the parent of Song. Library is the parent of Stat. What's important, there's also a bidirectional relationship between Song and Stat (but no ownership). The exception occurred in the following situation:
  1. I had an Artist with one Album with one Song that had an association with one Stat being a child of its Library and that was created in Transaction 1.
  2. In Transaction 2, the whole thing was loaded again, a new Stat added, being a child of a new Library.
  3. Transaction 2 ended. Exception thrown.
So at the time when Transaction 2 ended I had a Song with 2 Stats, each referring to a different Library, but one of those Libraries was out of context, as it is lazy loaded from Stat.

The fix

The fix (or hack) was to eagerly load Library from Song. It's not ideal but it fixed the problem. There was no need to update this Library instance when transaction ended, so I'm wondering if it's a Hibernate defect...