Using XForms from Java apps

What this page is about

This page relates to the use of plain XForms applications. If you use Form Builder and Form Runner, the content of this page is probably not relevant.

Orbeon doesn't recommend you write new code using this technology. Instead, Orbeon recommends that you use Form Builder and Form Runner, including the Form Runner Java Embedding API for integration of Java applications with Form Runner.

Introduction

Some example applications shipping with Orbeon Forms hook up XForms by using the Page Flow Controller (PFC) and XML pipelines. But it is also possible to use the XForms engine without worrying about these technologies and to simply use it as an XForms engine for your Java applications.

The recommended way of using XForms this way is what is called separate deployment. With this method, your application is deployed in its own Java web archive (WAR), and Orbeon Forms in its own WAR. This has the following benefits:

  • Easier upgrades of both your application and Orbeon Forms.

  • Preventing situations where different versions of JAR files could conflict.

  • Cleaner application architecture.

This is implemented with the help of a servlet filter, referred to as the XForms filter.

Deployment and configuration

web.xml configuration

The value of the oxf.xforms.renderer.context parameter specifies the context into which you have deployed Orbeon Forms. By default, Orbeon forms deploys to /orbeon so this value is usually safe. If you deploy Orbeon Forms to another context, you need to change this value accordingly.

The <url-pattern> defined under the first <filter-mapping> has the value /xforms-jsp/*. This means that all the data generated by URLs starting with /xforms-jsp/ is post-processed by Orbeon Forms. You can change this value as desired.

The <url-pattern> defined under the second <filter-mapping> has the value /orbeon/*. This is necessary to allow for all Orbeon Forms resources, such as JavaScript, CSS, and Ajax server, to be accessible. This /orbeon/* value is related to the default context into which you deploy Orbeon Forms: if you change your context, you change this value as well.


<!-- Filter configuration -->
<filter>
    <filter-name>orbeon-xforms-filter</filter-name>
    <filter-class>org.orbeon.oxf.servlet.OrbeonXFormsFilter</filter-class>
    <init-param>
        <param-name>oxf.xforms.renderer.context</param-name>
        <param-value>/orbeon</param-value>
    </init-param>
    <init-param>
        <param-name>oxf.xforms.renderer.default-encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>-->
</filter>

<!-- Any web resource under /xforms-jsp is processed by the XForms engine -->
<filter-mapping>
    <filter-name>orbeon-xforms-filter</filter-name>
    <url-pattern>/xforms-jsp/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

<!-- This is necessary so that XForms engine resources can be served appropriately -->
<filter-mapping>
    <filter-name>orbeon-xforms-filter</filter-name>
    <url-pattern>/orbeon/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

[SINCE Orbeon Forms 4.6.1]

The filter parameter oxf.xforms.renderer.default-encoding allows you to specify a default character encoding when the content provided by the servlet or JSP page doesn't specify one. Previously, the default character encoding was hardcoded to ISO-8859-1. Since 4.6.1 the hardcoded default remains this, but you can change it with oxf.xforms.renderer.default-encoding. The default Orbeon Forms web.xml overrides this default to UTF-8, which is what most modern HTML uses.

[SINCE Orbeon Forms 2023.1]

If you are using Orbeon Forms 2023.1 or newer, and are running a servlet container that uses the Jakarta Servlet API (e.g. Tomcat 10+, WildFly 27+), you need to use the org.orbeon.oxf.servlet.JakartaOrbeonXFormsFilter servlet filter class instead of org.orbeon.oxf.servlet.OrbeonXFormsFilter.

Limitation: Currently you cannot deploy your application in the default servlet context (i.e. the empty context).

Application server configuration

Your WAR must be deployed in such a way that it is allowed forwarding requests to other web applications.

Location of JSPs

With the default configuration shown above, all JSPs located in the directory called xforms-jsp in your WAR are processed by the XForms engine. However, it is likely that you will prefer another location. In that case, you just change the url-mapping configuration.

You must not deploy resources under the /orbeon/ directory, as that directory is reserved for Orbeon Forms resources.

Other Java resources

You don't have to produce XForms from JSP. You can do so directly from servlets, or other Java web application frameworks (usually based on servlets and template languages). What matters is that the filter defined in web.xml kicks in for those resources and that you produce well-formed XML as output. For this to happen, you modify the <filter-mapping> accordingly to enable the filter for the URLs handled by your framework.

Access control

You control security for all of your application's pages, including XForms pages, in your own application's web.xml. It is not possible to access your application's XForms pages by accessing Orbeon Forms URLs directly: your application controls the generation of XForms content, not Orbeon Forms.

However by default you can still access Orbeon Forms applications through Orbeon Forms URLs. If you don't want to deploy any Orbeon Forms applications directly, you can block external accesses to the Orbeon Forms WAR by configuring the Orbeon Forms WAR's web.xml.

Setting up your application

You deploy Orbeon Forms as a separate WAR with the following steps:

  1. Deploy Orbeon Forms as usual, typically in the /orbeon context. Follow the installation instructions and the Orbeon Forms Tutorial if needed.

  2. Deploy your own application as a separate WAR.

  3. Copy WEB-INF/lib/orbeon-xforms-filter.jar from orbeon-xforms-filter.war into your application's WEB-INF/lib/ directory.

  4. Configure your application's web.xml as described in the previous section to setup the Orbeon Forms XForms filter.

  5. Setup your application in cross-context mode, as described in the previous section.

  6. From the uncompressed orbeon.war, you can remove:

    • All the files under WEB-INF/resources, except the directory WEB-INF/resources/config and its content. (This directory contains configuration files which you might want to change, in particular the properties files.)

    • All the application-server or portal configuration files under WEB-INF; namely: jboss-web.xml, jonas-web.xml, liferay-display.xml, liferay-portlet.xml, portlet.xml, sun-web.xml, weblogic.xml. (The application-server specific files contain an example of how to declare a data source, so you can safely remove those files even if they are for the application server you are using.)

    • The WEB-INF/classes directory.

    • The WEB-INF/commons-cli-1_0.jar file.

Session handling

Before forwarding Ajax requests to Orbeon Forms, the XForms filter checks that a session exists for the current user. This is done to deal with the scenario where users log out from your application but still have, say in another tab, a form created with Orbeon Forms open. When they logout, you invalidate the session in your app, but the Orbeon session is still there. Without this check, after logging, users could switch to the other tab, and continue to interact with the form, and it would work as the Ajax requests would be routed to Orbeon Forms, whose session is still alive, which, obviously, you wouldn't want that happen.

If your application never creates a session, this check will always fail, and Ajax request will never go through. To get around this, you can either:

  • Setup your application server so your application session is shared with the Orbeon Forms session.

    • For instance, if using WebLogic, you can do so by deploying your app and Orbeon Forms in the same EAR, and setting <sharing-enabled> to true in weblogic-application.xml.

    • With Tomcat, enable Single Sign On.

  • Always create a session in your code, e.g. with request.getSession(), even if you're not going to store anything in that session.

All URLs are designed go through your web application's context, so your application and the Orbeon Forms XForms engine automatically share the same session.

Generating XHTML and XForms

Your JSP pages or servlets must generate well-formed XML documents that contain XHTML and XForms tags. There are two methods of passing this information to Orbeon Forms, described below.

Producing XHTML and XForms as JSP or servlet output

With this method, your JSP or servlet simply outputs XHTML and XForms in its output as it would HTML content. For example, a basic JSP page typically looks like this:

<xh:html
    xmlns:xh="http://www.w3.org/1999/xhtml"
    xmlns:xf="http://www.w3.org/2002/xforms"
    xmlns:xxf="http://orbeon.org/oxf/xml/xforms">
    <xh:head>
        <xh:title>Guess The Number</xh:title>
        <xf:model>
            <xf:instance>...</xf:instance>
        </xf:model>
    </xh:head>
    <xh:body>
        <xh:h1>My Page</xh:h1>
        <xf:input ref="..."/>...
    </xh:body>
</xh:html>

When using JSP, you can then use JSP tags as usual to produce your XHTML and XForms page as you would a regular HTML page.

Passing XHTML and XForms as a request attribute

With this method, the output of your JSP or servlet is ignored by Orbeon Forms. Instead, you set an attribute into the HttpServletRequest object which is passed to servlets (and also accessible in JSP through the request variable):

request.setAttribute("oxf.xforms.renderer.document", xformsDocument);

The name of the attribute must be "oxf.xforms.renderer.document". It may contain XHTML and XForms as an XML Document Object Model (DOM), as a dom4j Document, or as a String containing XML.

File inclusions

When you deploy your XForms in the Orbeon Forms resources directory, you can reference instances in separate files from your XForms with: <xf:instance src="oxf:/path/file.xml">. You can also use XInclude to include arbitrary parts of your form with: <xi:include href="oxf:/path/file.xml">. The oxf: scheme tells Orbeon Forms that the file is loaded from the resource directory, typically from WEB-INF/resources inside the Orbeon Forms war file.

You could do this in separate deployment as well, but you would need to put the files to include in the Orbeon Forms resource directory. Since that resource directory is typically inside the Orbeon Forms war file, doing so would defeat the purpose of using separate deployment.

For instances, you could use a relative URL such as <xf:instance src="http://wiki.orbeon.com/file.xml">. If the URL for your form is http://localhost/myapp/forms/registration.jsp, then Orbeon Forms will load the file to include from http://localhost/myapp/forms/file.xml. Most likely this will work, but you will incur the cost of an additional HTTP request every time the page is loaded.

So instead, you will want to use an inclusion mechanism native to the template language you are using in your application. If you write JSP, use the include directive. To include an instance, use:

<xf:instance id="my-instance">
    <%@include file="file.xml"%>
</xf:instance>

You can use the include directive in the same way to include an arbitrary part of your form stored in a separate file:

<xh:body>
    <!-- Part of the form inline. -->
    <xh:div>
        ...
    </xh:div>
    <!-- Part of the form stored in a spearate file -->
    <%@include file="part.xhtml"%>
</xh:body>

How-tos

Passing instance data from Java to XForms during page initialization

Say your application produces XForms using JSP. You might want to pass some information from the Java side to the XForms side. This could be user profile information, data retrieved from a database, etc.

It may be tempting to directly inline data into instances, like this:

<xf:instance id="profile-instance">
  <profile>
    <user-id><% request.getAttribute('user-id') %></user-id>
  </profile>
</xf:instance>

This, is not a good way, for the following reason. The XForms engine will receive for a first request e.g.:

<xf:instance id="profile-instance">
  <profile>
    <user-id>john</user-id>
  </profile>
</xf:instance>

Then, for another request:

<xf:instance id="profile-instance">
  <profile>
    <user-id>mary</user-id>
  </profile>
</xf:instance>

The two are different, and this is enough for the XForms engine to determine that the two XForms documents are different, and so to disable caching.

The bottom line is this: in order for the best case scenario of caching to happen, the XForms engine must see the same incoming XForms markup.

The good news is that you can initialize instance data in a way that will achieve that, directly in XForms. For example:

<xf:bind
    ref="instance('profile-instance')/user-id"
    xxf:default="xxf:get-request-attribute('user-id', 'text/plain')"/>

With this XForms, the request attribute is queried directly from XForms and stored into the instance. Note the text/plain second parameter, which makes sure that the user-id attribute is not parsed as XML.

The difference with the first example is that the XForms code is the same for all requests, so caching can work.If the request attribute is an entire instance in XML format, you could also write:

<xf:instance id="profile-instance">
  <!-- Start with empty profile -->
  <profile/>
</xf:instance>

<!-- Then load profile from request -->
<xf:insert
    event="xforms-model-construct-done"
    ref="instance('profile-instance')"
    origin="xxf:get-request-attribute('profile')"/>

In this case, the value will be parsed as XML before insertion.

You can similarly use other functions, including:

  • xxf:get-request-parameter()

  • xxf:get-session-attribute()

Accessing submitted XML data

Orbeon Forms supports the input:instance URI to access XML data submitted to the current page:

<xf:instance id="input" src="input:instance"/>

This allows you to implement scenarios like this one:

  1. JSP page is accessed through HTTP GET

    1. Page generates XForms

    2. XForms is processed by the Orbeon filter and XForms engine

    3. HTML is sent to the browser

  2. User performs action on page

    1. XForms server performs a submission to another JSP page, submitting XML through the HTTP POST or PUT method

  3. New JSP page is accessed through HTTP POST or PUT

    1. Page generates XForms

    2. XForms is processed by the Orbeon filter and XForms engine

    3. XForms engine has access to the submitted XML data through the input:instance URI

    4. HTML is sent to the browser NOTE: In separate deployment, you can use input:instance only if your JSP or servlet has not attempted to read the request body first!

Implementing XForms services with JSP

The backend of your forms is usually best implemented with "services" which can be called with <xf:submission>. Most of the time, XML is being POSTed to the service and XML is returned by the service. Since services take XML as input and generate XML, XML pipelines are an ideal tool to implement services.

However, you can also implement simple services directly with JSP. To produce XML output, your JSP page has to set the appropriate content type for the response with:

response.setContentType("application/xml")

To read XML input, you can create an object that represents the input document using the dom4j API:

Document queryDocument = xmlReader.read(request.getInputStream())

You then use this object to gather data about the query sent to your service.

In XForms you reference the service with the action attribute of <xf:submission>:

<xf:submission
    id="do-query"
    ref="instance('query')"
    method="post"
    replace="instance"
    instance="photos"
    resource="/xforms-jsp/flickr-search/service-search.jsp"/>

<xf:submission
    id="do-query"
    ref="instance('query')"
    method="post"
    replace="instance"
    instance="photos"
    resource="/xforms-jsp/flickr-search/service-search.jsp"/>

Advanced

Processing model

What happens when your JSP or servlet produces an XHTML and XForms document?

  • If configured appropriately in web.xml, the Orbeon Forms XForms filter kicks in and intercepts the output of your JSP or servlet (whether produced the regular way or passed as a request attribute).

  • The Orbeon Forms XForms filter then forward the request to Orbeon Forms, at the location /xforms-renderer.

  • Orbeon Forms reacts to /xforms-renderer by extracting the XHTML and XForms document from the forwarded request.

  • Orbeon Forms sends the XHTML and XForms document to the standard Orbeon Forms epilogue called /config/epilogue-servlet.xpl. The epilogue performs several tasks, including transforming XHTML and XForms into HTML that the browser can understand. The default configuration of this pipeline should be fine for most use cases, which means you usually don't need to worry about it.

Note that the epilogue applies the default theme under /config/theme-plain.xsl. However, it does not perform further URL rewriting by default.

Attributes set by the Orbeon Forms filter

The Orbeon Forms filter implemented by OrbeonXFormsFilter sets the following request attributes:

Property nameValue typeComments

oxf.xforms.renderer.deployment

separate

oxf.xforms.renderer.base-uri

path

- contains request path, i.e. /xforms-jsp/guess-the-number/test.jsp - used for xml:base resolution by XForms

oxf.xforms.renderer.document

XHTML document as string

the string must contain well-formed XML

oxf.xforms.renderer.content-type

content-type

content-type of the document passed

oxf.xforms.renderer.has-session

true / false

whether the filter sees an existing session or not

NOTE: In general, you do not need to know about these properties to use the Orbeon Forms filter.

Last updated