Thursday, December 11, 2008

GRUB and arrow keys

OK, not really related to coding but I thought it would be useful to put this out there in case anyone runs into the same issue. I'm trying to use a remote VMWare console for a linux VM I'm running at work and for some reason the arrow keys won't work. That means that I can't move up and down to select the recovery boot. I finally figured out, after much trial and error, that you can use Ctrl-P for "up" and Ctrl-N for "down". Good luck!

Thursday, April 3, 2008

More progress on JPA

I've refactored my code now that I have a request-per-session. So far, this is what the Model object looks like:


object Model extends Logger {
def logname = "Model"

lazy val factory = Persistence.createEntityManagerFactory("foo")

// Per-request entity manager
//object emVar extends RequestVar[EntityManager](null)
//def em = emVar.is

// Temporarily using ThreadLocal until we get lifecycle handling in RequestVar
val emVar = new ThreadLocal[EntityManager]
def em = emVar.get()

/**
This method allows me to clean up my code a bit and only handle JPA-related exceptions.
An example usage would be:

def addFood(newFood : Food) =
wrapEM({
em.persist(newFood)
S.redirectTo("/food/list.html")
}, {
case cve : ConstraintViolationException => S.error("That food already exists!")
case _ => S.error("Internal error adding food")
})

Note that if I used normal try/catch then the wildcard match would trap the RedirectException
thrown by S.redirectTo.
*/
def wrapEM[A](f : => A, handler : PartialFunction[Throwable, A]) : A = {
try {
val tx = em.getTransaction()

tx.begin()
try {
val ret : A = f
ret
} finally {
// make sure that we commit even with a redirectexception
tx.commit()
}
} catch {
// Special case. Usually we want to know why it failed to commit, not just that it failed
case re : RollbackException => {
val (cause,message) = if (re.getCause() == null) {
(re,"No cause")
} else {
(re.getCause(), re.getCause().getMessage())
}
this.error("EM Commit error: {}", message)
handler(cause)
}
case he : HibernateException => {
this.error("Hibernate error", he)
handler(he)
}
case pe : PersistenceException => {
this.error("EM Error", pe)
handler(pe)
}
}
}

/**
Bridging utility method. This should eventually go away.
*/

def usingEM[A](f: EntityManager => Can[A]) : Can[A] = wrapEM(
{f(em)}, {
case e : Exception => Failure(e.getMessage(), Full(e), Nil)
})

/**
Loads the given object and returns a Full can with the object if it could be loaded, Empty if it's not found.
*/
def load[A](id : java.lang.Long, clazz : Class[A]) : Can[A] = load(this.em, id, clazz)
def load[A](em : EntityManager, id : java.lang.Long, clazz : Class[A]) : Can[A] = em.find(clazz, id) match {
case found : A => Full(found)
case null => Empty
}

/**
Queries for a single instance of an object based on the queryName and params.

@return Full(x) if a single result is returned, Empty if no results are returned, Failure on any errors

*/
def find[A](providedEM : EntityManager, queryName : String, params : Pair[String,Any]*) : Can[A] = {
try {
val query = providedEM.createNamedQuery(queryName)

params foreach { param => query.setParameter(param._1, param._2) }

Full(query.getSingleResult().asInstanceOf[A])
} catch {
case e: NoResultException => Empty
}
}

/**
Queries for a result list of objects based on the queryName and params

@return A scala.collection.jcl.Buffer of results
*/
def findAll[A](em : EntityManager, queryName : String, params : Pair[String,Any]*) : Buffer[A] = {
val query = em.createNamedQuery(queryName)

params foreach { param => query.setParameter(param._1, param._2) }

val result = query.getResultList().asInstanceOf[java.util.List[A]]

new BufferWrapper[A] { override def underlying = result }
}

// Implicit defs to help with entity member access
implicit def setToWrapper[A](set : java.util.Set[A]) = new SetWrapper[A]{override def underlying = set}
implicit def listToWrapper[A](list : java.util.List[A]) = new BufferWrapper[A]{override def underlying = list}
}


I just found out that I can do JNDI and JTA in Jetty, so I might take a crack at getting that working once I've ripped out all of the old JPA access code in my various classes.

Wednesday, April 2, 2008

Evolution of JPA from Lift

Edit: Forgot that I could use RequestVar. Also, Look at my next post for a further evolution of the Model object

Well, with some suggestions and info from David and Viktor I've started overhauling my use of JPA from within Lift. First and foremost, I've added in support for an EntityManager-per-request. This requires a little setup and utility class in my case:


