Dependency-management

5 views
Skip to first unread message

Viktor Klang

unread,
Feb 9, 2008, 12:25:49 PM2/9/08
to lif...@googlegroups.com
Hey folks,

a situation not unknown to Java developers is libraries that exist upon compilation, but is somehow missing when the application is deployed.

Currently /lift/ behaves IMHO somewhat naughty by emitting a nasty page that looks somewhat like this upon a CLE (ClassLoadError)

Exception occured while processing /user_mgt/sign_up
Message: java.lang.NoClassDefFoundError: javax/mail/Address
net.liftweb.mapper.MetaMegaProtoUser$class.sendValidationEmail(ProtoUser.scala:213)
com.crap.model.User$.sendValidationEmail(User.scala:9)
net.liftweb.mapper.MetaMegaProtoUser$class.testSignup$1(ProtoUser.scala:227)
net.liftweb.mapper.MetaMegaProtoUser$$anonfun$innerSignup$1$1.apply(ProtoUser.scala:240)
net.liftweb.mapper.MetaMegaProtoUser$$anonfun$innerSignup$1$1.apply(ProtoUser.scala:240)
net.liftweb.http.S$SFuncHolder$$anonfun$apply$42.apply(S.scala:719)
net.liftweb.http.S$SFuncHolder$$anonfun$apply$42.apply(S.scala:719)
scala.List.map(List.scala:724)
net.liftweb.http.S$SFuncHolder.apply(S.scala:719)
net.liftweb.http.LiftSession$$anonfun$buildFunc$1$2.apply(LiftSession.scala:101)
net.liftweb.http.LiftSession$$anonfun$7$$anonfun$apply$8.apply(LiftSession.scala:111)
net.liftweb.http.LiftSession$$anonfun$7$$anonfun$apply$8.apply(LiftSession.scala:111)
scala.List.map(List.scala:724)
net.liftweb.http.LiftSession$$anonfun$7.apply(LiftSession.scala:111)
net.liftweb.http.LiftSession$$anonfun$7.apply(LiftSession.scala:104)
scala.List.flatMap(List.scala:1036)
net.liftweb.http.LiftSession.runParams(LiftSession.scala:104)
net.liftweb.http.LiftSession$$anonfun$processRequest$1.apply(LiftSession.scala:170)
net.liftweb.http.LiftSession$$anonfun$processRequest$1.apply(LiftSession.scala:156)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$wrapQuery$1.apply(S.scala:272)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$.net$liftweb$http$S$$wrapQuery(S.scala:269)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19$$anonfun$apply$20$$anonfun$apply$21$$anonfun$apply$22$$anonfun$apply$23$$anonfun$apply$24$$anonfun$apply$25$$anonfun$apply$26.apply(S.scala:292)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19$$anonfun$apply$20$$anonfun$apply$21$$anonfun$apply$22$$anonfun$apply$23$$anonfun$apply$24$$anonfun$apply$25.apply(S.scala:291)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19$$anonfun$apply$20$$anonfun$apply$21$$anonfun$apply$22$$anonfun$apply$23$$anonfun$apply$24.apply(S.scala:290)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$.net$liftweb$http$S$$initNotice(S.scala:211)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19$$anonfun$apply$20$$anonfun$apply$21$$anonfun$apply$22$$anonfun$apply$23.apply(S.scala:289)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19$$anonfun$apply$20$$anonfun$apply$21$$anonfun$apply$22.apply(S.scala:288)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19$$anonfun$apply$20$$anonfun$apply$21.apply(S.scala:287)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19$$anonfun$apply$20.apply(S.scala:286)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18$$anonfun$apply$19.apply(S.scala:285)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17$$anonfun$apply$18.apply(S.scala:284)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1$$anonfun$apply$17.apply(S.scala:283)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$_init$1.apply(S.scala:282)
net.liftweb.http.S$.net$liftweb$http$S$$doAround(S.scala:252)
net.liftweb.http.S$$anonfun$net$liftweb$http$S$$doAround$1.apply(S.scala:253)
net.liftweb.util.ThreadLazy.apply(Lazy.scala:83)
net.liftweb.http.S$.net$liftweb$http$S$$doAround(S.scala:253)
net.liftweb.http.S$.net$liftweb$http$S$$_init(S.scala:281)
net.liftweb.http.S$$anonfun$init$2$$anonfun$apply$29.apply(S.scala:338)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$$anonfun$init$2.apply(S.scala:337)
net.liftweb.util.ThreadGlobal.doWith(ThreadGlobal.scala:24)
net.liftweb.http.S$.init(S.scala:336)
net.liftweb.http.LiftSession.processRequest(LiftSession.scala:155)
net.liftweb.http.LiftServlet.doService(LiftServlet.scala:220)
net.liftweb.http.LiftServlet$$anonfun$doIt$1$1.apply(LiftServlet.scala:108)
net.liftweb.http.LiftServlet$$anonfun$doIt$1$1.apply(LiftServlet.scala:106)
net.liftweb.util.Helpers$.calcTime(Helpers.scala:845)
net.liftweb.util.Helpers$.logTime(Helpers.scala:831)
net.liftweb.http.LiftServlet.doIt$1(LiftServlet.scala:106)
net.liftweb.http.LiftServlet.service(LiftServlet.scala:112)
net.liftweb.http.LiftFilter.lift(LiftServlet.scala:749)
net.liftweb.http.LiftFilter.doFilter(LiftServlet.scala:709)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:202)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:869)
org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:664)
org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:527)
org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:80)
org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)


