Configure the Application Server (Apache Tomcat on Linux)

Overview

This guide describes how to configure Apache Tomcat as the application server for Bloomreach Experience Manager (XM) on Linux. The instructions follow the recommended practices for the Developer Edition Stack and brXM Standard Stack.

Prerequisites

Before you begin, ensure the following requirements are met:

  • Java is installed:
    • Bloomreach Experience Manager 16: Oracle or OpenJDK Java 17 JDK (e.g., /usr/java/jdk-17.0.10)
    • Bloomreach Experience Manager 15: Oracle or OpenJDK Java 11 JDK (e.g., /usr/java/jdk-11.0.22)
    • Bloomreach Experience Manager 14: Oracle or OpenJDK Java 8 JDK (e.g., /usr/java/jdk-1.8)
  • A supported relational database server (such as MySQL) is installed. See System Requirements for the full list.

Target Configuration

Following this guide results in:

  • Bloomreach Experience Manager running under a dedicated user account (cms) with home directory /opt/cms
  • Apache Tomcat installed under /usr/local
  • Tomcat common libraries located at /usr/local/share/tomcat-common/lib

Note: These are recommended paths. You can use different locations if required by your environment.

Preparation

Create a dedicated user account to run Bloomreach Experience Manager:

useradd -m -d /opt/cms cms

Install and Configure Apache Tomcat

Note: To simplify upgrades, separate Catalina Base from Catalina Home. Store all custom configuration in Catalina Base, and keep Catalina Home unmodified. Upgrading Apache Tomcat then only requires updating a symlink and restarting the service.

Install Apache Tomcat (Catalina Home)

  1. Download the latest supported version from Apache Tomcat:

    • Use version 10.1 for brXM 16
    • Use version 9.0 (or 8.5) for brXM 15/14
  2. Unpack the archive:

cd /usr/local tar -xzvf apache-tomcat-<LATEST>.tar.gz
  1. Create a symbolic link for easier upgrades:
ln -s apache-tomcat-<LATEST> tomcat
  1. Set directory and file permissions:
find /usr/local/tomcat/ -type d -print0 | xargs -0 chmod 755
find /usr/local/tomcat/ -type f -print0 | xargs -0 chmod 644
find /usr/local/tomcat/ -type f -name \*\.sh -print0 | xargs -0 chmod 755

Install Common Libraries

  1. Create the directory for shared libraries and place the required database connector JAR:
mkdir -p /usr/local/share/tomcat-common/lib cd /usr/local/share/tomcat-common/lib # Example: MySQL connector wget http://search.maven.org/remotecontent?filepath=com/mysql/mysql-connector-j/8.4.0/mysql-connector-j-8.4.0.jar -O mysql-connector-j-8.4.0.jar

Note: If you use a different database, replace the MySQL connector with the appropriate JDBC driver. See System Requirements for supported databases.

  1. For brXM versions before 16, add the following JARs if they are not already present in the distribution's common/lib directory:
# Geronimo JTA spec wget http://search.maven.org/remotecontent?filepath=org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar -O geronimo-jta_1.1_spec-1.1.1.jar # Jakarta Mail wget http://search.maven.org/remotecontent?filepath=com/sun/mail/jakarta.mail/1.6.7/jakarta.mail-1.6.7.jar -O jakarta.mail-1.6.7.jar # JCR wget http://search.maven.org/remotecontent?filepath=javax/jcr/jcr/2.0/jcr-2.0.jar -O jcr-2.0.jar

Configure Apache Tomcat (Catalina Base)

Note: Catalina Base is where Tomcat runs from and where all configuration changes are made. Keep Catalina Home unmodified.

Prepare Catalina Base

  1. Switch to the cms user and create the required directory structure:
su - cms mkdir -p tomcat/bin tomcat/conf tomcat/logs tomcat/shared/lib heapdumps tomcat/temp tomcat/webapps tomcat/work ln -sf /usr/local/tomcat/bin/startup.sh tomcat/bin/startup.sh ln -sf /usr/local/tomcat/bin/shutdown.sh tomcat/bin/shutdown.sh cd /usr/local/tomcat/conf cp catalina.policy catalina.properties server.xml web.xml tomcat-users.xml ~/tomcat/conf cd ~/tomcat

Note: By default, tomcat-users.xml is referenced in server.xml. If you do not use this file, Tomcat will log a SEVERE warning. To avoid this, comment out the UserDatabase resource and realm in conf/server.xml.

