[Lift] Re: Weird RequestVar behavior (or something else?)
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?)
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?)
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 -~--~~~~--~~--~--~---