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
- Java already installed:
- For Bloomreach Experience Manager 16:
Oracle or OpenJDK Java 17 JDK installed, e.g. /usr/java/jdk-17.0.10 * - For Bloomreach Experience Manager 15:
Oracle or OpenJDK Java 11 JDK installed, e.g. /usr/java/jdk-11.0.22 * - For Bloomreach Experience Manager 14:
Oracle or OpenJDK Java 8 JDK installed, e.g. /usr/java/jdk-1.8 *
- For Bloomreach Experience Manager 16:
- Relational database server (MySQL or other supported database server) already installed
Intended Results
The instructions on this page aim for the following results:
- 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 *
* These are the locations we recommend. It is possible to use different locations depending on your environment setup.
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)
- Dowload the latest applicable release from Apache Tomcat: version 10.1 for brXM 16 or 9.0 (or 8.5) for brXM 15/14, then unpack it.
cd /usr/local tar -xzvf apache-tomcat-<LATEST>.tar.gz
- Create a symlink to it:
ln -s apache-tomcat-<LATEST> tomcat
- Correct some 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
- Create the tomcat-common/lib directory for the common classloader and place the required database connector jar there:
mkdir -p /usr/local/share/tomcat-common/lib cd /usr/local/share/tomcat-common/lib # e.g. 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
- Legacy, before brXM 16: if you do not have the following jars in the distribution's common/lib directory, also place these in tomcat-common/lib
# 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)
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
-
If you do not want your distribution's context.xml to be leading, remove it from there and add a conf/context.xml with at least the following (and adjust it to match your environment where necessary):
<?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>
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, e.g. JAVA_HOME):
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
- Make the file bin/setenv.sh executable:
chmod +x bin/setenv.sh
- Legacy: if you don't have conf/log4j2.xml in your packaged distribution file, add it manually: download example log4j2.xml file.
If you use the log4j2.xml example above, make sure that the web.xml of the cms and site webapps contain an <env-entry> element of type java.lang.String, with name logging/contextName and value "cms" and "site", respectively.
- (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 # 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