Embedding and Proxying Orbeon Forms

Rationale

Often times, Orbeon Forms integrators wish to embed Orbeon Forms within a portal or another application. Orbeon Forms offers some support for this, but does not necessarily have a one-size-fits-all solution. This page is meant to help you better understand what is involved to make such as setup work.

Existing support

Orbeon Forms features some support for portals and embedding:
What is not supported out of the box:
  • Portlet 1 (JSR-168) (instead of the newer Portler 2 (JSR-286), which is supported by Orbeon Forms)
  • a Java tag library or other API to embed Orbeon Forms into an arbitrary page

Concepts

What embedding means

Here by "embedding" we don't mean shipping Orbeon Forms with something else. Rather we mean setting up an application to work with Orbeon Forms such as a page/form rendered by Orbeon Forms appears embedded within that application's page.

What proxying means

Here "proxying" means that Orbeon Forms runs as a separate entity, typically connected via HTTP. It could be on the same app server/container, or even in a separate machine. So for example:
  • client browser connects to your app
  • but your app uses proxy code to talk to Orbeon via HTTP behind the scene
NOTE: Proxying is a way to achieve embedding. It's not the only way: for example when the Orbeon Forms full portlet is used, there is no proxying going on.

URL rewriting

URL rewriting means that in HTML or XML produced by Orbeon, you replace (rewrite) some URLs before sending that HTML or XML to the client.

The purpose of URL rewriting is to adjust URLs so that when the user clicks on a link or performs another client action, then links, Ajax requests, and form submission go to the "right place". The same applies to ressources loaded by the page like JavaScript, CSS, and image files.

For example, within a Java portlet container, a form submission must reach a specific portlet, not a regular web application running in the container.

Session forwarding

Orbeon Form expects that if it produces a page's HTML within a given Java servlet session, then later incoming Ajax requests address the same session. This means in general that the JSESSIONID cookie received by Orbeon Forms must be correct. This can mean, such as in the Form Runner proxy portlet or TYPO3 extension cases, that the proxy/extension must store the mapping between a client session and the Orbeon Forms session. See for example getRemoteSessionId() and  setRemoteSessionId() from OrbeonProxyPortlet.scala.

HTML fragment

When Orbeon Forms runs standalone, it produces a full HTML page. This includes HTML <html>, <head> and <body> elements. When Orbeon Forms produces HTML to be embedded into a portlet or another application, it produces an HTML fragment, rooted in a <div> element instead. This is because you can't simply embed a full HTML document within another. For any Orbeon page, you can enable the production of an HTML fragment by appending the orbeon-embeddable=true URL parameter. For example:

http://orbeonhost:8080/orbeon/xforms-controls/?orbeon-embeddable=true

This produces a fragment that looks like this:

<div class="orbeon-portlet-div">
    <link rel="stylesheet" href="/orbeon/3.9.0/config/theme/orbeon.css" type="text/css">
    <!-- Other <link> and <style> elements here -->
    ...
    <script type="text/javascript"
            src="/orbeon/3.9.0/ops/yui/yahoo/yahoo.js" class="xforms-baseline"></script>
    <!-- Other <script> elements here -->
    ...
    <div id="orbeon" class="orbeon-portlet-body orbeon">
        <div class="maincontent">
            <noscript>...</noscript>
            <form id="xforms-form"
                  class="xforms-form xforms-initially-hidden xforms-layout-nospan"
                  action="/orbeon/xforms-controls/" method="POST" onsubmit="return false"
                  enctype="multipart/form-data">
                <input type="hidden" name="orbeon-embeddable" value="true">
                <input type="hidden" name="$uuid"
                       value="DB65EFB2-3B89-2A0C-B3CC-DE9AE1AE03E7">
                <!-- Other Orbeon hidden fields here -->
                ...
                <!-- Other Orbeon internal markup here -->
                ...
                <!-- Actual form markup here -->
                ...
            </form>
        </div>
    </div>
    <!-- Optional link to home -->
    <div class="orbeon-portlet-home">
        <a href="/orbeon/">Home</a>
    </div>
</div>

The fragment is produced by an XSLT stylesheet set via the oxf.epilogue.theme.embeddable property. By default:

<property as="xs:anyURI"  name="oxf.epilogue.theme.embeddable"
          value="oxf:/config/theme-embeddable-examples.xsl"/>

When embedding Orbeon Forms, you should always set the orbeon-embeddable=true URL parameter.

What Orbeon Forms needs on the client

Resources

NOTE: Below paths starting with /orbeon denote that Orbeon Forms is installed under the /orbeon servlet context. It is not mandatory to use /orbeon.

Orbeon Forms needs the following resources:
  • the HTML fragment (see above) containing the markup for the form
  • Orbeon Forms and user CSS files
  • other Orbeon Forms and user resources such as images (referred to in HTML or CSS)
  • resources served by the Orbeon XForms server, which resides at the path /orbeon/xforms-server

In Ajax mode, additionally:

  • Orbeon Forms and user JavaScript files
This means that the URLs seen by the browser must, directly or via proxying, reach Orbeon Forms.

The Orbeon XForms Server

The Orbeon XForms Server responds at the path /orbeon/xforms-server. It does the following:
  • responds to client Ajax requests (HTTP POST)
  • serves combined JavaScript and CSS resources (HTTP GET)
  • handle background file uploads (HTTP POST)
Any access to the XForms Server must include the proper session cookie.

Communication with the server to handle a given page's updates

After an Orbeon Forms form is loaded in the browser, the user can interact with it. As that happens, the form typically needs to talk back to the XForms Server, via two means:
  • Ajax mode: using Ajax requests
  • Noscript mode: using full HTML form submissions

