[Lift] Re: Weird RequestVar behavior (or something else?)

2008-09-29 Thread David Pollak
Kris,

If your goal is to have some cleanup function added at the end of the
request, you can do the following:

trait Cleanable {def cleanup: Unit}

class MyStuff {
  var needsCleanup: Can[Cleanable] = Empty

  S.addCleanupFunc(() = needsCleanup.foreach(_.cleanup))
}

So, in the above code, if you assign some value to needsCleanup, it will be
cleaned up at the end of the request.

Does this help point you in the right direction?

Thanks,

David

On Mon, Sep 29, 2008 at 11:46 AM, Kris Nuttycombe [EMAIL PROTECTED]
 wrote:


 I'm not opposed to that design, but I do think that it's a little
 unexpected that any declaration of a RequestVar essentially creates a
 singleton. This is probably once again something that could be solved
 with a bit better documentation. It's surprising to use a class in a
 pattern where one would ordinarily expect to create independent
 instances and to get singletons instead.

 What other mechanisms exist to bind to Lift's lifecycle hooks?
 RequestVar does essentially what I need it to do in my hack, but
 relying upon the internal implementation of AnyVar makes me a little
 queasy.

 Kris

 On Mon, Sep 29, 2008 at 12:14 PM, David Pollak
 [EMAIL PROTECTED] wrote:
  Kris,
 
  You are correct that RequestVar and SessionVar reference the same
 underlying
  data no matter what instances they are part of.  There are a bunch of
 design
  reasons for this.  If you have a bunch of instance variables that have
  different things that need cleaning up, I'd suggest not using RequestVar.
 
  Sorry,
 
  David
 
  On Sat, Sep 27, 2008 at 11:34 AM, Kris Nuttycombe
  [EMAIL PROTECTED] wrote:
 
  For the record, here's my ugly-ass workaround, depending upon the fact
  that cleanupFunc is called to obtain the cleanup function at the same
  time that the default value is set.
 
  import javax.naming.InitialContext
  import net.liftweb.util.{Can,Full,Log}
  import net.liftweb.http.RequestVar
 
  object JNDIResource {
   val context = new InitialContext()
  }
 
  import JNDIResource._
 
  abstract class JNDIResource[T](val name: String) extends
  RequestVar[T](context.lookup(name).asInstanceOf[T]) {
 
   // This is way too dependent upon an implementation detail of the
  superclass.
   override def cleanupFunc : Can[() = Unit] = {
 Log.debug(Initializing JNDI resource  + name + ( + this.is +
 ))
 initialize(this.is) //this will result in a recursive call, but
  the the order of operations is such that it will take the other
  branch.
 
 Full(() = {
 Log.debug(Releasing JNDI resource  + name + ( + this.is +
 ))
 dispose(this.is)
   })
   }
 
   /**
* Subclasses should override this method to provide initialization
  of the resource
*/
   protected def initialize(resource : T) {
   }
 
   /**
* Subclasses should override this method to provide cleanup on the
  resource
*/
   protected def dispose(resource: T) {
   }
  }
 
 
 
 
 
  --
  Lift, the simply functional web framework http://liftweb.net
  Collaborative Task Management http://much4.us
  Follow me: http://twitter.com/dpp
  Git some: http://github.com/dpp
 
  
 

 



-- 
Lift, the simply functional web framework http://liftweb.net
Collaborative Task Management http://much4.us
Follow me: http://twitter.com/dpp
Git some: http://github.com/dpp

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
Lift group.
To post to this group, send email to liftweb@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/liftweb?hl=en
-~--~~~~--~~--~--~---



[Lift] Re: Weird RequestVar behavior (or something else?)

2008-09-29 Thread David Pollak
Kris,

I'd do something like:

object MyTransactionThingy extends LoanWrapper {
  private val transactions = new ThreadGlobal[Map[String, Transaction]]

  def apply[T](f: = T): T = {
transactions.doWith(Map.empty){
   val ret = f
   transactions.value.values.foreach(_.cleanup)
}   
  }

  def transaction(name: String): Transaction = {
 transactions.value.get(name) match {
   case Some(v) = v
   case None =
 val ret = new Transaction
 transactions.set(transactions.value + (name = ret))
 ret
 }
  }
}

In Boot, do:
  S.addAround(List(MyTransactionThingy))

In the above code, replace String with whatever the identifier is for 
looking up Transactions and Transaction with whatever JTA representation 
there is.

Thanks,

David

