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")
 }