Skip to content

Instantly share code, notes, and snippets.

@avernet
Last active December 15, 2025 02:03
Show Gist options
  • Select an option

  • Save avernet/e5414a1a9916dd98623fcd531767f51b to your computer and use it in GitHub Desktop.

Select an option

Save avernet/e5414a1a9916dd98623fcd531767f51b to your computer and use it in GitHub Desktop.
Docker compose config with CORS setup for embedding

To run

  • Add orbeon.war and orbeon-embedding.war.
  • Run docker compose up.

Embedding demo: overriding orbeon-forms-context

The embedding demo JSP reads:

ServletContext.getInitParameter("orbeon-forms-context")

The WAR’s WEB-INF/web.xml provides a default value (typically /orbeon). To override it without modifying the WAR, we use a Tomcat <Parameter> in the context descriptor:

  • runtime/war-tomcat/orbeon-embedding.xml sets:
    • name="orbeon-forms-context"
    • value="https://orbeon.local:8081"
    • override="false" so the WAR’s web.xml cannot replace it

CORS + Secure session cookie (without modifying orbeon.war)

Important Tomcat detail: conf/Catalina/localhost/orbeon.xml is a Tomcat Context descriptor, not a web deployment descriptor. Tomcat does not apply <filter> / <filter-mapping> from that file.

To configure CORS and session cookie settings without changing orbeon.war, we inject a small WEB-INF/tomcat-web.xml into the Orbeon webapp:

  1. runtime/war-tomcat/tomcat-web.xml is mounted into the container at: /usr/local/tomcat/conf/tomcat-web.xml
  2. runtime/war-tomcat/orbeon.xml configures StandardRoot + a FileResourceSet to mount that file into the webapp at: /WEB-INF/tomcat-web.xml

Tomcat automatically processes /WEB-INF/tomcat-web.xml with higher precedence than the global defaults in conf/web.xml, allowing us to add:

  • org.apache.catalina.filters.CorsFilter
  • <session-config><cookie-config><secure>true</secure>...</cookie-config> so the JSESSIONID cookie includes Secure

Notes on observing headers

CORS headers are only added when the request includes an Origin header.

Example:

curl -I -H 'Origin: https://presenter.local:8081'
http://orbeon.local:8081/orbeon/fr/orbeon/bookshelf/new

services:
orbeon-forms:
container_name: orbeon-forms-tomcat
image: tomcat:9.0
volumes:
- ./orbeon-embedding.war:/usr/local/tomcat/webapps/orbeon-embedding.war
- ./orbeon-embedding.xml:/usr/local/tomcat/conf/Catalina/localhost/orbeon-embedding.xml
- ./orbeon.war:/usr/local/tomcat/webapps/orbeon.war
- ./orbeon.xml:/usr/local/tomcat/conf/Catalina/localhost/orbeon.xml
- ./tomcat-users.xml:/usr/local/tomcat/conf/tomcat-users.xml
- ./tomcat-web.xml:/usr/local/tomcat/conf/tomcat-web.xml
- ~/.orbeon:/root/.orbeon
#- ~/Orbeon/orbeon-forms/local:/usr/local/tomcat/resources-local
#- ~/Orbeon/orbeon-forms/catalina/webapps/ROOT:/usr/local/tomcat/webapps/ROOT
#- ~/.orbeon/lib/mysql-connector-java-5.1.47.jar:/usr/local/tomcat/lib/mysql-connector-java-5.1.47.jar
ports:
- 8080:8080
<Configuration status="error" dest="err" name="OrbeonWebAppConfig">
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%date{ISO8601} %-5level %logger{1} %X{orbeon-incoming-http-header-host} - %message%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.orbeon.oxf.xforms.processor.XFormsServer" level="debug"/>
<Logger name="org.orbeon.oxf.fr.FormRunnerPersistence" level="debug"/>
<Logger name="org.orbeon.oxf.processor.DebugProcessor" level="debug"/>
<Logger name="org.orbeon.oxf.processor.pdf.XHTMLToPDFProcessor" level="debug"/>
<Logger name="org.orbeon.properties" level="warn"/>
<Logger name="org.orbeon.relational" level="debug"/>
<Logger name="org.orbeon.auth" level="debug"/>
<Logger name="org.orbeon.filter.form-runner-auth" level="info"/>
<Logger name="org.orbeon.lifecycle" level="debug"/>
<Logger name="org.orbeon.xforms.submission.two-pass" level="debug"/>
<Logger name="org.orbeon.caches" level="trace"/>
<Logger name="org.exist.storage.btree.Paged" level="warn"/>
<Logger name="org.exist.storage.DBBroker" level="warn"/>
<Logger name="org.exist.storage.BrokerPool" level="warn"/>
<Root level="info">
<AppenderRef ref="ConsoleAppender"/>
</Root>
</Loggers>
</Configuration>
<Context path="/orbeon-embedding" reloadable="false" override="true" allowLinking="true" swallowOutput="false">
<Resources cachingAllowed="false" />
<Parameter
override="false"
name="orbeon-forms-context"
value="https://orbeon.local:8081/orbeon"/>
<Manager pathname=""/>
</Context>
<Context path="/orbeon" reloadable="false" override="true" allowLinking="true" swallowOutput="false">
<CookieProcessor sameSiteCookies="none"/>
<Resources cachingAllowed="false" className="org.apache.catalina.webresources.StandardRoot">
<!-- Inject /WEB-INF/tomcat-web.xml without modifying orbeon.war -->
<PostResources
className="org.apache.catalina.webresources.FileResourceSet"
base="/usr/local/tomcat/conf/tomcat-web.xml"
webAppMount="/WEB-INF/tomcat-web.xml"
internalPath="/" />
</Resources>
<Resource
name="jdbc/mysql" driverClassName="com.mysql.jdbc.Driver"
auth="Container" type="javax.sql.DataSource"
initialSize="3" maxActive="10" maxIdle="10" maxWait="30000"
poolPreparedStatements="true" testOnBorrow="true" validationQuery="select 1"
username="avernet" password=""
url="jdbc:mysql://host.docker.internal:3306/avernet?useSSL=false"/>
<Parameter
override="true"
name="oxf.resources.priority.1.oxf.resources.filesystem.sandbox-directory"
value="/usr/local/tomcat/resources-local"/>
<Parameter
override="true"
name="oxf.resources.priority.1"
value="org.orbeon.oxf.resources.FilesystemResourceManagerFactory"/>
<Manager pathname=""/>
</Context>
<tomcat-users xmlns="http://tomcat.apache.org/xml" version="1.0">
<user username="a" password="a" roles="orbeon-user"/>
</tomcat-users>
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<session-config>
<cookie-config>
<secure>true</secure>
</cookie-config>
</session-config>
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
<init-param>
<param-name>cors.allowed.origins</param-name>
<param-value>https://presenter.local:8081</param-value>
</init-param>
<init-param>
<param-name>cors.support.credentials</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.methods</param-name>
<param-value>GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH</param-value>
</init-param>
<init-param>
<param-name>cors.allowed.headers</param-name>
<param-value>orbeon-client,content-type</param-value>
</init-param>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
</web-app>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment