Problem with BlazeDS and LiftFilter 1.0+

17 views
Skip to first unread message

oshyshko

unread,
Oct 31, 2009, 5:24:05 AM10/31/09
to Lift
Looks like "net.liftweb.http.LiftFilter" after version 1.0.+ breaks
BlazeDS servlet.

Version 0.9. of "net.liftweb.http.LiftFilter" works fine.
I want to switch to a newer version of Liftweb, but I can't.

Any ideas how to make LiftFilter 1.0.+ to work with BlazeDS servlet?


More info:
I used Scala 2.7.1 and Liftweb 0.9 with BlazeDS (works fine).

BlazeDS is simply a rpc-servlet that maps to "/messagebroker/*" path
and exposes your classes as web services.

====================================
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-
class>
</servlet>

<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping>
====================================


These web-services are simple Scala classes like this:
====================================
class UserService {
def list(): Array[User] = User.findAll().toArray
}

// User is a regular Lifweb Remapper class:
class User extends KeyedMapper[Long, User] { ... }
====================================

This servlet is wrapped by "net.liftweb.http.LiftFilter".
If not wrapped, all DB stuff inside UserService wouldn't work.
====================================
<filter>
<filter-name>LiftFilter</filter-name>
<filter-class>net.liftweb.http.LiftFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>/messagebroker/*</url-pattern>
</filter-mapping>
====================================

Now, If i change my POMs from:
<scala.version>2.7.1</scala.version>
<liftweb.version>0.9</liftweb.version>

to

<scala.version>2.7.4</scala.version>
<liftweb.version>1.0</liftweb.version>

<!-- or any 1.0+ :
<liftweb.version>1.1-M5</liftweb.version>
<liftweb.version>1.0.2</liftweb.version>
-->

Then its all broken and any access to "/messagebroker/*" returns:
====================================
HTTP/1.1 404 Not Found
Content-Type: text/html
Content-Length: 257
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: JSESSIONID=1dj9cjb4kegk7;Path=/
X-Lift-Version: 0.11-SNAPSHOT
Server: Jetty(6.1.21)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://
www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html><body>The Requested URL /messagebroker/amf was not found on this
server</body></html>
====================================

And this makes me bound to Liftweb 0.9.

Timothy Perrett

unread,
Oct 31, 2009, 6:57:35 AM10/31/09
to Lift
Have you set this in Boot.scala:

LiftRules.passNotFoundToChain = true

Without this Lift wont pass requests that it cant handle to other
servlets. I would recommend upgrading to 1.1-M6 and Scala 2.7.5 - the
API has changed *a lot* since 0.9

Cheers, Tim

oshyshko

unread,
Oct 31, 2009, 7:52:14 AM10/31/09
to Lift
Adding "LiftRules.passNotFoundToChain = true" didn't help, the
response has changed to:

================================
HTTP/1.1 200 OK
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: JSESSIONID=1a8mg0498vrwd;Path=/
Content-Length: 0
Server: Jetty(6.1.21)
================================

But your post led me to more experiments.
Here is a workaround for liftweb-1.0+:

Don't map LiftFilter to "/messagebroker/*" explicitely.

My current web.xml contains this:
================================
<filter>
<filter-name>LiftFilter</filter-name>
<filter-class>net.liftweb.http.LiftFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>

<!--
Removed this for Liftweb-1.0.+

<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>/messagebroker/*</url-pattern>
</filter-mapping>
-->
================================

I've verified this on scala-2.7.5 + liftweb-1.1M6.
Runs like a champ.

Thanks, Tim.

Timothy Perrett

unread,
Oct 31, 2009, 8:19:21 AM10/31/09
to Lift
You needed to specify the passNotFoundToChain var, thats why you were
getting the 404 originally - as you detail, its not needed to
explicitly set the /messagebroker URL in your web.xml.

Glad you got it working

Cheers, Tim

oshyshko

unread,
Nov 7, 2009, 1:15:26 PM11/7/09
to Lift
Removing BlazeDS servlet from LiftFilter coverage is a bad idea:
If it is done so, lift-mapper creates separate transactions for all
operations.

http://groups.google.com/group/liftweb/browse_thread/thread/e2c0dd2c5dcc458b

This means LiftFilter and BlazeDS servlet should become friends.

"LiftRules.passNotFoundToChain = true" is in my Boot.scala. The
response is:
================================
HTTP/1.1 200 OK
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Set-Cookie: JSESSIONID=1a8mg0498vrwd;Path=/
Content-Length: 0
Server: Jetty(6.1.21)
================================

Note: if I remove LiftFilter, the servlet works fine.

Any ideas how to make LiftFilter not to intrude into BlazeDS deeds?

David Pollak

unread,
Nov 10, 2009, 6:58:02 PM11/10/09
to lif...@googlegroups.com
Can you code up a full working example of what you want to do and post it on GitHub?
--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

glenn

unread,
Nov 10, 2009, 7:19:55 PM11/10/09
to Lift
David,

(This is a copy of an earlier reply to your Migration Guide post. I
didn't get any response there, so I'm listing it on this
thread since it seems somewhat related to the current discussion)

I attempted to follow your blog piece and rewrite the code in
Integrating Flex, BlazeDS, and Scala/Lift, at http://flexonrails.net/?p=103.

Maybe I'm being a bit ambitious to redo this, but when I run just the
Lift portion (without Flex/BazeDS) and make a call to my LiftActor
implementation, nothing happens. Shouldn't the
messageHandler be called automatically (There is nothing to "start",
is there?). It doesn't when I trace through the code. What am I doing
wrong?

Here's my rewrite of Notifier using LiftActor:

class Notifier extends LiftActor{

val msgBroker = MessageBroker.getMessageBroker(null)
val clientID = UUIDUtils.createUUID()
val msg = new AsyncMessage()
var notificationsSent = 0;

val currentTime = new Date().getTime();

protected def messageHandler = {
case Notify =>{
msg.setDestination("notifications")
msg.setClientId(clientID)
msg.setTimestamp(currentTime)
msg.setBody(new Notification(notificationsSent, "Hello from
Scala/Lift", new Date()))
msgBroker.routeMessageToService(msg,null)
notificationsSent = 1

}

LAPinger.schedule(this, Notify, 500L)

}

}

case object Notify

class Notification(var id: Int, var message: String, var timesent:
Date){
def getId = id
def setId(id: Int) = this.id = id
def getMessage = message
def setMessage(m: String) = message = m
def getTimesent = timesent
def setTimesent(t: Date) = timesent = t

}

I also have an XMLApiHelper with:

def dispatch: LiftRules.DispatchPF = {
case Req("webservices" :: c :: Nil, "", GetRequest)=> () =>
start_feed(c:String)

and start_feed simply calls new Notifiier().

Given this code, the URL: http://localhost:8080/webservices/Notify
successfully calls into
start_feed and creates Notifier but the messageHandler isn't called.

Any help is appreciated?

Thanks,

Glenn

On Nov 10, 3:58 pm, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> Can you code up a full working example of what you want to do and post it on
> GitHub?
>
>
>
> On Sat, Nov 7, 2009 at 10:15 AM, oshyshko <oshys...@gmail.com> wrote:
>
> > Removing BlazeDS servlet from LiftFilter coverage is a bad idea:
> > If it is done so, lift-mapper creates separate transactions for all
> > operations.
>
> >http://groups.google.com/group/liftweb/browse_thread/thread/e2c0dd2c5...
>
> > This means LiftFilter and BlazeDS servlet should become friends.
>
> > "LiftRules.passNotFoundToChain = true" is in my Boot.scala. The
> > response is:
> > ================================
> > HTTP/1.1 200 OK
> > Expires: Thu, 01 Jan 1970 00:00:00 GMT
> > Set-Cookie: JSESSIONID=1a8mg0498vrwd;Path=/
> > Content-Length: 0
> > Server: Jetty(6.1.21)
> > ================================
>
> > Note: if I remove LiftFilter, the servlet works fine.
>
> > Any ideas how to make LiftFilter not to intrude into BlazeDS deeds?
>
> > On Oct 31, 2:19 pm, Timothy Perrett <timo...@getintheloop.eu> wrote:
> > > You needed to specify the passNotFoundToChain var, thats why you were
> > > getting the 404 originally - as you detail, its not needed to
> > > explicitly set the /messagebroker URL in your web.xml.
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net
> Beginning Scalahttp://www.apress.com/book/view/1430219890

David Pollak

unread,
Nov 10, 2009, 7:32:18 PM11/10/09
to lif...@googlegroups.com

messageHandler will be called when a message is put into the Actor's mailbox.  There's nothing in any of this code that sends a Notify message to a Notifier instance.  That would look like:

val myNotifier = new Notifier
myNotifier ! Notify

Note that you have a:   LAPinger.schedule(this, Notify, 500L) call that's *inside* the Notify message handler case.  This will send a Notify message to this in 500 ms, but that scheduling will only happen as part of a Notify message.  If you change the code to:



class Notifier extends LiftActor{

 val msgBroker = MessageBroker.getMessageBroker(null)
 val clientID = UUIDUtils.createUUID()
 val msg = new AsyncMessage()
 var notificationsSent = 0;

 val currentTime =  new Date().getTime();

 protected def messageHandler = {
   case Notify =>{
     msg.setDestination("notifications")
     msg.setClientId(clientID)
     msg.setTimestamp(currentTime)
     msg.setBody(new Notification(notificationsSent, "Hello from Scala/Lift", new Date()))
     msgBroker.routeMessageToService(msg,null)
     notificationsSent = 1

    }
  }
 
  LAPinger.schedule(this, Notify, 500L)


}


The LAPinger call is part of the Notifier constructor (yeah, I think the random constructors all over the class body is a mistake, but Martin doesn't) and you'll get pinged in 500 ms.

Also, as a process note, I've been swamped with family issues over the last 3 weeks (our nanny's finally back so I don't have 1/2 of my workday devoted to childcare), Derek's traveling with his family and Tim and Marius are swamped at work.  The questions on the list have backed up quite a bit.  I'm hoping to try to get some of the backlog of questions and tickets resolved.

Thanks,

David

 



--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890

oshyshko

unread,
Nov 12, 2009, 5:14:15 PM11/12/09
to Lift
Hello David,

Here is source code.

http://github.com/oshyshko/lift_vs_blazeds

1. Run it via:
$ mvn jetty:run-exploded

2. Go to http://localhost:8080/
You should see a SWF with a text field and a button (you will need
Flash Player 10):
[Kaboom!] (Say it)

3. Press "Say it"

4. Watch Jetty's reply in console:
Kaboom!

5. Stop Jetty, open web.xml, find this snipped and uncomment
LiftFilter mapping:
<!-- uncomment this -->
<!--
<filter-mapping>
<filter-name>LiftFilter</filter-name>
<url-pattern>*</url-pattern>
</filter-mapping>
-->

6. Run Jetty
$ mvn jetty:run-exploded

7. Go to http://localhost:8080/
8. Press "Say it"
9. Watch Jetty's console - message "Kaboom!" will not appear

On Nov 11, 2:32 am, David Pollak <feeder.of.the.be...@gmail.com>
wrote:
> ...

David Pollak

unread,
Nov 12, 2009, 7:29:35 PM11/12/09
to lif...@googlegroups.com
the issue is the new debugging help screen in dev mode got in the way of the passing not found... so...

package services

import net.liftweb.http._
import net.liftweb.mapper._
import net.liftweb.util._
import java.sql.{Connection, DriverManager}

class Boot extends Bootable {
  def boot {
    LiftRules.liftRequest.append {
      case Req("messagebroker" :: _, _, _) =>    false
    }


    LiftRules.passNotFoundToChain = true
  }
}

What the code does is it looks for calls to /messagebroker/* and ensures these are not Lift calls.

You can find working code at http://github.com/dpp/lift_vs_blazeds

Thanks,

David

oshyshko

unread,
Nov 12, 2009, 8:16:28 PM11/12/09
to Lift
Hello David,

I have added some DB stuff + added "S.addAround(DB.buildLoanWrapper)"
But the code produces separate transactions.

For example, this code:
=========================================
class Service {
def say(message: String) {
println(message)

(0 to 5).foreach( i => User.findAll )
}
}
=========================================


Produces this SQL:
=========================================
091113 2:59:24 6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query commit
6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query commit
6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query SELECT user_t.id FROM user_t
6 Query commit
6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query SELECT user_t.id FROM user_t
6 Query commit
6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query SELECT user_t.id FROM user_t
6 Query commit
6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query SELECT user_t.id FROM user_t
6 Query commit
6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query SELECT user_t.id FROM user_t
6 Query commit
6 Query SET autocommit=0
6 Query SET autocommit=0
6 Query SELECT user_t.id FROM user_t
6 Query commit
=========================================

Basically it behaves like when LiftFilter url mapping was commented in
"web.xml".

I have updated http://github.com/oshyshko/lift_vs_blazeds

To reproduce it:

1. mysql -> create database demo;

2. Enable MySQL query logging by editing "my.ini" + restart MySQL
daemon:
[mysqld]
log=mysql-log.log

3. Open separate console to watch what's going on
$ tail -f mysql-log.log

4. mvn jetty:run-exploded

5. Open http://localhost:8080/ + click "Say it"

6. Observe 7 transactions instead of 1 in the log
should =>
6. Observe all queries inside 1 transaction in the log

On Nov 13, 2:29 am, David Pollak <feeder.of.the.be...@gmail.com>
wrote:

David Pollak

unread,
Nov 12, 2009, 11:56:23 PM11/12/09
to lif...@googlegroups.com
Oshyshki,

Your call to Service.say is outside the scope of the Lift HTTP request, so it's not wrapped with the transaction management stuff.  You could insert:

DB.use(DefaultConnectionIdentifier) {
  ignore =>

David Pollak

unread,
Nov 12, 2009, 11:58:52 PM11/12/09
to lif...@googlegroups.com
Oshyshki,

Your call to Service.say is outside the scope of the Lift HTTP request, so it's not wrapped with the transaction management stuff.  You could insert:

DB.use(
DefaultConnectionIdentifier) {
  ignore =>
  (0 to 5).foreach(i => User.findAll)
}

Inside your say method and all of the requests would be part of the same transaction.

But to demonstrate that the DB.buildLoanWrapper stuff works correctly, I added a standard HTML form to the page.  This form is serviced within the Lift scope, so the Mapper transaction stuff is done correctly.  Please apply the enclosed diff to your Git project.

Thanks,

David


On Thu, Nov 12, 2009 at 5:16 PM, oshyshko <oshy...@gmail.com> wrote:
scope.diff

oshyshko

unread,
Nov 13, 2009, 6:08:20 AM11/13/09
to Lift
David,

> Your call to Service.say is outside the scope of the Lift HTTP request, so
> it's not wrapped with the transaction management stuff.

Exactly.

The thing that I want LiftFilter to wrap "/messagebroker/*", so I will
get per-HTTP request
automatic transaction management and my code will remain clean.

LiftFilter 0.9 and BlazeDS worked perfectly together,
but when I switched to Lift 1.0 and greater, LiftFilter killed
BlazeDS.

Removing mappings from "web.xml" or adding
"LiftRules.liftRequest.append { case Req("messagebroker" :: _, _, _)
=> false }"
didn't really solved the problem but diverged it: BlazeDS is alive,
but it made nice features of Lift useless.

I believe that these are attempts to cure symptoms while disease is
still here: LiftFilter kills BlazeDS.

It would be great to have a solution like this:
- LiftFilter wraps "/messagebroker/*" but doesn't change requests, so
BlazeDS servlet still working
- methods like Service.say gain transaction management from Lift and
its code remains clean

--
Oleksandr

David Pollak

unread,
Nov 13, 2009, 8:47:18 AM11/13/09
to lif...@googlegroups.com
On Fri, Nov 13, 2009 at 3:08 AM, oshyshko <oshy...@gmail.com> wrote:

David,

> Your call to Service.say is outside the scope of the Lift HTTP request, so
> it's not wrapped with the transaction management stuff.

Exactly.

The thing that I want LiftFilter to wrap "/messagebroker/*", so I will
get per-HTTP request
automatic transaction management and my code will remain clean.

Sorry.  You can either pass the request onto the Filter chain or handle it in Lift.  You cannot do both.
 

LiftFilter 0.9 and BlazeDS worked perfectly together,
but when I switched to Lift 1.0 and greater, LiftFilter killed
BlazeDS.

First, this sounds like a bug in Lift 0.9.  If Lift 0.9 was wrapping the call to filter.chain in the Lift request context, that's definitely a bug in Lift.

Second, Lift did not "kill" BlazeDS.  As I demonstrated, Lift is able to pass the request down the filter chain to BlazeDS without impacting the request or the request state in any way.
 

Removing mappings from "web.xml" or adding
"LiftRules.liftRequest.append { case Req("messagebroker" :: _, _, _)
=> false }"
didn't really solved the problem but diverged it: BlazeDS is alive,
but it made nice features of Lift useless.

They are not useless.  The issue you're facing is the the S context is not available "around" your method calls.  If there's a way to extract the JSESSIONID from BlazeDS, you can call SessionMaster.getSession(jsessionId, Empty).  With the resulting session, you can call S.initIfUninitted(foundSession) { your code here }

I don't know how BlazeDS works, but it may be possible to do something that sets up this kind of context around the dispatch code and you'd have the Lift session context and BlazeDS.
 

I believe that these are attempts to cure symptoms while disease is
still here: LiftFilter kills BlazeDS.

No, it most certainly does not.  Repeating that refrain is not helping address the issue.
 

It would be great to have a solution like this:
- LiftFilter wraps "/messagebroker/*" but doesn't change requests, so
BlazeDS servlet still working
- methods like Service.say gain transaction management from Lift and
its code remains clean

Unfortunately, without digging into BlazeDS, I can't help beyond the suggestion above.

Thanks,

David
 

--
Oleksandr


oshyshko

unread,
Nov 13, 2009, 11:52:41 AM11/13/09
to Lift
Hello David,

May be there can be a compromise solution.

A custom filter that has only these duties:
- to open LiftMapper-compatible transaction on HTTP-request (it will
be accessible from DB entities like User)
- call chain
- when all chain was processed
+ commit the transaction
+ in case of exception, roll back the transaction + propagate
exception

<filter>
<filter-name>LiftFilterTransactionWrapper</filter-name>
<filter-class>services.LiftFilterTransactionWrapper </filter-
class>
</filter>

<filter-mapping>
<filter-name> LiftFilterTransactionWrapper </filter-name>
<url-pattern>/messagebroker/*</url-pattern>
</filter-mapping>

class LiftFilterTransactionWrapper ... {
...
}

This will be just enough.

What should be inside LiftFilterTransactionWrapper?

--
Oleksandr
Reply all
Reply to author
Forward
0 new messages