InstallationForm Builder is distributed as a Java WAR file. You install it into a Servlet container, preferably Tomcat 5.5.Installation notes are availabe online as part of the Orbeon Forms tutorial: We recommend, for testing, that:
Once the WAR file is deployed, restart your container and access the following URL, assuming a default port of 8080 for your container: This is the Summary page of Form Builder. The page should look like the following (most likely without any content in the summary table): Then select "New Form". This takes you to a new empty form:
Form types and form dataWe make a distinction between:
In Form Builder, a form type is identified by a two-level hierarchy of names:
This two-level hierarchy allows for easy grouping of forms, and allows using a single instance of Form Builder to host distinct applications. Form data is identified by a three-level hierarchy which includes:
Overall architectureForm Builder actually consists of two parts:
Form Builder/Runner are implemented on top of the open source Orbeon Forms platform. They are implemented entirely with the following technologies:
Form Builder is written essentially in XForms, but it also produces, for each form type edited, an XForms file which Form Runner can load and run. In short, Form Builder is an XForms application which is able to produce other XForms applications. Both Form Builder and Form Runner share a persistence layer, which in turn allows them to talk to databases or external services. Form Builder/Runner comes with a built-in XML database called eXist, which by provides persistence out of the box. However, you can also access an external eXist instance, or implement your entirely custom persistence layer in your own system. The following figure illustrates the overall architecture: Integration points
Form Builder/Form Runner integrate with other systems through two main means: |
| Storage | Notes | More information |
| eXist | A full persistence layer implemented on top of the open source eXist database. You can setup this persistence layer to either use the internal eXist database, or an external one. | |
| Oracle | A full persistence layer implemented on top of the Oracle database. Forms and data are stored using Oracle XMLType columns. |
Oracle persistence layer |
| MySQL | A full persistence layer implemented on top of MySQL. | MySQL persistence layer |
| File system (resource) | A minimal, read-only persistence layer implemented on top of Orbeon Forms' resource manager. This allows reading form definitions stored within the Orbeon Forms WAR file. |
In the
future, Form Builder could include more persistence implementations, for targeting additional relational databases or CMS systems.
This configuration is done in an XML property file called properties.xml. This file is located under your exploded WAR file at the following location:
Persistence configuration properties look like this in properties.xml:
All those property names start with oxf.fr.persistence.app.uri to denote that they configure the Form Runner's persistence layer per application. Follows:
There are two types of persistence services:
The default configuration properties above specify the following:
Form Builder calls URLs as follows:
In addition, the Oracle XML supports versioning to some level, in that it keeps older versions. However this is not visible at the level of the REST API.
If all you need is keep older versions in the database, it might be sufficient to use the Oracle XML persistence layer implementation, or to implement a similar system for eXist or your own persistence layer. The best will be for Form Runner and Form Builder to handle versioning natively.
When the resource is an XML file (e.g. form.xhtml, data.xml), the persistence layer must return an appropriate XML content-type: application/xml, or application/xhtml+xml.
When using POST to perform a search, the format is as follows:
NOTE: In Milestone 1, sort-key is not yet supported.
The query element is the most complex element. It is used for structured search. The path attribute is the one that matters: it contains a path to the element or attribute in the XML data to retrieve.
Say your documents looks like this:
Then the path personal-info/first-name matches XML elements following the XPath notation, relative to the root element of the document. The paths sent by Form Runner are simple paths, not full XPath expressions.
If the query element is empty, it just denotes the details to return in the response. If it contains a string, then it requires performing a search.
The persistence layer must return a document of this form:
The order of the detail elements is determined by the order of the query elements of the request.
First, configure properties.xml, for example:
What this does is tell Form Runner to dispatch all persistence API calls for applications with name "acme" to the specified URL root. Replace the URL as appropriate, but it must point to a server able to implement the persistence API's behavior. It could be implemented with Java, .NET, PHP, Ruby, Orbeon Forms itself, etc.
You must then implement a server component responding to the /persistence path on server example.com. Upon receiving a request, it must:
Depending on the type of storage chosen, storage operations may be more or less complex.
Configuring persistence through properties
Storage for the persistence hierarchy can be configured at multiple levels:- Globally
- For each application
- For each form type within an application
- For each form type (the form definition and associated resources) vs. form data (filled-out form data)
This configuration is done in an XML property file called properties.xml. This file is located under your exploded WAR file at the following location:
WEB-INF/resources/config/properties.xml
You
can edit this file with any text editor. Some of the properties do not
require a restart or Orbeon Forms, but in doubt you can restart the
Servlet container.Persistence configuration properties look like this in properties.xml:
<property as="xs:anyURI" name="oxf.fr.persistence.app.uri.*.*.*" value="/fr/service/exist"/>
<property as="xs:anyURI" name="oxf.fr.persistence.app.uri.orbeon.builder.form" value="/fr/service/resource"/>
<property as="xs:anyURI" name="oxf.fr.persistence.app.uri.orbeon.bookcast.form" value="/fr/service/resource"/>
All those property names start with oxf.fr.persistence.app.uri to denote that they configure the Form Runner's persistence layer per application. Follows:
- A string to specify the application name, like "orbeon" or "acme".
- A form type name, like "bookcast" or "address". Note that "builder" is a special form type name for Form Builder itself.
- Whether the configuration regards form data ("data"), or the form configuration files ("form").
There are two types of persistence services:
- Local: those are handled by Orbeon Forms, and start with the prefix /fr/service/
- Remote: those are handled by third-party software, and are absolute URLs starting with http: or https:
- oxf.fr.persistence.app.uri.*.*.* -> form types and form data for all applications
- oxf.fr.persistence.app.uri.*.*.data -> form data for all applications
- oxf.fr.persistence.app.uri.*.*.form -> form types for all applications
- oxf.fr.persistence.app.uri.orbeon.*.form -> form types for all forms in application "orbeon"
- oxf.fr.persistence.app.uri.orbeon.*.data -> form data for all forms in application "orbeon"
- oxf.fr.persistence.app.uri.orbeon.bookcast.* -> form type and data for "orbeon bookcast"
- oxf.fr.persistence.app.uri.orbeon.bookcast.form -> form type for "orbeon bookcast"
- oxf.fr.persistence.app.uri.orbeon.bookcast.data -> form data for "orbeon bookcast"
The default configuration properties above specify the following:
- By default, everything is stored into eXist
- The "orbeon" application's "builder" and "bookcast" form definitions are stored into Orbeon Forms resources
- But their associated form data is stored into eXist
- /fr/service/exist points to Form Runner's internal eXist API implementation
- /fr/service/oracle points to Form Runner's internal Oracle XML API implementation
- /fr/service/resource points to Form Runner's internal resource manager API implementation
Persistence API
The Form Builder persistence API is based on REST. This means that Form Builder and Form Runner use HTTP to communicate with a persistence layer.Form Builder calls URLs as follows:
- Create, Read, update, delete form data -> HTTP GET, PUT, DELETE to:
/crud/[APPLICATION]/[FORM_TYPE]/data/[FORM_DATA_ID]/data.xml - Search -> POST to:
/search/[APPLICATION]/[FORM_TYPE
- GET: read a resource
- PUT: create or update a resource
- DELETE: delete a resource
Support for versioning
Form Runner / Builder do not at the moment fully support versioning, but when retrieving form.xhtml, Form Runner also passes a URL parameter when possible:- .../form.xhtml?document=6E6FC50F4BB945235EB5B573F2C7E695
- editing form data
- viewing form data
- producing a PDF
- creating new form data
- listing form data on the summary page
In addition, the Oracle XML supports versioning to some level, in that it keeps older versions. However this is not visible at the level of the REST API.
If all you need is keep older versions in the database, it might be sufficient to use the Oracle XML persistence layer implementation, or to implement a similar system for eXist or your own persistence layer. The best will be for Form Runner and Form Builder to handle versioning natively.
Data exchange format
When using GET, PUT and DELETE to deal with resources, the body of HTTP requests just contains the resource to handle.- GET: request body is empty, response body contains resource
- PUT: request body contains resource, request response is empty
- DELETE: both request and response bodies are empty
When the resource is an XML file (e.g. form.xhtml, data.xml), the persistence layer must return an appropriate XML content-type: application/xml, or application/xhtml+xml.
When using POST to perform a search, the format is as follows:
<search xmlns="">
<!-- Application name -->
<app>orbeon</app>
<!-- Form type name -->
<form>address</form>
<!-- Free text search query -->
<query/>
<!-- Structured search query -->
<query name="first-name" path="personal-info/first-name"/>
<query name="last-name" path="personal-info/last-name">smith</query>
<!-- Paging -->
<page-size>10</page-size>
<page-number>1</page-number>
<!-- Sorting -->
<sort-key/>
<!-- Language -->
<lang/>
</search>
<!-- Application name -->
<app>orbeon</app>
<!-- Form type name -->
<form>address</form>
<!-- Free text search query -->
<query/>
<!-- Structured search query -->
<query name="first-name" path="personal-info/first-name"/>
<query name="last-name" path="personal-info/last-name">smith</query>
<!-- Paging -->
<page-size>10</page-size>
<page-number>1</page-number>
<!-- Sorting -->
<sort-key/>
<!-- Language -->
<lang/>
</search>
NOTE: In Milestone 1, sort-key is not yet supported.
The query element is the most complex element. It is used for structured search. The path attribute is the one that matters: it contains a path to the element or attribute in the XML data to retrieve.
Say your documents looks like this:
<form>
<personal-info>
<first-name>John</first-name>
<last-name>Doe</last-name>
<birth-date>1980-01-01</birth-date>
</personal-info>
</form>
<personal-info>
<first-name>John</first-name>
<last-name>Doe</last-name>
<birth-date>1980-01-01</birth-date>
</personal-info>
</form>
Then the path personal-info/first-name matches XML elements following the XPath notation, relative to the root element of the document. The paths sent by Form Runner are simple paths, not full XPath expressions.
If the query element is empty, it just denotes the details to return in the response. If it contains a string, then it requires performing a search.
The persistence layer must return a document of this form:
<documents total="2" page-size="10" page-number="1" query="">
<document created="2008-03-14T12:33:15.735-07:00" last-modified="2008-03-14T12:36:51.657-07:00" name="847C6B6ADB949146C0105433A374B5CE">
<details>
<detail>John</detail>
<detail>Doe</detail>
</details>
</document>
<document created="2008-03-14T12:32:53.735-07:00" last-modified="2008-03-14T12:32:53.735-07:00" name="85F3E3433DEB6E1EDE9DCD6F87D24240">
<details>
<detail>Sofia</detail>
<detail>Smith</detail>
</details>
</document>
</documents>
<document created="2008-03-14T12:33:15.735-07:00" last-modified="2008-03-14T12:36:51.657-07:00" name="847C6B6ADB949146C0105433A374B5CE">
<details>
<detail>John</detail>
<detail>Doe</detail>
</details>
</document>
<document created="2008-03-14T12:32:53.735-07:00" last-modified="2008-03-14T12:32:53.735-07:00" name="85F3E3433DEB6E1EDE9DCD6F87D24240">
<details>
<detail>Sofia</detail>
<detail>Smith</detail>
</details>
</document>
</documents>
The order of the detail elements is determined by the order of the query elements of the request.
Scenario
This scenario describes how company Acme can go about implementing their own persistence service.First, configure properties.xml, for example:
<property as="xs:anyURI" name="oxf.fr.persistence.app.uri.acme.*.data" value="http://example.com/persistence"/>
What this does is tell Form Runner to dispatch all persistence API calls for applications with name "acme" to the specified URL root. Replace the URL as appropriate, but it must point to a server able to implement the persistence API's behavior. It could be implemented with Java, .NET, PHP, Ruby, Orbeon Forms itself, etc.
You must then implement a server component responding to the /persistence path on server example.com. Upon receiving a request, it must:
- Check the HTTP method to determine the operation to perform (which CRUD operation or search operation)
- Check the requested path to determine the location of the resource
- In
the case of PUT, read the request body and store it appropriately. This
might require writing data into a CLOB or XML type column in a
relational database, for example.
- In the case of GET, read the storage and return the appropriate resource.
- In the case of DELETE, delete the appropriate resource in storage
- In the case of POST, perform a search. This might require generating an SQL query, for example.
Depending on the type of storage chosen, storage operations may be more or less complex.