object Model {
def createEM() = // fill this in however you want: JNDI, local Persistence, etc

// functions for setting a per-request entity manager
object emVar extends RequestVar[EntityManager](null)
def em = emVar.is

def wrapEM(f : => Unit, msg : String) : Unit = {
try {
f
} catch {
case e : Exception => this.error("Unexpected exception", e); S.error(msg)
}
}
}


The wrapEM method is something I've put together to make my code a little more concise when all I want to do is display an error when something goes wrong. The real meat of EM-per-session happens in Boot:


// Set up a LoanWrapper to automatically instantiate and tear down the EntityManager on a per-request basis
S.addAround(List(
new LoanWrapper {
def apply[T] (f : => T): T = {
val em = Model.createEM()

// Add EM into S scope
Model.emVar(em)

try {
f
} finally {
em.close()
}
}
}
))

Monday, March 31, 2008

JPA From Lift

This has been discussed a little on the lift mailing list so I thought I'd write something up. Lift has a nice, built-in mapper framework for doing database-object mapping, but it's geared mainly toward small, lightweight schemas. When you start to throw a lot of tables at it, or if you need to do legacy or out-of-the-mainstream work, you can start to run into limitations or concept mistmatches that can end up making more sophisticated (and heavyweight) frameworks look more attractive. In my case I have an existing Java project that has its own persistence layer using JPA (Hibernate EntityManager), so it really seemed like it would be best to reuse the existing code rather than trying to forklift the entire thing.

To start with, accessing JPA entities from Scala is really no different that accessing them from Java. In either case you're getting a JPA EntityManager interface from somewhere (JNDI in my case) and using the persist, merge, etc. methods to control object persistence management. There are, however, a few things you can do with Scala to make it a little simpler to use and more Scala-ish.

The first issue I came to was that all of my JPA entities use Java collections for their collection interfaces. In my case they're all java.util.Sets, but it's nicer to have a Scala set to get access to all of the nice methods like exists, toList, etc. For that, I start out with an implicit definition:


implicit def utilSetToSet[A] (s : java.util.Set[A]) =
new SetWrapper[A] { override def underlying = s}


That way, when I access a set member of a given entity, I can automatically use it as a Scala set.

The next thing I've added to make things a little easier is to create a "using" method to automatically handle closing the entity manager and handling any exceptions thrown:


def usingEM[A](f : EntityManager => Can[A]) : Can[A] = {
val em = emf.createEntityManager()

try {
f(em)
} catch {
case e: Exception => Failure(e.getMessage(), Full(e), Nil)
} finally {
em.close()
}
}


I've been vacillating on this particular method, since in the past I usually have used a filter to open an EntityManager and then close it at the end. Doing it this way allows me to better handle errors via Scala's nice pattern matching, but it also makes the code more "baroque":


object entityName extends RequestVar[Can[String]](Empty)
def view (xhtml : NodeSeq) : NodeSeq = usingEM({em =>
val myEntity = em.createNamedQuery("findEntityByName")
.setParamater("name", entityName.is openOr "non-existant")
.getSingleResult()

Full(bind("me", xhtml, // blah blah))
}) match {
case Failure(_, e : NoResultException, _) => S.error("Could not find entity by name")
case Failure(msg, exception, _) => log(msg, exception); S.error("Error retrieving entity"); Text("")
case Full(x) => x
}


I'm not convinced that this is the right way to do this so I'm open to suggestions.

Derek

Tuesday, March 25, 2008

Lift rocks!

I've been spending the past few months working on various projects (personal and work-related) based on the liftweb framework. It's been a heck of a learning process tackling Scala and a new web framework but it's been very enjoyable. I've worked a lot with Struts and Tapestry in the past and Lift just feels, well, more natural. Things are simpler (once I've figured them out) and make much more sense. The hardest challenge has been keeping up with the rapid pace at which Scala and Lift are evolving. Just following the mailing lists takes up a small but significant chunk of each day.

I started seriously using Lift around the time it was version 0.2, June of 2007. Here it is not even April '08 and we're on the brink of 0.7. In the intervening months a lot has changed and my understanding of the internal mechanisms has improved significantly. Being able to browse through the source (http://code.google.com/p/liftweb/) has helped me not only learn how Lift works, but how Scala works as well. Having never done functional programming before it's a lot like drinking from a firehose but I'm managing.

The only thing I'm not happy with in this whole situation is that my prospects for working with other "Lifties" is pretty limited. It seems like everyone involved with Lift is either in San Francisco, Europe, or Oz :( I mean, it's fun as a personal challenge but I really miss being able to collaborate with other people. Hopefully, as the framework and language grow there will be more opportunities. Either way, I'm definitely having fun in the meantime.