Configure the Tomcat Instance

  1. To allow the CMS application to open the RMI port, append the following to conf/catalina.policy:
grant codeBase "jar:file:${catalina.home}/webapps/" {
  permission java.net.SocketPermission "*:1099", "connect, accept, listen";
};
  1. In conf/catalina.properties, add the shared/lib path to the shared classloader:
shared.loader="${catalina.base}/shared/lib","${catalina.base}/shared/lib/*.jar"
  1. In the same file, add the tomcat-common/lib path to the common classloader:
common.loader=<original common.loader path>,"/usr/local/share/tomcat-common/lib","/usr/local/share/tomcat-common/lib/*.jar"

Add Environment-Specific Configuration for Bloomreach Experience Manager

  1. If you do not want the distribution's context.xml to take precedence, remove it and create a conf/context.xml file with at least the following content. Adjust the parameters as needed for your environment:
<?xml version='1.0' encoding='utf-8'?> <Context> <!-- Disable session persistence across Tomcat restarts --> <Manager pathname="" /> <Parameter name="repository-address" value="rmi://127.0.0.1:1099/hipporepository" override="false"/> <Parameter name="repository-directory" value="${catalina.base}/../repository" override="false"/> <Parameter name="start-remote-server" value="false" override="false"/> <Parameter name="check-username" value="liveuser" override="false"/> <Resource name="mail/Session" auth="Container" type="jakarta.mail.Session" mail.smtp.host="localhost"/> <!--- use type="javax.mail.Session" for brXM 15 and lower --> <!-- JNDI resource exposing database connection goes here --> </Context>

Note: RMI and its related parameters (repository-address and start-remote-server) are not supported as of version 17.0.0.

Session serialization and replication are not required or supported for the CMS web application, and are not needed by default for the site web application.

  1. Add a JNDI resource to conf/context.xml and a conf/repository.xml file configured for your database. For MySQL, see Configure Bloomreach Experience Manager for MySQL. For other databases, refer to Databases.

  2. Create a bin/setenv.sh file with the following content. Adjust values such as JAVA_HOME as needed:

JAVA_HOME=/usr/java/jdk-17.0.10 # or jdk-11.0.15, jdk-1.8 CATALINA_HOME="/usr/local/tomcat" CATALINA_BASE="/opt/cms/tomcat" CATALINA_PID="${CATALINA_BASE}/work/catalina.pid" CLUSTER_ID="$(whoami)-$(hostname -f)" MAX_HEAP=1024 MIN_HEAP=1024 REP_OPTS="-Drepo.bootstrap=false -Drepo.config=file:${CATALINA_BASE}/conf/repository.xml" JVM_OPTS="-server -Xmx${MAX_HEAP}m -Xms${MIN_HEAP}m -XX:+UseG1GC -Djava.util.Arrays.useLegacyMergeSort=true" DMP_OPTS="-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/cms/heapdumps" JRC_OPTS="-Dorg.apache.jackrabbit.core.cluster.node_id=${CLUSTER_ID}" L4J_OPTS="-Dlog4j.configurationFile=file://${CATALINA_BASE}/conf/log4j2.xml -DLog4jContextSelector=org.apache.logging.log4j.core.selector.BasicContextSelector" VGC_OPTS="-verbosegc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:${CATALINA_BASE}/logs/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=2048k" # following --add-opens for brXM 16 on Tomcat 10.1.20 to .24 only. # It's a workaround needed in combination with Freemarker back-end templating and may also be omitted in a SPA set-up ADD_OPENS_OPTS="--add-opens java.xml/com.sun.org.apache.xml.internal.utils=ALL-UNNAMED" CATALINA_OPTS="${JVM_OPTS} ${VGC_OPTS} ${REP_OPTS} ${DMP_OPTS} ${RMI_OPTS} ${L4J_OPTS} ${JRC_OPTS} ${ADD_OPENS_OPTS}" export JAVA_HOME CATALINA_HOME CATALINA_BASE
  1. Make bin/setenv.sh executable:
chmod +x bin/setenv.sh
  1. For older distributions: if conf/log4j2.xml is missing, download it from example log4j2.xml file.

    If you use this example, ensure that the web.xml of the CMS and site web applications contains an <env-entry> element of type java.lang.String, with name logging/contextName and value "cms" or "site" as appropriate.

  2. (Optional) To further configure the JSP servlet, add the following parameters to conf/web.xml:

<servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> <init-param> <param-name>fork</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>xpoweredBy</param-name> <param-value>false</param-value> </init-param> <!-- BEGIN extra init-params --> <init-param> <param-name>trimSpaces</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>development</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>checkInterval</param-name> <param-value>7200</param-value> </init-param> <init-param> <param-name>modificationTestInterval</param-name> <param-value>7200</param-value> </init-param> <init-param> <param-name>genStrAsCharArray</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>enablePooling</param-name> <param-value>false</param-value> </init-param> <!-- END extra init-params --> <load-on-startup>3</load-on-startup> </servlet>

Configure the Service

  1. Create an init script at /etc/init.d/cms. Adjust the variables at the start if you use a different user or home directory.

    The script name determines the user account. If you use a different user than cms, rename the script accordingly.

#!/bin/bash ### BEGIN INIT INFO # Provides: tomcat # Required-Start: $remote_fs $syslog # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start Tomcat at boot time # Description: Start Tomcat instance located at the user with the same name # as the script. Tomcat is started with the privileges of the user. ### END INIT INFO # Get basename and clean up start/stop symlink cruft (aka S20cms) appname=$(basename $0) appname=${appname##[KS][0-9][0-9]} appuser=${appname} apphome=/opt/${appuser}/tomcat config=${apphome}/bin/setenv.sh start_tomcat=${apphome}/bin/startup.sh stop_tomcat=${apphome}/bin/shutdown.sh CATALINA_PID="${apphome}/work/catalina.pid" if [[ -r ${config} ]]; then . ${config} else echo "Environment config missing: ${config}" exit 1 fi if [[ -n "${JAVA_HOME+x}" && -z ${JAVA_HOME} ]]; then echo "Please point JAVA_HOME in $(basename) to your SUN JRE of JDK" exit 1 fi export JAVA_HOME CATALINA_OPTS CATALINA_PID CATALINA_HOME CATALINA_BASE if [[ $(id -u) == 0 ]]; then SU="su - ${appuser} -c" elif [[ ${appuser} != $(/usr/bin/id -un) ]]; then echo "Access denied: You are neither a superuser nor the ${appuser} user" exit 1 fi test -r ${CATALINA_PID} && PID=$(cat ${CATALINA_PID}) cleanup() { /usr/bin/find ${apphome}/work/ ${apphome}/temp/ -maxdepth 1 -mindepth 1 -print0 | xargs -0 rm -rf } start() { echo -n "Starting ${appname}: " cd ${apphome} if [[ -n ${PID} ]]; then if ps -eo pid | grep -wq ${PID}; then echo "${appname} (${PID}) still running.." exit 1 else echo "(removed stale pid file ${CATALINA_PID}) " rm -f ${CATALINA_PID} fi fi cleanup ${SU} ${start_tomcat} > /dev/null if [[ $? ]]; then echo "${appname} started." else echo "${appname} failed to start." fi } stop() { echo -n "Shutting down ${appname}: " cd ${apphome} ${SU} ${stop_tomcat} > /dev/null if [[ -n ${PID} ]]; then echo "waiting for ${appname} to stop" for ((i=0;i<25;i++)); do RUNNING=$(ps -eo pid | grep -w ${PID}) if [[ ${i} == 24 ]]; then kill ${PID} > /dev/null 2>&1 && sleep 5s && \ kill -3 ${PID} > /dev/null 2>&1 && \ kill -9 ${PID} > /dev/null 2>&1 elif [[ ${RUNNING// /} == ${PID} ]]; then echo -n "." sleep 1s else break fi done fi test -e ${CATALINA_PID} && rm -f ${CATALINA_PID} echo "${appname} stopped." } case "${1}" in start) start ;; stop) stop ;; restart) if [[ -n ${PID} ]] && ps -eo pid | grep -wq ${PID}; then kill -3 ${PID} fi stop sleep 2s start ;; *) echo "Usage: ${0} {start|stop|restart}" ;; esac exit 0
  1. Register the service:
# Debian/Ubuntu update-rc.d cms defaults # Redhat chkconfig --add cms

Note: On Red Hat Enterprise Linux 7.2 and later, which use systemd, you can still use the init script as described in /etc/init.d/README:

Note that traditional init scripts continue to function on a systemd
system. An init script /etc/rc.d/init.d/foobar is implicitly mapped
into a service unit foobar.service during system initialization.

Next Steps

Share Feedback
Page: /deploy/enterprise-installation/linux-installation-manual
Section: Deploy
Category *