Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add exception handling for comet updates #1283

Merged
merged 4 commits into from

3 participants

@Shadowfiend
Owner

As per https://groups.google.com/forum/#!topic/liftweb/FFoyKp5H7hE .

The idea is to add LiftRules.cometUpdateExceptionHandler, a FactoryMaker[Box[JsCmd]]. If Full, the JsCmd defines the contents of a try/catch block wrapping a set of comet updates. If Empty, the try catch block is omitted entirely.

By default, it should be set to Full(JE.Call("liftComet.lift_cometError", JE.JsVar("e")).cmd) in development mode and Empty otherwise.

Shadowfiend added some commits
@Shadowfiend Shadowfiend Add cometUpdateExceptionHandler LiftRule.
Not implemented yet, but this rule specifies a Box[JsCmd]. If Full, the
contained JsCmd provides the code to put inside the catch of a try/catch
block that is wrapped around comet updates. If Empty, the old behavior
of not wrapping comet updates in try/catch at all is used.
cafb1de
@Shadowfiend Shadowfiend Properly use LiftRules.cometUpdateExceptionHandler.
CometActor now properly wraps its updates in a try/catch block when
cometUpdateExceptionHandler is Full.
777d72d
@Shadowfiend Shadowfiend Default cometUpdateExceptionHandler to Empty in production. 391bfa6
@Shadowfiend Shadowfiend Default implementation of liftComet.lift_cometError.
The default implementation tries to log the exception and then rethrows
it. The log is guarded for the existence of console.error and logged as
an error.
bfdffdd
@fmpwizard
Owner

Looks great and thanks for the detailed comment on the code.

@dpp dpp merged commit bfdffdd into master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jun 25, 2012
  1. @Shadowfiend

    Add cometUpdateExceptionHandler LiftRule.

    Shadowfiend authored
    Not implemented yet, but this rule specifies a Box[JsCmd]. If Full, the
    contained JsCmd provides the code to put inside the catch of a try/catch
    block that is wrapped around comet updates. If Empty, the old behavior
    of not wrapping comet updates in try/catch at all is used.
  2. @Shadowfiend

    Properly use LiftRules.cometUpdateExceptionHandler.

    Shadowfiend authored
    CometActor now properly wraps its updates in a try/catch block when
    cometUpdateExceptionHandler is Full.
  3. @Shadowfiend
  4. @Shadowfiend

    Default implementation of liftComet.lift_cometError.

    Shadowfiend authored
    The default implementation tries to log the exception and then rethrows
    it. The log is guarded for the existence of console.error and logged as
    an error.
This page is out of date. Refresh to see the latest.
View
20 web/webkit/src/main/scala/net/liftweb/http/CometActor.scala
@@ -1411,8 +1411,8 @@ private[http] class XmlOrJsCmd(val id: String,
* Returns the JsCmd that will be sent to client
*/
def toJavaScript(session: LiftSession, displayAll: Boolean): JsCmd = {
- var ret: JsCmd = JsCmds.JsTry(JsCmds.Run("destroy_" + id + "();"), false) &
- ((if (ignoreHtmlOnJs) Empty else xml, javaScript, displayAll) match {
+ val updateJs =
+ (if (ignoreHtmlOnJs) Empty else xml, javaScript, displayAll) match {
case (Full(xml), Full(js), false) => LiftRules.jsArtifacts.setHtml(id, Helpers.stripHead(xml)) & JsCmds.JsTry(js, false)
case (Full(xml), _, false) => LiftRules.jsArtifacts.setHtml(id, Helpers.stripHead(xml))
case (Full(xml), Full(js), true) => LiftRules.jsArtifacts.setHtml(id + "_outer", (
@@ -1421,7 +1421,21 @@ private[http] class XmlOrJsCmd(val id: String,
spanFunc(0, Helpers.stripHead(xml)) ++ fixedXhtml.openOr(Text(""))))
case (_, Full(js), _) => js
case _ => JsCmds.Noop
- }) & JsCmds.JsTry(JsCmds.Run("destroy_" + id + " = function() {" + (destroy.openOr(JsCmds.Noop).toJsCmd) + "};"), false)
+ }
+ val fullUpdateJs =
+ LiftRules.cometUpdateExceptionHandler.vend.foldLeft(updateJs) { (commands, catchHandler) =>
+ JsCmds.Run(
+ "try{" +
+ commands.toJsCmd +
+ "}catch(e){" +
+ catchHandler.toJsCmd +
+ "}"
+ )
+ }
+
+ var ret: JsCmd = JsCmds.JsTry(JsCmds.Run("destroy_" + id + "();"), false) &
+ fullUpdateJs &
+ JsCmds.JsTry(JsCmds.Run("destroy_" + id + " = function() {" + (destroy.openOr(JsCmds.Noop).toJsCmd) + "};"), false)
S.appendNotices(notices)
ret = S.noticesToJsCmd & ret
View
27 web/webkit/src/main/scala/net/liftweb/http/LiftRules.scala
@@ -1514,6 +1514,33 @@ class LiftRules() extends Factory with FormVendor with LazyLoggable {
@volatile var renderCometScript: LiftSession => JsCmd = session => ScriptRenderer.cometScript
/**
+ * If this is Full, comet updates (partialUpdates or reRenders) are
+ * wrapped in a try/catch statement. The provided JsCmd is the body of
+ * the catch statement. Within that JsCmd, the varibale "e" refers to the
+ * caught exception.
+ *
+ * In development mode, this defaults to Full and the command within
+ * invokes liftComet.lift_cometError with the exception;
+ * lift_cometError rethrows the exception by default. In production
+ * mode, this defaults to Empty.
+ *
+ * Note that if you set this to Full, it is highly advised that you
+ * rethrow the exception. If you fail to rethrow the exception, you
+ * run the risk of dropping an unpredictable number of updates (i.e.,
+ * if the third of 20 updates that are sent to the client in a single
+ * response throws an exception, none of the subsequent ones will run;
+ * failing to rethrow the exception means any updates that did not run
+ * will never be run).
+ */
+ val cometUpdateExceptionHandler: FactoryMaker[Box[JsCmd]] =
+ new FactoryMaker[Box[JsCmd]]( () => {
+ if (Props.devMode)
+ Full(JE.Call("liftComet.lift_cometError", JE.JsVar("e")).cmd)
+ else
+ Empty
+ } ) {}
+
+ /**
* Renders that JavaScript that holds Comet identification information
*/
@volatile var renderCometPageContents: (LiftSession, Seq[CometVersionPair]) => JsCmd =
View
6 web/webkit/src/main/scala/net/liftweb/http/js/ScriptRenderer.scala
@@ -251,6 +251,12 @@ object ScriptRenderer {
setTimeout("liftComet.lift_cometEntry();",""" + LiftRules.cometFailureRetryTimeout + """);
},
+ lift_cometError: function(e) {
+ if (console && typeof console.error == 'function')
+ console.error(e.stack || e);
+ throw e;
+ },
+
lift_cometEntry: function() {
var isEmpty = function(){for (var i in lift_toWatch) {return false} return true}();
if (!isEmpty) {
Something went wrong with that request. Please try again.