So I have like 2 ideas:

1) Perhaps have a mechanism that ensures that all dependencies are present upon startup of the webapp
2) An automatic routine that stores debug-information about errors that occur runtime (which user, at what time, with what parameters, giving what error)

Any input on this?

Cheers,
-V

java.lang.Thread.run(Unknown Source)


--
_____________________________________
/                                                                 \
       /lift/ committer (www.liftweb.net)
     SGS member (Scala Group Sweden)
 SEJUG member (Swedish Java User Group)
\_____________________________________/

Steve Jenson

unread,
Feb 9, 2008, 2:07:17 PM2/9/08
to lif...@googlegroups.com
This happens with any exception that bubbles to the top.

Reading through the LiftServlet source, I see that David is using the
Development Run Mode to switch between showing the exception and
throwing it away. That's reasonable but it highlights that most of us
don't know the difference between Development and Production mode in
Lift. I guess this means I'm deploying in Development mode.

An error handling method I've used in the past that worked well was to
have a function that determined whether to spit out the error page or
to send it somewhere else based on the HttpRequest. For instance, if
the user was in a special debug mode or was from an internal address
then spit out the exception, otherwise show a clean error page and do
something else with the actual error like email it, send it to a
message queue, etc.

If nobody gets around to cleaning this up, I'll improve it in a few
weeks to work the way I describe.

Steve

Viktor Klang

unread,
Feb 9, 2008, 2:11:20 PM2/9/08
to lif...@googlegroups.com
On Feb 9, 2008 8:07 PM, Steve Jenson <ste...@gmail.com> wrote:

This happens with any exception that bubbles to the top.

Reading through the LiftServlet source, I see that David is using the
Development Run Mode to switch between showing the exception and
throwing it away. That's reasonable  but it highlights that most of us
don't know the difference between Development and Production mode in
Lift. I guess this means I'm deploying in Development mode.

An error handling method I've used in the past that worked well was to
have a function that determined whether to spit out the error page or
to send it somewhere else based on the HttpRequest. For instance, if
the user was in a special debug mode or was from an internal address
then spit out the exception, otherwise show a clean error page and do
something else with the actual error like email it, send it to a
message queue, etc.

If nobody gets around to cleaning this up, I'll improve it in a few
weeks to work the way I describe.

I suggest having a discussion around the subject and see what a consensus-situation would look like.

What's your take on the dependency checking?

-V
 

Steve Jenson

unread,
Feb 9, 2008, 2:22:38 PM2/9/08
to lif...@googlegroups.com
On Feb 9, 2008 11:11 AM, Viktor Klang <viktor...@gmail.com> wrote:
> I suggest having a discussion around the subject and see what a
> consensus-situation would look like.

Fire away. What do you think?

> What's your take on the dependency checking?

For my own app, I'm moving towards a one-jar approach to avoid this
issue and others. I would build a single jar, run my staging tests
using it, and once I'm satisfied, I would push that to production
servers.

Steve

Viktor Klang

unread,
Feb 9, 2008, 2:31:00 PM2/9/08
to lif...@googlegroups.com
On Feb 9, 2008 8:22 PM, Steve Jenson <ste...@gmail.com> wrote:

On Feb 9, 2008 11:11 AM, Viktor Klang <viktor...@gmail.com> wrote:
> I suggest having a discussion around the subject and see what a
> consensus-situation would look like.

Fire away. What do you think?

I'm opting for a pluggable strategy pattern so /lift/ users can use whatever notification method they see fit. We supply the plumbing and data, and then they can provide the plugins.

Something along the lines of:

ErrorReporter
|
|----------- EmailErrorReporter
|----------- XMPPErrorReporter
|----------- DBErrorReporter
|----------- FileErrorReporter
|----------- etc...
 


> What's your take on the dependency checking?

For my own app, I'm moving towards a one-jar approach to avoid this
issue and others. I would build a single jar, run my staging tests
using it, and once I'm satisfied, I would push that to production
servers.

Agreed, but not having to write tests for it would be even better.

-V

 

Steve

Steve Jenson

unread,
Feb 9, 2008, 3:09:43 PM2/9/08
to lif...@googlegroups.com
On Feb 9, 2008 11:31 AM, Viktor Klang <viktor...@gmail.com> wrote:
> On Feb 9, 2008 8:22 PM, Steve Jenson <ste...@gmail.com> wrote:
> >
> >
> > On Feb 9, 2008 11:11 AM, Viktor Klang <viktor...@gmail.com> wrote:
> > > I suggest having a discussion around the subject and see what a
> > > consensus-situation would look like.
> >
> > Fire away. What do you think?
>
> I'm opting for a pluggable strategy pattern so /lift/ users can use whatever
> notification method they see fit. We supply the plumbing and data, and then
> they can provide the plugins.
>
> Something along the lines of:
>
> ErrorReporter
> |
> |----------- EmailErrorReporter
> |----------- XMPPErrorReporter
> |----------- DBErrorReporter
> |----------- FileErrorReporter
> |----------- etc...

Yeah, I like it. It's easy for people to write their own subclass of
ErrorReporter. A method in LiftServlet can be used to supply the
ErrorReporter.

> > > What's your take on the dependency checking?
> >
> > For my own app, I'm moving towards a one-jar approach to avoid this
> > issue and others. I would build a single jar, run my staging tests
> > using it, and once I'm satisfied, I would push that to production
> > servers.
> >
>
> Agreed, but not having to write tests for it would be even better.