Kris Nuttycombe wrote:
 I'm not sure how closely you've looked at the code I posted for
 JNDIResource, but my intention was essentially to create a subclass of
 RequestVar customized for managing JNDI resources that provided ways
 to override both initialization and cleanup of the resource. I thought
 that RequestVar would be appropriate because I don't want some of
 those resources (specifically transactions) to be hanging around
 longer than a request; this work is an extension of the JPA stuff that
 Derek's been doing to enable JTA-type persistence units instead of
 just resource-local.

 In this case, I'm managing two types of interdependent resources,
 EntityManagers and UserTransactions. I can't use an EntityManager
 without being within the scope of an active UserTransaction. So
 cleanup is one issue, but so is initialization - when I obtain an
 EntityManager instance, I want it to be guaranteed that an active
 UserTransaction is available. The thing that's really hackish about my
 implementation of cleanupFunc is that I'm depending upon the call to
 cleanupFunc that occurs in the implementation of AnyVar#is that's
 nominally intended to register the cleanup function for the default
 value. Since dflt is a call-by-name parameter, I can't extensibly tack
 a call to an additional initialization function on there, and I can't
 put the call to the initialization in the constructor because that's
 not guaranteed to be called on every request; in the AnyVar#is
 function right where cleanupFunc is called is about the only place I
 could figure out that would reliably do that setup.

 Kris

 On Mon, Sep 29, 2008 at 2:12 PM, David Pollak
 [EMAIL PROTECTED] wrote:
   
 Kris,

 If your goal is to have some cleanup function added at the end of the
 request, you can do the following:

 trait Cleanable {def cleanup: Unit}

 class MyStuff {
   var needsCleanup: Can[Cleanable] = Empty

   S.addCleanupFunc(() = needsCleanup.foreach(_.cleanup))
 }

 So, in the above code, if you assign some value to needsCleanup, it will be
 cleaned up at the end of the request.

 Does this help point you in the right direction?

 Thanks,

 David

 On Mon, Sep 29, 2008 at 11:46 AM, Kris Nuttycombe
 [EMAIL PROTECTED] wrote:
 
 I'm not opposed to that design, but I do think that it's a little
 unexpected that any declaration of a RequestVar essentially creates a
 singleton. This is probably once again something that could be solved
 with a bit better documentation. It's surprising to use a class in a
 pattern where one would ordinarily expect to create independent
 instances and to get singletons instead.

 What other mechanisms exist to bind to Lift's lifecycle hooks?
 RequestVar does essentially what I need it to do in my hack, but
 relying upon the internal implementation of AnyVar makes me a little
 queasy.

 Kris

 On Mon, Sep 29, 2008 at 12:14 PM, David Pollak
 [EMAIL PROTECTED] wrote:
   
 Kris,

 You are correct that RequestVar and SessionVar reference the same
 underlying
 data no matter what instances they are part of.  There are a bunch of
 design
 reasons for this.  If you have a bunch of instance variables that have
 different things that need cleaning up, I'd suggest not using
 RequestVar.

 Sorry,

 David

 On Sat, Sep 27, 2008 at 11:34 AM, Kris Nuttycombe
 [EMAIL PROTECTED] wrote:
 
 For the record, here's my ugly-ass workaround, depending upon the fact
 that cleanupFunc is called to obtain the cleanup function at the same
 time that the default value is set.

 import javax.naming.InitialContext
 import net.liftweb.util.{Can,Full,Log}
 import net.liftweb.http.RequestVar

 object JNDIResource {
  val context = new InitialContext()
 }

 import JNDIResource._

 abstract class JNDIResource[T](val name: String) extends
 RequestVar[T](context.lookup(name).asInstanceOf[T]) {

  // This is way too dependent upon an implementation detail of the
 superclass.
  override def cleanupFunc : Can[() = Unit] = {
Log.debug(Initializing JNDI resource  + name + ( + this.is +
 ))
initialize(this.is) //this will result in a recursive call, but
 the the order of operations is such that it will take the other
 branch.

Full(() = {

[Lift] Re: Weird RequestVar behavior (or something else?)

2008-09-26 Thread Kris Nuttycombe

Well, I figured out what's going on. RequestVar and SessionVar depend
upon their *class name* for uniqueness. So, you can't just create a
RequestVar instance and expect uniqueness, and in fact if you create a
RequestVar as a member of a reusable superclass like my JNDIResource,
even with a declaration like object foo extends RequestVar, it will
be treated as the same instance in all of the subclasses. If it's
created as an anonymous class, it just gets a name like
DeclaringClass$$anon$1, the same class name for each instance.

BLEAH.

This violated my expectations on a number of levels, and cost me
several hours of hunting to figure out. I guess that if one wants to
create some abstraction that simplifies the setup of some class of
RequestVar instances, the only way to do so is to ensure that this
abstraction is itself abstract. Of course, if someone goes to extend
your abstraction, they have to know that *their* abstraction also must
be abstract and never instantiated directly lest they get name
collisions...

This all seems far too complicated for what RequestVar is doing -
associating a variable with the request handling lifecycle in a
typesafe manner, though I now understand why it was done this way. So,
would it break things to add some random salt to the variable name
that's being mapped into that hash? I can work around the issue, I
think, but it would be nice if the behavior wasn't quite so
unexpected.

Kris

--~--~-~--~~~---~--~~
You received this message because you are subscribed to the Google Groups 
Lift group.
To post to this group, send email to liftweb@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/liftweb?hl=en
-~--~~~~--~~--~--~---