Wednesday, February 6, 2008

Accessing the spring framework from LiftWeb

One of the benefits (if you can figure it out) of working with the JVM languages is the ability to integrate technologies. One of the problems is how to do so. The botlist web application is built on JRuby and Spring. I am now building future functionality with Scala/Lift and Spring.

If you have worked with spring; the spring ApplicationContext contains a link between the servlet world to the spring world. In this lift example, I extract the application context through the http servlet request and the session instance.

def getAC(request: HttpServletRequest) = {
val sess = request.getSession
val sc = sess.getServletContext

// Cast to the application context
val acobj = sc.getAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.botlistings")
acobj.asInstanceOf[AC]
}


After getting the application context, it is faily straight-forward to access the spring bean objects.


// Cast to the user visit log bean (defined in the spring configuration)
val log_obj = AgentUtil.getAC(httpRequest).getBean("userVisitLogDaoBean")
val log_dao = log_obj.asInstanceOf[LogDAO]
AgentUtil.auditLogPage(log_dao, httpRequest, "remote_agent")



val link = new Log()
link.setRequestUri(request.getRequestURI)
link.setRequestPage(curPage)
link.setHost(request.getHeader("host"))
link.setReferer(request.getHeader("referer"))
link.setRemoteHost(request.getRemoteAddr())
link.setUserAgent(request.getHeader("user-agent"))
dao.createVisitLog(link)


That is the core of linking lift to spring. Here is the full example.

RemoteAgents.scala

package org.spirit.lift.agents

import java.util.Random
import org.springframework.context.{ApplicationContext => AC}
import org.spirit.dao.impl.{BotListUserVisitLogDAOImpl => LogDAO}
import org.spirit.bean.impl.{BotListUserVisitLog => Log}
import net.liftweb.http._
import net.liftweb.http.S._
import net.liftweb.http.S
import scala.xml.{NodeSeq, Text, Group}
import net.liftweb.util.Helpers._
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse, HttpSession}


object AgentUtil {
def uniqueMsgId (clientip: String) : String = {
val r = new Random()
val rand_long = r.nextLong
hexEncode(md5( (clientip + rand_long).getBytes ))
}
def auditLogPage (dao: LogDAO, request: HttpServletRequest, curPage: String) = {
val link = new Log()
link.setRequestUri(request.getRequestURI)
link.setRequestPage(curPage)
link.setHost(request.getHeader("host"))
link.setReferer(request.getHeader("referer"))
link.setRemoteHost(request.getRemoteAddr())
link.setUserAgent(request.getHeader("user-agent"))
dao.createVisitLog(link)
}

def getAC(request: HttpServletRequest) = {
val sess = request.getSession
val sc = sess.getServletContext

// Cast to the application context
val acobj = sc.getAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.botlistings")
acobj.asInstanceOf[AC]
}
}
/**
* Example request:
* http://localhost:8080/botlist/lift/pipes/agents/remote_agent
*/
class RemoteAgents (val request: RequestState, val httpRequest: HttpServletRequest) extends SimpleController {
def remote_agent: XmlResponse = {
// Cast to the user visit log bean (defined in the spring configuration)
val log_obj = AgentUtil.getAC(httpRequest).getBean("userVisitLogDaoBean")
val log_dao = log_obj.asInstanceOf[LogDAO]
AgentUtil.auditLogPage(log_dao, httpRequest, "remote_agent")

XmlResponse(
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:botmsg="http://xmlns.com/botmsg/0.1/" >
<botmsg:agentmsg>
<botmsg:botid>serverbot</botmsg:botid>
<botmsg:message>Hello my name is serverbot, go ahead with your request</botmsg:message>
<botmsg:status>200</botmsg:status>
<botmsg:requestid>{ Text(AgentUtil.uniqueMsgId(httpRequest.getRemoteAddr)) }</botmsg:requestid>
<botmsg:majorvers>0</botmsg:majorvers>
<botmsg:minorvers>0</botmsg:minorvers>
</botmsg:agentmsg>
</rdf:RDF>)
} // End of Method
}


Boot.scala

package bootstrap.liftweb

import net.liftweb.http._
import net.liftweb.util.{Helpers, Can, Full, Empty, Failure, Log}
import javax.servlet.http.{HttpServlet, HttpServletRequest , HttpServletResponse, HttpSession}
import scala.collection.immutable.TreeMap
import Helpers._

import org.spirit.lift.agents._

class Boot {
def boot {
LiftServlet.addToPackages("org.spirit.lift.agents")
val dispatcher: LiftServlet.DispatchPf = {
// if it's a web service, pass it to the web services invoker
case RequestMatcher(r, ParsePath("lift" :: "pipes" :: "agents" :: c :: _, _,_),_, _) => invokeAgents(r, c)
}
LiftServlet.addDispatchBefore(dispatcher)
}
private def invokeAgents(request: RequestState, methodName: String)(req: HttpServletRequest): Can[ResponseIt] =
createInvoker(methodName, new RemoteAgents(request, req)).flatMap(_() match {
case Full(ret: ResponseIt) => Full(ret)
case _ => Empty
})
} // End of Class