Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error handling #21

Closed
kosiakk opened this issue Sep 6, 2016 · 2 comments
Closed

Error handling #21

kosiakk opened this issue Sep 6, 2016 · 2 comments

Comments

@kosiakk
Copy link
Contributor

kosiakk commented Sep 6, 2016

In case of exception, current implementation simply aborts the processing.

I've introduced an onError handler in the TagConsumer, so that the library:

  1. Handles errors in a general manner
  2. Always properly closes all the tags
  3. If error handler does not re-trow the exception, the rendering continues

As the result, it's possible to use such TagConsumer:

class SafeHtmlOutput(val result: PrintWriter) : TagConsumer<PrintWriter> by result.appendHTML()

    override fun onError(exception: Exception) {
        // prints a very basic error block
        h4(classes = "error") {
            style = "background-color: red; text-color: white"
            onTagContent(exception.message ?: "Unknown error")
        }

        exception.printStackTrace() // send to stderr log
    }

}

It cannot get much simpler than that. As you see, it has all the freedom to properly react to the exception. For example, the tag contents might simply remain empty, while the rest of the page remains fully-functional.

I use Kotlinx.html for stream-like processing: page headers are sent almost instantly (by abusing TagConsumer.finalize and PrintWriter.flush), while the rest is generated gradually, like in the original PHP (but with good compiler) for lighting-fast zero-delay instant content delivery. Therefore errors are rather common and must be handled.

@kosiakk
Copy link
Contributor Author

kosiakk commented Sep 6, 2016

Do you think, such approach is reasonable? I have no idea how does it work in JS target.

My original implementation was very simple:

interface TagConsumer<out R> {
     // ...

    // default implementation simply re-throws the exception after closing the tag
    fun onError(exception: Exception) : Unit = throw exception
}

fun <T : Tag> T.visit(block: T.() -> Unit) {
    consumer.onTagStart(this)
    try {
        this.block()
    } catch (err: Exception) {
        consumer.onError(err)
    } finally {
        // all tags are properly closed
        consumer.onTagEnd(this)
    }
}

kosiakk added a commit to kosiakk/kotlinx.html that referenced this issue Sep 6, 2016
Tag Consumers might handle exceptions (issue Kotlin#21)
@cy6erGn0m
Copy link
Contributor

The only problem I can see is that if we have a chain of consumers then the error handler in the middle of the chain can only write directly to the delegate rather than pass through the whole chain

pros: we can't get infinite error handling issue
cons: it's not clear what the state the outer consumer will have after writing directly inside of the error handler

@kosiakk kosiakk mentioned this issue Sep 8, 2016
@kosiakk kosiakk closed this as completed Sep 15, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants