XForms - Implementation notes about xforms:submission

Architecture

Submission types

  • implement Submission [should be a better name]
  • derive from BaseSubmission
  • control the way to establish a connection
    • RegularSubmission:
      • uses protocol, typically HTTP or HTTPS, but also can use oxf: or file:
      • via the Connection class
      • Connection returns a SubmissionResult
    • RequestDispatcherSubmission:
      • uses a Servet RequestDispatcher
    • ClientGetAll:
      • just tells the client to do a get of a client URL
    • CacheableSubmission:
      • wraps around RegularSubmission to provide aggressive caching
    • LocalPortletSubmission:
      • local Orbeon submission within a portlet
    • FilterPortletSubmission:
      • only for separate deployment in portlets with replace="all"
    • EchoSubmission:
      • for testing

Replacers

  • implement Replacer
  • derive from BaseReplacer
  • determine what to do with the result of a connection
    • AllReplacer:
      • for replace="all"
    • InstanceReplacer:
      • for replace="instance"
    • TextReplacer:
      • for replace="text"
    • NoneReplacer:
      • for replace="none"
      • also used by other replace="..." if there is no response body
      • also used internally RequestDispatcherSubmission to tell that no action must be taken by replacer
    • RedirectReplacer:
      • when 301/302 is received with replace="all"

Synchronous vs. asynchronous

Synchronous:
  • Callable<SubmissionResult> runs right away
    • starts connection
    • handles deserialization (without replacing)
  • returns SubmissionResult to caller
  • so Submission.connect() returns SubmissionResult
  • so XFMS.handleSubmissionResult runs right away
Async:
  • Callable<SubmissionResult> added to AsynchronousSubmissionManager
    • starts connection
    • handles deserialization (without replacing)
  • returns null to caller
  • so Submission.connect() returns null
  • so XFMS.handleSubmissionResult doesn't run
  • XFMS.doSubmitReplace calls XFMS.handleSubmissionResult when connection is done

Replace="all" vs. others

Replace="all" is different:
  • when response has content, tries to stream result to OutputStream in response
  • may run in two passes
The following is to note as of 2007-08-28:
  • Submission is in two phases when using anything but method="get". The two-phase mechanism has been in place since a very long time.
  • Some recent changes and fixes:
    • For the second phase, we now do NOT modify the containing document (if we do it's a bug) so we can keep the same dynamic state. This means we do not dispatch xforms-submit-serialize, xforms-submit-done or xforms-submit-error during the second phase.
    • XFormsServer makes sure to return the containing document to the pool when document caching is enabled.
  • If an error occurs during the second phase, an exception is thrown.
  • If an error occurs during the first phase, then however xforms-submit-error is dispatched as usual.

Cacheable submissions

TODO

Future enhancements/refactoring

Issues as of 2011-08-11:
  • current architecture is not as clean as it should be
  • lifecycle of submission, error handling, invariants are not easy to check
  • extensibility is hard
  • there is code duplication (in Submission types)
  • etc.
Things that should be easier / done better :
  • combining Submission type/Replacer
  • creating new Submission types
  • determining what Submission type to use
  • checking that error handling (and event dispatch) is correctly implemented
  • CacheableSubmission should be orthogonal to other Submission types
  • etc.

Notes on asynchronous submissions


During page initialization, the server may send polling events to dispatch after a delay, following this format:

var orbeonInitData = { "server-events":[ { "delay":9807, "discardable":true, "show-progress":false, "event":"X2ztnLbujs+..." } ] };

The content is the same as a server event sent back through Ajax.

Background submissions

Fire-and-forget submissions

NOTE: This is obsolete as of February 2010 builds.
  • In this case, Orbeon Forms does not create a new thread, but it uses the current Ajax request thread (for simplicity/economy reasons)
    • when async submission is found (@mode="asynchronous"), store information but do not run submission
    • XFormsServer sends Ajax response and closes connection
    • THEN process queued async submissions
  • Callable instances are added to XFCD when asynchronous submissions are encountered
  • openConnection() was refactored to separate headers processing and remove dependency on ExternalContext
  • XFCD.processAsynchronousSubmissions() is run:
    • at the end of XFormsServer processing
    • at the end of XFormsToXHTML processing
  • converters were refactored so that endDocument() causes the the Servlet OutputStream to be closed
    • allows client to entirely receive Ajax response before async submissions are processed on the server
    • this works in xforms-server.xpl, where oxf:xml-converter/oxf:html-converter are placed before oxf:http-serializer
    • this may not work properly for XFormsToXHTML given the complexity of the epilogue
      • -> async submissions during page initialization may delay the page loading
Comments