Staging is independent of tests, it's where you look at it with eyeballs,
either your own or your QA teams.

Steve

Viktor Klang

unread,
Feb 9, 2008, 3:50:07 PM2/9/08
to lif...@googlegroups.com
On Feb 9, 2008 9:09 PM, Steve Jenson <ste...@gmail.com> wrote:

On Feb 9, 2008 11:31 AM, Viktor Klang <viktor...@gmail.com> wrote:
> On Feb 9, 2008 8:22 PM, Steve Jenson <ste...@gmail.com> wrote:
> >
> >
> > On Feb 9, 2008 11:11 AM, Viktor Klang <viktor...@gmail.com> wrote:
> > > I suggest having a discussion around the subject and see what a
> > > consensus-situation would look like.
> >
> > Fire away. What do you think?
>
> I'm opting for a pluggable strategy pattern so /lift/ users can use whatever
> notification method they see fit. We supply the plumbing and data, and then
> they can provide the plugins.
>
> Something along the lines of:
>
> ErrorReporter
> |
> |----------- EmailErrorReporter
> |----------- XMPPErrorReporter
> |----------- DBErrorReporter
> |----------- FileErrorReporter
> |----------- etc...

Yeah, I like it. It's easy for people to write their own subclass of
ErrorReporter. A method in LiftServlet can be used to supply the
ErrorReporter.

Yeah, it's alot of bang for the buck.
 


> > > What's your take on the dependency checking?
> >
> > For my own app, I'm moving towards a one-jar approach to avoid this
> > issue and others. I would build a single jar, run my staging tests
> > using it, and once I'm satisfied, I would push that to production
> > servers.
> >
>
> Agreed, but not having to write tests for it would be even better.

Staging is independent of tests, it's where you look at it with eyeballs,
either your own or your QA teams.

Haha, sorry, translation error on my side!
Usually eyeballing is good, but when you've worked doubletime for a while... ;)

-V
 


Steve

Marius

unread,
Feb 10, 2008, 3:14:52 AM2/10/08
to liftweb
Well IMHO lift could/should provide and error page (or perhaps
multiple pages mapped to throwable classes) completly customizable in
the sense that user can define the error page (say in boot). Therefore
whenever there is a situation like the above one, lift will catch the
exception and redirect to the user defined page. Also it should
provide access to the throwable perhaps via an S class function so
from my error page I can define a snippet and simply look at the
Throwable.

The way that I see it this is about more than dependencies management
but rather handling runtime throwables (errors/exceptions).

David Pollak

unread,
Feb 10, 2008, 12:46:45 PM2/10/08
to lif...@googlegroups.com
Folks,

In LiftServlet:

  /**
  * The partial function (pattern matching) for handling converting an exception to something to
  * be sent to the browser depending on the current RunMode (development, etc.)
  *
  * The best thing to do is browserResponseToException = { case (...) => } orElse browserResponseToException
  * so that your response over-rides the default, but the processing falls through to the default.
  */
  var browserResponseToException: PartialFunction[(Props.RunModes.Value, RequestState, Throwable), ResponseIt] = {

So... this is a Partial Function.  It defines when is done with Exceptions happen in each runmode.

Anything you want to do can be done here.

You can return different error pages depending on the runmode and the exception type (e.g., an SQL exception might return a certain kind of page, where a MatchError might return something else).  You can send the exception information to other sources.

So... what that you guys want to do cannot be achieved with this structure?

What kind of helpers (partial functions that can be composed by application developers) do any of you want to write?

Thanks,

David

Viktor Klang

unread,
Feb 10, 2008, 1:07:10 PM2/10/08
to lif...@googlegroups.com
On Feb 10, 2008 6:46 PM, David Pollak <feeder.of...@gmail.com> wrote:
Folks,

In LiftServlet:

  /**
  * The partial function (pattern matching) for handling converting an exception to something to
  * be sent to the browser depending on the current RunMode (development, etc.)
  *
  * The best thing to do is browserResponseToException = { case (...) => } orElse browserResponseToException
  * so that your response over-rides the default, but the processing falls through to the default.
  */
  var browserResponseToException: PartialFunction[(Props.RunModes.Value, RequestState, Throwable), ResponseIt] = {

So... this is a Partial Function.  It defines when is done with Exceptions happen in each runmode.

Anything you want to do can be done here.

You can return different error pages depending on the runmode and the exception type (e.g., an SQL exception might return a certain kind of page, where a MatchError might return something else).  You can send the exception information to other sources.

So... what that you guys want to do cannot be achieved with this structure?

What kind of helpers (partial functions that can be composed by application developers) do any of you want to write?

Hmmm, perhaps it's time to create something like a LiftConfig, because it might be difficult for newbies to find where and how they configure /lift/.
A personal opinion is also that it's easier to share classes than partial functions, if I write an XMPPErrorReporter I can easily make it available as an XMPPErrorReport.scala file instead of having the adopter of it cut'n'paste a PF

I'm not saying I have all the clowns in the circus, but i welcome a discussion about how to achieve clarity, simplicity and security on the topic.

Best regards,
-V
 

Steve Jenson

unread,
Feb 10, 2008, 1:12:53 PM2/10/08
to lif...@googlegroups.com
You even documented that you could use it that way and I still didn't
notice. I will write some customized helpers and document them here in
a few weeks when I need them. It's on my work to-do list.

Thanks!
Steve

Viktor Klang

unread,
Feb 10, 2008, 1:28:44 PM2/10/08
to lif...@googlegroups.com
On Feb 10, 2008 7:12 PM, Steve Jenson <ste...@gmail.com> wrote:

You even documented that you could use it that way and I still didn't
notice. I will write some customized helpers and document them here in
a few weeks when I need them. It's on my work to-do list.

Yeah, and imagine what it's like for a newcomer...

What do you think about a LiftConfig? (Something that exposes the configuration of /lift/ in an easy-acces & sensible way)

-V

 

David Pollak

unread,
Feb 10, 2008, 6:08:14 PM2/10/08
to lif...@googlegroups.com
On 2/10/08, Viktor Klang <viktor...@gmail.com> wrote:


On Feb 10, 2008 7:12 PM, Steve Jenson <ste...@gmail.com> wrote:

You even documented that you could use it that way and I still didn't
notice. I will write some customized helpers and document them here in
a few weeks when I need them. It's on my work to-do list.

Yeah, and imagine what it's like for a newcomer...

Part of the issue is that lift does things "differently" than most other frameworks.  lift makes heavy use of partial functions for customization.  That's not going to change.  Using classes or stuff that implements interfaces a la Java becomes goat rodeo that leads right down the XML path to hell.  As Rails has demonstrated, configurations as in-language DSLs is the best way to go and Partial Functions are an excellent declarative way of structuring a DSL.

I've localized the vast majority of these customization points in LiftServlet.  And, yes, the other customizations that one does in Boot should probably happen in LiftServlet or some other conveniently named object rather than the sprawl that's starting to happen.

And also, this stuff needs to get documented.  And yes, there needs to be a "cook book" for common operations.  Perhaps we can construct the cook book by folks on the list asking questions and I'll answer the questions and someone with a good organizational sense can organize the answers on the wiki.

What do you think about a LiftConfig? (Something that exposes the configuration of /lift/ in an easy-acces & sensible way)


That's what the LiftServlet object is.

Thanks,

David
 



--
lift, the secure, simple, powerful web framework http://liftweb.net
Collaborative Task Management http://much4.us

Viktor Klang

unread,
Feb 10, 2008, 6:19:49 PM2/10/08
to lif...@googlegroups.com
On Feb 11, 2008 12:08 AM, David Pollak <feeder.of...@gmail.com> wrote:


On 2/10/08, Viktor Klang <viktor...@gmail.com> wrote:


On Feb 10, 2008 7:12 PM, Steve Jenson <ste...@gmail.com> wrote:

You even documented that you could use it that way and I still didn't
notice. I will write some customized helpers and document them here in
a few weeks when I need them. It's on my work to-do list.

Yeah, and imagine what it's like for a newcomer...

Part of the issue is that lift does things "differently" than most other frameworks.  lift makes heavy use of partial functions for customization.  That's not going to change.  Using classes or stuff that implements interfaces a la Java becomes goat rodeo that leads right down the XML path to hell. 

 
As Rails has demonstrated, configurations as in-language DSLs is the best way to go and Partial Functions are an excellent declarative way of structuring a DSL.

Agreed, given that the injection point is intuitive and the amount of code to inject into the PF has few LoC.
Overusage of classes suck major donkey equipment, but as containers of code to distribute they are clearly more suited than the old C-send-a-patch-way of doing it.

(imagine you've written a good XMPPErrorReporter, and you want to send it to DaveB so he can utilize it.
 


I've localized the vast majority of these customization points in LiftServlet.  And, yes, the other customizations that one does in Boot should probably happen in LiftServlet or some other conveniently named object rather than the sprawl that's starting to happen.

Also agreed, configuration should be intuitive and be kept in a single place (or a few places, but they are easily located and are intiutively named).
 


And also, this stuff needs to get documented.  And yes, there needs to be a "cook book" for common operations.  Perhaps we can construct the cook book by folks on the list asking questions and I'll answer the questions and someone with a good organizational sense can organize the answers on the wiki.

What do you think about a LiftConfig? (Something that exposes the configuration of /lift/ in an easy-acces & sensible way)


That's what the LiftServlet object is.

Then I suggest we rename it. No Java WedDev is ever going to look to do configuration in something names <X>Servlet. (And other devs might not even know what a Servlet actually is supposed to be)

Best regards,
-V

 

David Pollak

unread,
Feb 10, 2008, 6:35:58 PM2/10/08
to lif...@googlegroups.com
On 2/10/08, Viktor Klang <viktor...@gmail.com> wrote:


On Feb 11, 2008 12:08 AM, David Pollak <feeder.of...@gmail.com> wrote:


On 2/10/08, Viktor Klang <viktor...@gmail.com> wrote:


On Feb 10, 2008 7:12 PM, Steve Jenson <ste...@gmail.com> wrote:

You even documented that you could use it that way and I still didn't
notice. I will write some customized helpers and document them here in
a few weeks when I need them. It's on my work to-do list.

Yeah, and imagine what it's like for a newcomer...

Part of the issue is that lift does things "differently" than most other frameworks.  lift makes heavy use of partial functions for customization.  That's not going to change.  Using classes or stuff that implements interfaces a la Java becomes goat rodeo that leads right down the XML path to hell. 

 
As Rails has demonstrated, configurations as in-language DSLs is the best way to go and Partial Functions are an excellent declarative way of structuring a DSL.

Agreed, given that the injection point is intuitive and the amount of code to inject into the PF has few LoC.
Overusage of classes suck major donkey equipment, but as containers of code to distribute they are clearly more suited than the old C-send-a-patch-way of doing it.

(imagine you've written a good XMPPErrorReporter, and you want to send it to DaveB so he can utilize it.

Partial Functions and classes are not mutually incompatible.  Quite the opposite:

object XmppErrorReporter {
  def reportAndContinue(originalPf: ErrorReportingPF): ErrorReportingPF = {
     case v @ (_, e, _) => reportWithXmpp(e)
        originalPF(v)
  }

  def reportWithXmpp(e: Throwable) {
     // send the report out via XMPP
  }
}

Boot {
  LiftServlet.browserResponseToException = XmppErrorReporter.reportAndContinue(
LiftServlet.browserResponseToException)
}

If someone wants to distribute code, they can do so as objects or methods/vals in classes or traits.  The ProtoUser stuff makes heavy use of this technique.

Yeah, the paradigm is different, but it allows for a lot of flexibility.

I've localized the vast majority of these customization points in LiftServlet.  And, yes, the other customizations that one does in Boot should probably happen in LiftServlet or some other conveniently named object rather than the sprawl that's starting to happen.

Also agreed, configuration should be intuitive and be kept in a single place (or a few places, but they are easily located and are intiutively named).
 


And also, this stuff needs to get documented.  And yes, there needs to be a "cook book" for common operations.  Perhaps we can construct the cook book by folks on the list asking questions and I'll answer the questions and someone with a good organizational sense can organize the answers on the wiki.

What do you think about a LiftConfig? (Something that exposes the configuration of /lift/ in an easy-acces & sensible way)


That's what the LiftServlet object is.

Then I suggest we rename it. No Java WedDev is ever going to look to do configuration in something names <X>Servlet. (And other devs might not even know what a Servlet actually is supposed to be)

I'll think about it.

It means breaking every bit of lift code in existence and that's a big break.  I'm not sure of the benefits because all of the configuration that's done in the example code is done on LiftServlet.

If others who have existing lift code that will require some attention to fix care to weigh in, that'd be helpful.

Thanks,

David

David Bernard

unread,
Feb 10, 2008, 6:42:29 PM2/10/08
to lif...@googlegroups.com
actual :
* every "configuration" should drive by code (we all agree)
* Boot class is the location of the configuration/customisation of the webapp

My suggestion :
* Extensions (widgets lib, error reporter ,...) should have an activate method (like Activator in Eclipse plugin).
* activate method should be call explicitly from Boot (no auto-detection, scan of jar,...)
* activate method are like Boot but at the extension level (where Boot is at application level)

An interesting things could be to define a convention for Extensions'activate (name and location)

Like this extension could be normal jar (with one or several "activators"), and to use an extension add the jar and activate it.

/davidB

Viktor Klang wrote:
>
>
> On Feb 11, 2008 12:08 AM, David Pollak <feeder.of...@gmail.com

> <mailto:feeder.of...@gmail.com>> wrote:
>
>
>
> On 2/10/08, *Viktor Klang* <viktor...@gmail.com


> <mailto:viktor...@gmail.com>> wrote:
>
>
>
> On Feb 10, 2008 7:12 PM, Steve Jenson <ste...@gmail.com

> <mailto:marius...@gmail.com>> wrote:
> > >
> > >
> > >
> > > On Feb 9, 7:25 pm, "Viktor Klang"

> <http://www.liftweb.net>)


> > > > SGS member (Scala Group Sweden)
> > > > SEJUG member (Swedish Java User Group)
> > > > \_____________________________________/
> > >
> > > > >
> > >
> >
>
> /lift/ committer (www.liftweb.net

> <http://www.liftweb.net>)


> SGS member (Scala Group Sweden)
> SEJUG member (Swedish Java User Group)
> \_____________________________________/
>
>
>
>
> --
> lift, the secure, simple, powerful web framework http://liftweb.net
> Collaborative Task Management http://much4.us
>
>
>
>
>
> --
> _____________________________________
> / \

> /lift/ committer (www.liftweb.net <http://www.liftweb.net>)

Viktor Klang

unread,
Feb 10, 2008, 6:43:01 PM2/10/08
to lif...@googlegroups.com

I stand corrected. That was sexier than my late-at-sunday-evening-braincells could produce.
 


I've localized the vast majority of these customization points in LiftServlet.  And, yes, the other customizations that one does in Boot should probably happen in LiftServlet or some other conveniently named object rather than the sprawl that's starting to happen.

Also agreed, configuration should be intuitive and be kept in a single place (or a few places, but they are easily located and are intiutively named).
 


And also, this stuff needs to get documented.  And yes, there needs to be a "cook book" for common operations.  Perhaps we can construct the cook book by folks on the list asking questions and I'll answer the questions and someone with a good organizational sense can organize the answers on the wiki.

What do you think about a LiftConfig? (Something that exposes the configuration of /lift/ in an easy-acces & sensible way)


That's what the LiftServlet object is.

Then I suggest we rename it. No Java WedDev is ever going to look to do configuration in something names <X>Servlet. (And other devs might not even know what a Servlet actually is supposed to be)

I'll think about it.

It means breaking every bit of lift code in existence and that's a big break.  I'm not sure of the benefits because all of the configuration that's done in the example code is done on LiftServlet.

As long as we provide a step-by-step upgrade manual, it should be fairly okay if we distribute it wisely.
Cheers,
-V
 

Viktor Klang

unread,
Feb 10, 2008, 6:46:24 PM2/10/08
to lif...@googlegroups.com
On Feb 11, 2008 12:42 AM, David Bernard <david.be...@gmail.com> wrote:

actual :
* every "configuration" should drive by code (we all agree)
* Boot class is the location of the configuration/customisation of the webapp

Don't forget Bootable
It'll allow for extensions 


My suggestion :
* Extensions (widgets lib, error reporter ,...) should have an activate method (like Activator in Eclipse plugin).
* activate method should be call explicitly from Boot (no auto-detection, scan of jar,...)
* activate method are like Boot but at the extension level (where Boot is at application level)

Are you referring to an OSGi approach?
 


An interesting things could be to define a convention for Extensions'activate (name and location)

Like this extension could be normal jar (with one or several "activators"), and to use an extension add the jar and activate it.

hmm, do you have some kind of example to show what you mean?

-V
 



--
_____________________________________
/                                                                 \
       /lift/ committer (www.liftweb.net)

Marius

unread,
Feb 11, 2008, 2:47:25 AM2/11/08
to liftweb


On Feb 10, 7:46 pm, "David Pollak" <feeder.of.the.be...@gmail.com>
wrote:
I personally didn't know about this approach but I find it quite
usefull... This should suffice and I find it quite elegant but I need
to play with it more.
A quick question ... is there an out of the box mechanism to return an
xhtml file content as ResponseIt ?

>
> Thanks,
>
> David

David Bernard

unread,
Feb 11, 2008, 4:00:01 AM2/11/08
to lif...@googlegroups.com
Viktor Klang wrote:
>
>
> On Feb 11, 2008 12:42 AM, David Bernard <david.be...@gmail.com
> <mailto:david.be...@gmail.com>> wrote:
>
>
> actual :
> * every "configuration" should drive by code (we all agree)
> * Boot class is the location of the configuration/customisation of
> the webapp
>
>
> Don't forget *Bootable*

> It'll allow for extensions
>
>
>
> My suggestion :
> * Extensions (widgets lib, error reporter ,...) should have an
> activate method (like Activator in Eclipse plugin).
> * activate method should be call explicitly from Boot (no
> auto-detection, scan of jar,...)
> * activate method are like Boot but at the extension level (where
> Boot is at application level)
>
>
> Are you referring to an OSGi approach?

IMHO OSGi is heavy (and overdesigned), but there is good ideas.

>
>
>
> An interesting things could be to define a convention for
> Extensions'activate (name and location)
>
> Like this extension could be normal jar (with one or several
> "activators"), and to use an extension add the jar and activate it.
>
>
> hmm, do you have some kind of example to show what you mean?

If I pick the ErrorReporter example provide by DavidP :

In the lib :
package xmpper;

object XmppErrorReporter {
def reportAndContinue(originalPf: ErrorReportingPF): ErrorReportingPF = {
case v @ (_, e, _) => reportWithXmpp(e)
originalPF(v)
}

def reportWithXmpp(e: Throwable) {
// send the report out via XMPP
}
}

object Activator {
def activate_XmppErrorReporter() = {
LiftServlet.browserResponseToException = XmppErrorReporter.reportAndContinue(LiftServlet.browserResponseToException)
}

def dispose_XmppErrorReporter() = {
}
}

In the app that wish to use XmppErrorReporter, instead of read a doc, copy/paste a code (that could change in futur version)

class Boot {
def boot {
//...
xmpper.Activator.activate_XmppErrorReporter()
mywidgets.Activator.activate() // register resources toserve,...
//...
}
}

It is a very simple Lifecycle management.

Now, to be a usable solution, we need to have a common convention about Activator :
* names, location, type (class, object, trait),...
* do we call activate method/function directly or register them (or instance of Activator)
* how to avoid twice activation
...

Is it clearer ? (it's a draft reply to how to package/distribute component (error reporter, widgets,...)

/davidB

>
> -V
>
>
>
>
> /davidB
>
> Viktor Klang wrote:
> >
> >
> > On Feb 11, 2008 12:08 AM, David Pollak
> <feeder.of...@gmail.com <mailto:feeder.of...@gmail.com>

> > <mailto:feeder.of...@gmail.com


> <mailto:feeder.of...@gmail.com>>> wrote:
> >
> >
> >
> > On 2/10/08, *Viktor Klang* <viktor...@gmail.com
> <mailto:viktor...@gmail.com>

> > <mailto:viktor...@gmail.com


> <mailto:viktor...@gmail.com>>> wrote:
> >
> >
> >
> > On Feb 10, 2008 7:12 PM, Steve Jenson <ste...@gmail.com
> <mailto:ste...@gmail.com>

> > <mailto:feeder.of...@gmail.com

> > <mailto:marius...@gmail.com


> <mailto:marius...@gmail.com>>> wrote:
> > > >
> > > >
> > > >
> > > > On Feb 9, 7:25 pm, "Viktor Klang"
> > <viktor.kl...@gmail.com

> <mailto:viktor.kl...@gmail.com> <mailto:viktor.kl...@gmail.com

Marius

unread,
Feb 11, 2008, 4:05:56 AM2/11/08
to liftweb
The recommendable way to use an error xhtml page is to use
LiftSessio#indAnyTemplate ?


Br's,
Marius

Viktor Klang

unread,
Feb 11, 2008, 4:33:18 AM2/11/08
to lif...@googlegroups.com
On Feb 11, 2008 10:00 AM, David Bernard <david.be...@gmail.com> wrote:

Viktor Klang wrote:
>
>
> On Feb 11, 2008 12:42 AM, David Bernard <david.be...@gmail.com
> <mailto:david.be...@gmail.com>> wrote:
>
>
>     actual :
>     * every "configuration" should drive by code (we all agree)
>     * Boot class is the location of the configuration/customisation of
>     the webapp
>
>
> Don't forget *Bootable*
> It'll allow for extensions
>
>
>
>     My suggestion :
>     * Extensions (widgets lib, error reporter ,...) should have an
>     activate method (like Activator in Eclipse plugin).
>     * activate method should be call explicitly from Boot (no
>     auto-detection, scan of jar,...)
>     * activate method are like Boot but at the extension level (where
>     Boot is at application level)
>
>
> Are you referring to an OSGi approach?

IMHO OSGi is heavy (and overdesigned), but there is good ideas.

Agreed. Are there any lightweight component frameworks that could be easily adoptable for Scala & /lift/?
 

Could it be injected?

-V

 



--
_____________________________________
/                                                                 \
       /lift/ committer (www.liftweb.net)

Marius

unread,
Feb 11, 2008, 5:05:58 AM2/11/08
to liftweb


On Feb 11, 11:05 am, Marius <marius.dan...@gmail.com> wrote:
> The recommendable way to use an error xhtml page is to use
> LiftSessio#findAnyTemplate ?
>

Hmm .. I just tried this ... and findAnyTemplate works partially in
the sense theat error page snippets and lift/tags are not
processed ... etc.?

What I did is something like :

LiftServlet.browserResponseToException = {
case (mode, state, ex) => {
val node = (S.session match {
case Full(s) => s.findAnyTemplate("/error.html" :: Nil)
case _ => Empty
}) match {
case Full(n) => n theSeq(0)
case _ => <br/>
}

XhtmlResponse(node, ResponseInfo.docType(state), List("Content-
Type" -> "text/html"), 500)
}
}

I could bet there's a simpler way of using error pages ... David, any
thoughts?

P.S.
I'm throwing a NPE from a snippet to get into this. BTW I really like
this browserResponseToException approach.

> Br's,
> Marius

Marius

unread,
Feb 11, 2008, 5:37:34 AM2/11/08
to liftweb


On Feb 11, 12:05 pm, Marius <marius.dan...@gmail.com> wrote:
> On Feb 11, 11:05 am, Marius <marius.dan...@gmail.com> wrote:
>
> > The recommendable way to use an error xhtml page is to use
> > LiftSessio#findAnyTemplate ?
>
> Hmm .. I just tried this ... and findAnyTemplate works partially in
> the sense theat error page snippets and lift/tags are not
> processed ... etc.?
>
> What I did is something like :
>
> LiftServlet.browserResponseToException = {
> case (mode, state, ex) => {
> val node = (S.session match {
> case Full(s) => s.findAnyTemplate("/error.html" :: Nil)
> case _ => Empty
> }) match {
> case Full(n) => n theSeq(0)
> case _ => <br/>
> }
>
> XhtmlResponse(node, ResponseInfo.docType(state), List("Content-
> Type" -> "text/html"), 500)
> }
> }

sorry about the naive code above:

LiftServlet.browserResponseToException = {
case (mode, state, ex) => {
val node = S.session match {
case Full(s) => (s.findAnyTemplate("/error.html" :: Nil)
openOr Text("Error page not found")) theSeq(0)
case _ => <br/>
}

XhtmlResponse(node, ResponseInfo.docType(state), List("Content-
Type" -> "text/html"), 500)
}
}

this should be somehow better ... still the question remains. How to
use a normal error page that most likely will contain lift:surround,
snippets or any other lift artifact. (and sorry for spamming you guys
with this but it's pretty important for me)

TylerWeir

unread,
Feb 11, 2008, 9:49:51 AM2/11/08
to liftweb
I added a CookBook[1] page to the wiki. Hopefully we can fill it up
with useful snippets.

[1] http://liftweb.net/index.php/CookBook

Marius

unread,
Feb 11, 2008, 11:01:47 AM2/11/08
to liftweb
I just tried the following approach and worked correctly :

LiftServlet.browserResponseToException = {
case (mode, state, ex) => {
val node = S.session match {
case Full(s) => (s.processSurroundAndInclude("/error.html",
s.findAnyTemplate("/error.html" :: Nil) openOr Text("Error page not
found"))) theSeq(0)
case _ => <br/>
}

XhtmlResponse(node, ResponseInfo.docType(state), List("Content-
Type" -> "text/html"), 500)
}
}

lift:surround and snippets seems to be processed correctly. But it
seems too much word to return and error page with
browserResponseToException. Does anyone have a simpler way ?

Br's,
Marius

Marius

unread,
Feb 11, 2008, 11:09:31 AM2/11/08
to liftweb
forgot about fixHtml so the above becomes:

LiftServlet.browserResponseToException = {
case (mode, state, ex) => {
val node = S.session match {
case Full(s) => (s.fixHtml(s.processSurroundAndInclude("/
error.html", s.findAnyTemplate("/error.html" :: Nil) openOr
Text("Error page not found")))) theSeq(0)
case _ => <br/>
}

XhtmlResponse(node, ResponseInfo.docType(state), List("Content-
Type" -> "text/html"), 500)
}
}

unfortunately not very lightweight :( ... any feedback would be
appreciated !

Br's,
Marius

David Pollak

unread,
Feb 11, 2008, 12:47:02 PM2/11/08
to lif...@googlegroups.com

Marius wrote:
> forgot about fixHtml so the above becomes:
>
> LiftServlet.browserResponseToException = {
> case (mode, state, ex) => {
> val node = S.session match {
> case Full(s) => (s.fixHtml(s.processSurroundAndInclude("/
> error.html", s.findAnyTemplate("/error.html" :: Nil) openOr
> Text("Error page not found")))) theSeq(0)
> case _ => <br/>
> }
>
> XhtmlResponse(node, ResponseInfo.docType(state), List("Content-
> Type" -> "text/html"), 500)
> }
> }
>

In general, I would not make any S or session related calls in the error
handler. If an exception was thrown, that means that the system is in
an unstable state.

I'd load the error template at object instantiation time (rather than at
exception processing time) and change up any of the context information
(the LiftServlet.context object should be populated when Boot is called,
so you've got the context and can look up resources based on the context.)

If you need some lightweight routines to load resources and parse them
based just on the Context and also to do XHTML bind substitution, I can
separate those routines out for you.

Marius

unread,
Feb 11, 2008, 2:15:07 PM2/11/08
to liftweb


On Feb 11, 7:47 pm, David Pollak <d...@athena.com> wrote:
> Marius wrote:
> > forgot about fixHtml so the above becomes:
>
> > LiftServlet.browserResponseToException = {
> > case (mode, state, ex) => {
> > val node = S.session match {
> > case Full(s) => (s.fixHtml(s.processSurroundAndInclude("/
> > error.html", s.findAnyTemplate("/error.html" :: Nil) openOr
> > Text("Error page not found")))) theSeq(0)
> > case _ => <br/>
> > }
>
> > XhtmlResponse(node, ResponseInfo.docType(state), List("Content-
> > Type" -> "text/html"), 500)
> > }
> > }
>
> In general, I would not make any S or session related calls in the error
> handler. If an exception was thrown, that means that the system is in
> an unstable state.

Well yes and no. Not all exceptions implies system instability as some
of them may reflect "synthetic" failure conditions.

>
> I'd load the error template at object instantiation time (rather than at
> exception processing time) and change up any of the context information
> (the LiftServlet.context object should be populated when Boot is called,
> so you've got the context and can look up resources based on the context.)

Very valid point ! ... Although the code above is just for
exemplification

>
> If you need some lightweight routines to load resources and parse them
> based just on the Context and also to do XHTML bind substitution, I can
> separate those routines out for you.

That would be nice David. I also was thinking at something like:

1. Have XhtmlResponse to accept a template name instead of a Node and
determine the node internally.
2.Have some other class XhtmlResponseTemplate that does that.

In this way the above code would become:

LiftServlet.browserResponseToException = {
case (mode, state, ex) => {
XhtmlResponseTemplate("/error.html",
ResponseInfo.docType(state), List("Content-Type" -> "text/html"),
500) // proposal #2 above. I think I'd prefer this over #1
// XhtmlResponseTemplate("/error.html",
ResponseInfo.docType(state), List("Content-Type" -> "text/html"),
500) // proposal #1 above

}
}

which is way nicer. Of course this raises the concerns that you posted
above in terms of using S and session in error handling but if you
would build some lightweight routines that won't be a concern at all.
If you like, I could take a look in this area and come up with a
proposed implementation unless of course you want to do it as this
might imply some core refactoring? Just please let me know.

Marius

unread,
Feb 11, 2008, 2:19:08 PM2/11/08
to liftweb
Oh almost forgot

XhtmlResponseTemplate could be simplified even more perhaps like
XhtmlResponseTemplate("/error.html") since the content type can be
assumed to be text/xhtml or text/html and the state may not be
necessary but optional. Same as HTTP error code.


Br's,
Marius

Marius

unread,
Feb 13, 2008, 5:01:25 AM2/13/08
to liftweb
David P. ... do you have any thoughts regarding these notes? Any news
regarding "If you need some lightweight routines to load resources and
parse them based just on the Context and also to do XHTML bind
substitution, I can separate those routines out for you. "

What's your take on this?

David Pollak

unread,
Feb 13, 2008, 1:07:16 PM2/13/08
to lif...@googlegroups.com
Marius,

I'm working on it. :-)

I've got a ton of stuff on my plate and can't always get APIs implemented over night.  Sorry.

David

Marius

unread,
Feb 13, 2008, 2:15:20 PM2/13/08
to liftweb
:) oh no worries ... and for sure I didn't want to be pushy :) ... it
was a kind reminder

Br's,
Marius

On Feb 13, 8:07 pm, "David Pollak" <feeder.of.the.be...@gmail.com>
wrote:
> Marius,
>
> I'm working on it. :-)
>
> I've got a ton of stuff on my plate and can't always get APIs implemented
> over night. Sorry.
>
> David
>
> On 2/13/08, Marius <marius.dan...@gmail.com> wrote:
>
>
>
>
>
> > David P. ... do you have any thoughts regarding these notes? Any news
> > regarding "If you need some lightweight routines to load resources and
> > parse them based just on the Context and also to do XHTML bind
> > substitution, I can separate those routines out for you. "
>
> > What's your take on this?
>
> > On Feb 11, 9:19 pm, Marius <marius.dan...@gmail.com> wrote:
> > > Oh almost forgot
>
> > > XhtmlResponseTemplate could be simplified even more perhaps like
> > > XhtmlResponseTemplate("/error.html") since the content type can be
> > > assumed to be text/xhtml or text/html and the state may not be
> > > necessary but optional. Same as HTTP error code.
>
> > > Br's,
> > > Marius
>
> --
Reply all
Reply to author
Forward
0 new messages