Communication via Ajax

Ajax requests use the URL of the XForms Server.

Communication via HTML Form submission

In Noscript mode, things are different: because no JavaScript is used, the action attribute on the HTML <form> element is used to submit the whole HTML form. Each user action translates into an HTML form submission, which reloads the page entirely from the server.

For a given page, Orbeon Forms generates an action attribute with the URL of the current page as known by Orbeon. For example, if you load this standard Orbeon Forms page:

/orbeon/fr/orbeon/contact/edit/1ffd6bd30d25361062fc59a568240002

Then that's also the value placed in the <form> element:

<form id="xforms-form" class="xforms-form xforms-noscript xforms-layout-span"
       action="/orbeon/fr/orbeon/contact/edit/1ffd6bd30d25361062fc59a568240002"
       method="POST">

The POST method is always used for this.

NOTE: In some modes, instead, the URL is /orbeon/xforms-server-submit instead. [TODO: explain]

Navigating to new pages

Using XForms, there are 2 ways of navigating to a new page:
  • <xforms:load> action
  • <xforms:submission replace="all"> submission
This translates on the client to:
  • for simple case (<xforms:load>, submission with GET without special headers): JavaScript sets the new URL of the page,
  • for all other cases: a full HTML form POST submission takes place

Simple case

This behaves as if the user had entered the given URL in the browser URL bar. This case is rare, and can even be disabled.

HTML form POST

In this case:
  • client submits the form to the server
  • server processes the submission
    • typically opens an HTTP connection using GET, POST, or PUT
    • resulting content is streamed back to the client

Proxy portlet scenarios

What this is

This describes the case of a JSR-168/286 portlet with a hypothetical proxy portlet, similar to the Form Runner Liferay Proxy Portlet.

Initial form load

  • portal calls the portlet's doView()/render() method.
  • portlet must decide what to do then: load a given initial Orbeon Forms page, for example load:
    http://orbeonhost:8080/orbeon/xforms-controls/
  • portlet must make sure that appropriate session id is kept around
  • Orbeon returns an HTML fragment
  • portlet rewrite URLs in HTML fragment
  • portlet sends HTML fragment to portlet output
  • portlet output is aggregated by portal into resulting HTML page

URL rewriting for resources and Ajax requests

Say URL rewriting is NOT performed. This URL will be present in the browser:

/orbeon/3.9.0/config/theme/orbeon.css

Unless there is an /orbeon context deployed locally, requesting this URL will fail.

With a JSR-286 container, resource URLs can be rewritten to portlet resource URLs, and the proxy portlet can forward the request to Orbeon.

With a JSR-168 container, resources cannot go through the portlet, so either a proxy servlet must be used, or the Orbeon Forms servlet must be deployed under /orbeon.

URL rewriting for HTML form posts (Noscript updates and page navigation)

This is an important one.

Imagine, in particular in Noscript mode, that the HTML <form> action URL is not rewritten. Its value is, for example:

/orbeon/xforms-controls/

When the user clicks a button, the browser submits the whole page to that URL, which belongs to Orbeon Forms. Orbeon Forms produces a response which does not include the portal's header, footer, decorations, etc. In effect, the user has left the portal.

What needs to be done here is that the URL must be rewritten to be a portlet action URL. If this is done, the URL as seen in the browser is a URL encoded by the portal. When the HTML form is submitted to that URL, the portal can:
  • intercept the request
  • forward it to the proxy portlet
  • the proxy portlet forward the request to Orbeon
  • the proxy portlet stores the response content as the latest HTML to show
  • upon the following render request, the proxy portlet responds with new HTML as per an initial form load
  • the portal can render the portlet output  as expected

Data flow for page navigation

The data flow here is interesting:
  1. Client on page 1 sends request to portal
  2. Portal forwards request to proxy portlet
  3. Portlet forwards request to Orbeon
  4. Orbeon processes request via <xforms:submission> and performs HTTP request, typically to Orbeon itself, to retrieve page 2
  5. Orbeon process incoming request for page 2
  6. Page 2 returns response to caller, which is page 1's <xforms:submission>
  7. Page 1 streams the response back to the caller, which is the proxy portlet
  8. Proxy portlet sends the response back to the caller, which is the portal
  9. Finally, the response ends up in the browser

Embedded proxy scenarios

Rationale

This describes the scenario where the form is embedded within an application which is not a portal.

TODO: to possibilities for updates:
  • "Ajax portlet" scenario
  • HTML form POST to application, which re-renders the page

How to rewrite URLs

In a very simple case, the HTML returned by Orbeon could simply be parsed for URLs, in particular the <form action=""> attribute.

A better way, used by the Form Runner proxy portlet, is to put the Orbeon server in a special mode, where it encodes all URLs via a special WSRP-inspired scheme of markers. The portlet can then efficiently parse the resulting HTML for such markers, and rewrite URLs as it goes.

[SINCE: 2012-05-14] When Orbeon Forms receives a Orbeon-Client header set to the value portlet, it does the following:
  • it encodes encodes all URLs, including resource URLs, using the WSRP scheme
  • it changes the XForms HTML form submission path to /xforms-server-submit to facilitate the proxy's job of detecting XForms engine POSTs
This header must be set on all requests for pages, HTML, and CSS.

NOTE: Between 2011-10-18 and 2012-05-14, the header was named  Orbeon-Container instead of Orbeon-Client. It was renamed for clarity.

Orbeon Forms has a Java class which helps deal with WSRP encodingL WSRP2Utils.java.