Configure the Application Server (Apache Tomcat on Linux)
Introduction
Goal
Configure an application server using the Bloomreach Experience Manager standard stack, running Apache Tomcat on a Linux operating system.
Background
This page describes how to set up a Bloomreach Experience Manager application server according to our best practices. The end result is the most commonly used setup based on the Developer Edition Stack and the brXM Standard Stack.
Assumptions
- Oracle or OpenJDK Java 8 JRE (or JDK) already installed under: /usr/java/jre1.8.0
 - Relational database server (MySQL or other supported database server) already installed
 - Bloomreach Experience Manager will run under its own dedicated user called cms, with home-directory: /opt/cms
 - Base Apache Tomcat installation under: /usr/local
 - 
 
Tomcat common libs under: /usr/local/share/tomcat-common/lib
 
Preparation
- 
 
Create a user account so you can run Bloomreach Experience Manager under it's own user.
useradd -m -d /opt/cms cms
 
Install and Configure Apache Tomcat
Install Apache Tomcat (Catalina Home)
- Download the latest release of Apache Tomcat 8.5 or 9.0 and unpack it.
 
cd /usr/local tar -xzvf apache-tomcat-8.<LATEST>.tar.gz
- Create a symlink to it:
 
ln -s apache-tomcat-8.<LATEST> tomcat
Install Common Libraries
- Create the tomcat-common directory for the common classloader and place the required jars there:
 
mkdir -p /usr/local/share/tomcat-common/lib cd /usr/local/share/tomcat-common/lib 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 wget http://search.maven.org/remotecontent?filepath=javax/mail/mail/1.4.7/mail-1.4.7.jar -O mail-1.4.7.jar wget http://search.maven.org/remotecontent?filepath=javax/jcr/jcr/2.0/jcr-2.0.jar -O jcr-2.0.jar wget http://search.maven.org/remotecontent?filepath=com/mysql/mysql-connector-j/8.0.32/mysql-connector-j-8.0.32.jar -O mysql-connector-j-8.0.32.jar
Configure Apache Tomcat (Catalina Base)
Prepare Catalina Base
- 
 
Create the necessary directories for the Catalina Base and copy the configuration files that will be modified later:
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
 
Configure the Tomcat Instance
- 
 
To allow the CMS application to open the RMI port, edit conf/catalina.policy and add the following to the end:
grant codeBase "jar:file:${catalina.home}/webapps/" { permission java.net.SocketPermission "*:1099", "connect, accept, listen"; }; 
- 
 
Edit conf/catalina.properties and add the shared/lib path to the shared classloader:
shared.loader="${catalina.base}/shared/lib","${catalina.base}/shared/lib/*.jar" 
- 
 
Edit conf/catalina.properties and 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
- 
 
Add a conf/context.xml with at least the following (and adjust it to match 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="javax.mail.Session" mail.smtp.host="localhost"/> <!-- JNDI resource exposing database connection goes here --> </Context>Session serialization and session replication for the CMS platform web application is neither required nor supported by BloomReach and for the site web application by default not needed.
 - 
 
At this point you also need to add a JNDI resource to conf/context.xml, and add a conf/repository.xml configuration file adjusted for your database. If using MySQL you should be able to use the examples provided at Configure Bloomreach Experience Manager for MySQL as-is. See Databases for other database servers.
 - 
 
Add a file bin/setenv.sh with the following contents (adjust where necessary):
JAVA_HOME=/usr/java/jre1.8.0 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" RMI_OPTS="-Djava.rmi.server.hostname=127.0.0.1" 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" CATALINA_OPTS="${JVM_OPTS} ${VGC_OPTS} ${REP_OPTS} ${DMP_OPTS} ${RMI_OPTS} ${L4J_OPTS} ${JRC_OPTS}" export JAVA_HOME CATALINA_HOME CATALINA_BASE - Make the file bin/setenv.sh executable:
 
chmod +x bin/setenv.sh
- Add a conf/log4j2.xml 
 
Download Example log4j2-dist.xml file.
If you use the log4j2.xml example above, make sure to add to the end of the web.xml of the cms webapp the following part:
<env-entry> <env-entry-name>logging/contextName</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>cms</env-entry-value> </env-entry>
and add to the end of the web.xml of the site webapp the following part:
<env-entry> <env-entry-name>logging/contextName</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>site</env-entry-value> </env-entry>
 - (Optional) Modify the JSP servlet configuration in conf/web.xml by adding some extra parameters.
 
        <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
- 
 
Create an init script: /etc/init.d/cms, adjust the variables at the start if a different user or home directory is used.
 
#!/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
#
# Copyright BloomReach 2017
#
# 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
- 
 
Install the service
 
# Debian/Ubuntu update-rc.d cms defaults # Redhat chkconfig --add cms
Please note that while Red Hat Enterprise Linux 7.2 uses systemd, it is still possible to use the above init script as stated in the RHEL 7.2 /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
- Configure Apache HTTP Server as Reverse Proxy for Bloomreach Experience Manager
 - Deploy a Project Distribution
 - Configure the Application After First Deployment