AlfClusterIns

From AlfrescoWiki

Jump to: navigation, search

NOTE: THIS IS A WORK IN PROGRESS!! I'm in the process of creating it....

Contents

[edit] Purpose

Provide detailed instructions for installing Alfresco CE Cluster that services internal and external network users. The instructions will cover:

  • Alfresco CE Cluster Installation Providing
    • CIFS (internal)
    • FTP (internal)
    • Secure WebDAV (internal/external)
    • Secure HTTPS (internal/external)
    • NTLM User Authentication for Single Sign On (SSO) (internal)
    • JAAS User Authentication (external)
    • LDAP User Imports from Micrsoft AD
    • Multilingual Menus
  • Unique/Individual Site Branding with single Alfresco CE Cluster
  • Nag Removal
  • Keepalived for High Availability (HA) and Load Balancing (LB)
  • Secure Apache HTTPD/mod_jk Tomcat installation and configuration
  • Redhat ES5 Linux installation and configuration specific to Alfresco
  • Oracle 10g DB example

[edit] Assumptions/Prerequisites

  • A populated Microsoft Active Directory (AD) infrastructure
  • A Oracle 10g DB Instance
  • An Network File System (NFS) Server to house the Alfresco Repository
  • Installer/Administrator has root/administrator privileges for all relevant systems
  • A secure network with firewall policies
  • Installer/Administrator is versed in installing RedHat operating systems and managing rpms
  • Adequate NFS storage needs for your estimated Alfresco repository volume
  • Installation will begin with a SINGLE non-HA/LB Alfresco CE instance. The cluster will be built up around it.

[edit] Create Oracle DB User

Before installing Alfresco you need a DB instance for Alfresco to connect to and populate. As the sql administrator create the ALFRESCO user identified by the password of your choosing. Change tablespace locations as neccessary. These are the minimum grants neccessary.

sql>

CREATE USER "ALFRESCO"  PROFILE "DEFAULT" IDENTIFIED BY "<!PASSWORD!>" DEFAULT TABLESPACE "ALFRESCO_DATA" TEMPORARY TABLESPACE "TEMP" QUOTA UNLIMITED ON "ALFRESCO_DATA" ACCOUNT UNLOCK;
GRANT CONNECT TO "ALFRESCO";
GRANT CREATE ANY INDEX TO "ALFRESCO";
GRANT CREATE ANY PROCEDURE TO "ALFRESCO";
GRANT CREATE ANY SEQUENCE TO "ALFRESCO";
GRANT CREATE ANY SYNONYM TO "ALFRESCO";
GRANT CREATE ANY TABLE TO "ALFRESCO";
GRANT CREATE ANY TRIGGER TO "ALFRESCO";
GRANT CREATE ANY VIEW TO "ALFRESCO";

[edit] Install Alfresco Server

Create a SINGLE working Alfresco instance and then later we'll create 3 clones changing just 4 files.

[edit] Install RedHat System

  • 1 RedHat ES Server (VMWARE OK).
    • At least 2 NIC
    • Enough disk for OS and Alfresco Installation
    • Optional disk RAID
    • /alf partition (Min. 400MB - Size as needed)
    • /alt/tmp partition (AT LEAST 4GB)

You don't need a lot of disk or fast disks for that matter. It is CPU/IO intensive. The operating install will be dependent on your needs and environment. That being said I highly suggest the following partition layout with the at LEAST the /alf and /alf/tmp required:

/dev/sda1                 2039       559      1377  29% /
/dev/sda2 - SWAP
/dev/sda3                 6115      1371      4745  23% /usr
/dev/sda5                  510       109       401  22% /tmp
/dev/sda6                 1020        57       963   6% /var
/dev/sda7                 2039        75      1965   4% /var/log
/dev/sda8                  510        17       494   4% /home
/dev/sda9                 4077       373      3704  10% /alf
/dev/sdb1                11900        32     11254   1% /alf/tmp

The /alf/tmp partition has EXT2 file system. It happens to be on a dedicated drive in our configuration. This tmp area is for the OpenOffice/Alfresco document conversions and the repository index rebuilds. In our experience 4GB was the MINIMUM for the temp storage area. As it is temporary we formatted with the EXT2 filesystem to eek some performance.

[edit] Configure Networking

In preparing for HA/LB with the LVS we will assign TWO network addresses. One will be the servers REAL address while the other address will be for the LVS service. LVS DIRECT requires NO-ARP for the virtual address. Unfortunately you can't have a single NIC for both the ARPing real address and an NON-ARPing alias address. The NIC is either all ARP or NO-ARP. Hence the need for a physical second NIC.

Configure the Real IP Address on ETH0. Season to taste.

DEVICE=eth0
BOOTPROTO=static
BROADCAST=X.X.X.255
IPADDR=X.X.X.X
NETMASK=255.255.255.0
NETWORK=X.X.X.0
ONBOOT=yes
NOZEROCONF=yes
USERCTL=no

Configure the Virtual IP Address on ETH1. Season to taste. Note: The most important setting is ARP=no !! This will be the 'shared' address for all the Alfresco clustered servers.


DEVICE=eth1
ARP=no
KEEPALIVE=yes
BOOTPROTO=static
IPADDR=X.X.X.X
NETMASK=255.255.255.0
NETWORK=X.X.X.0
TYPE=Ethernet
NOZEROCONF=yes
USERCTL=no
ONBOOT=yes

[edit] Install Required RPMs with YUM, Current JAVA & OpenOffice

In our organization we used RedHat ES5 Update0 i386. We also removed all extra and unneccessary RPM packages.

  • Alfresco depends on ImageMagick
  • JAAS Authentication depends on Kerberos

Since there are so many dependencies with installing ImageMagick, install yum and let it do the work. (87+packages..)

[edit] Install YUM

rpm -i yum-3.0.1-5.el5.noarch.rpm python-elementtree-1.2.6-5.i386.rpm python-sqlite-1.1.7-1.2.1.i386.rpm rpm-python-4.4.2-37.el5.i386.rpm yum-metadata-parser-1.0-8.fc6.i386.rpm python-urlgrabber-3.1.0-2.noarch.rpm expat-1.95.8-8.2.1.i386.rpm m2crypto-0.16-6.el5.1.i386.rpm

[edit] Create Local Repository of RedHat ES5 RPMs

Install createrepo:

rpm -i createrepo-0.4.4-2.fc6.noarch.rpm

You'll have to expand the RPMs from the RedHat CDs/DVD and have them either on the NFS server and then reference/mount the collection to create a local repo.

createrepo /mnt/es5/U0/Expanded

Create a local.repo YUM config file

vi /etc/yum.repos.d/local.repo

Insert the local.repo config settings:

[localrepo]
name=Fedora Core $releasever - My Local Repo
baseurl=file:///mnt/es5/U0/Expanded
enabled=1
gpgcheck=0
#gpgkey=file:///path/to/you/RPM-GPG-KEY

[edit] Install Kerberos

For JAAS Authentication install the Kerberos libraries. This is the minimum. If you want to test the config you'll need kinit which is in another krb rpm package.

rpm -i krb5-libs-1.5-17

[edit] Configure /etc/krb5.conf

Configure Kerberos for your environment. Here is an EXAMPLE config that we utilize.

  • [!YOUR_AD_DOMAIN!] e.g. AD.COMPANY.COM or just COMPANY.COM It will depend on your organization. Also USE UPPER CASE for this setting.
  • [!MICROSOFT_KRB_SERVER!] Your AD server address name. The server name is likely to be used in all subsequent settings for LDAP, NTLM and Kerberos.
  • In [domain_realm] include any matching domains that AD would be authoritative.
[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 default_realm = [!YOUR_AD_DOMAIN!]
 dns_lookup_realm = true
 dns_lookup_kdc = true
 ticket_lifetime = 24h

[realms]
 [!YOUR_AD_DOMAIN!] = {
  kdc = [!MICROSOFT_KRB_SERVER!]:88
  admin_server = [!MICROSOFT_KRB_SERVER!]:749
  default_domain = [!YOUR_AD_DOMAIN!]
 }

[domain_realm]
 .ad.company.com = [!YOUR_AD_DOMAIN!]
 ad.company.com = [!YOUR_AD_DOMAIN!]
 .company.com = [!YOUR_AD_DOMAIN!]
 company.com = [!YOUR_AD_DOMAIN!]

[kdc]
 profile = /var/kerberos/krb5kdc/kdc.conf

[appdefaults]
 pam = {
   debug = false
   ticket_lifetime = 36000
   renew_lifetime = 36000
   forwardable = true
   krb4_convert = false
 }

[edit] Test Kerberos

If you need to test the Kerberos authentication configuration install the appropriate krb rpm package.

rpm -i krb5-workstation-1.5-17.i386.rpm

Then run kinit and supply just the username. If the config is correct the 'domain' will be appended. If the config is bad or mis-configured there will be errors reported.

kinit AD_USERNAME
Password for AD_USERNAME@COMPANY.COM: 

If the config is good and the password was correct a kerberos certificate should be listed. Run klist.

klist

You should see:

Ticket cache: FILE:/tmp/krb5cc_0
Default principal: AD_USERNAME@COMPANY.COM

Valid starting     Expires            Service principal
08/31/07 17:40:25  09/01/07 03:40:29  krbtgt/COMPANY.COM@COMPANY.COM
        renew until 09/01/07 17:40:25


Kerberos 4 ticket cache: /tmp/tkt0
klist: You have no tickets cached


[edit] Install ImageMagick

yum install ImageMagick

[edit] Alfresco/ImageMagick Quirk

Alfresco seems to make calls to imconvert. So, make a symbolic link.

ln -s /usr/bin/convert /usr/bin/imconvert

[edit] Install Java6.0u2

Get The JAVA 6.0u2 JDK from [Sun]. Agree to license and install the RPM.

rpm -i jdk-6u2-linux-i586.rpm

[edit] Remove/Re-install RedHat ALTERNATIVE Java

alternatives --remove java
alternatives --install /usr/bin/java java /usr/java/default/jre/bin/java 1

[edit] Create java.sh Profile

vi /etc/profile.d/java.sh
export PATH=$PATH:/usr/java/default/jre/bin:/usr/java/default/bin
export JAVA_HOME=/usr/java/default
export JRE_HOME=/usr/java/default/jre
export CLASSPATH=$CLASSPATH:/usr/java/default/lib/ojdbc14.jar

[edit] Source it..

. /etc/profile.d/java.sh

[edit] Install Oracle OJDBC.JAR

Alfresco with OracleDB depends on the Java JAR file ojdbc14.jar. You can get it from [Oracle] Copy/Move the jar file into /usr/java/default/lib/ then link it to /usr/java/default/jre/lib/

ln -s /usr/java/default/lib/ojdbc14.jar /usr/java/default/jre/lib/ojdbc14.jar

[edit] Install Xvfb for OpenOffice

OpenOffice will operate in a headless mode but needs a framebuffer to connect to. So install Xvfb. Xvfb will start BEFORE OpenOffice will start.

yum install Xvfb

[edit] Install LATEST OpenOffice

Get the current OpenOffice Linux suite [OOo_2.2.1_LinuxIntel_install_en-US.tar.gz]

You'll be running in OpenOffice in a HEADLESS configuration. But for the initial configuration/registration you'll need to have an available X session.

cd /usr/local/src

tar xvfzp OOo_2.2.1_LinuxIntel_install_en-US.tar.gz.tar.gz

cd OOF680_m18_native_packed-1_en-US.9161/RPMS

rm openoffice.org-gnome-integration-2.2.1-9161.i586.rpm openoffice.org-kde-integration-2.2.1-9161.i586.rpm

rpm -Uvih *.rpm

export DISPLAY=<!SOMEHOST!>:0.0

/opt/openoffice.org2.2/program/soffice.bin

[edit] Set Soffice Temp Space

Go thru the registration and set the TEMPORARY file location to the big '/alf/tmp partition. In the OO GUI go to Tools:Options:Paths:Temporary Files and change /tmp to '/alf/tmp/soffice'. That's it. You can close it up.

[edit] Create SOFFICE INIT.D Script

This script will start Xvfb and wait for it prior to starting soffice in headless mode.

vi /etc/init.d/soffice
#!/bin/sh
#
# soffice
#
# chkconfig: 345 98 11
# description: Starts and stops the soffice non-interactive document transformation

# Source function library.
. /etc/rc.d/init.d/functions

XVFB=/usr/bin/Xvfb
SOFFICE=/opt/openoffice.org2.2/program/soffice.bin
SOFFICE_TMP=/alf/tmp/soffice
KILLER=/usr/bin/killall

case "$1" in
  start)
    #
    # Start Soffice
    #

    echo -n "Starting Xvfb for SOFFICE: "
    $XVFB :1 -screen 0 800x600x16 -fbdir /tmp  > /dev/null 2>&1 &
    sleep 3s
    echo_success
    echo

    echo -n "Clearing SOFFICE ($SOFFICE_TMP): "
    rm -rf $SOFFICE_TMP/*
    echo_success
    echo

    echo -n "Starting SOFFICE: "
    $SOFFICE -invisible -accept="socket,host=localhost,port=8100;urp;" -display :1 > /dev/null 2>&1 &

    echo_success

    echo
    ;;

  startnow)
    #
    # Start Soffice
    #

    echo -n "Starting Xvfb for SOFFICE: "
    $XVFB :1 -screen 0 800x600x16 -fbdir /tmp  > /dev/null 2>&1 &
    echo_success
    echo

    echo -n "Starting SOFFICE: "
    $SOFFICE -invisible -accept="socket,host=localhost,port=8100;urp;" -display :1  > /dev/null 2>&1 &
    echo_success

    echo
    ;;

  stop)
    #
    # Stop Soffice
    #
    echo -n "Stopping SOFFICE: "
    $KILLER -q -s TERM soffice.bin
    echo_success
    echo

    echo -n "Stopping Xvfb for SOFFICE: "
    $KILLER -s TERM Xvfb
    echo_success
    echo

    ;;

  *)
    echo "Usage: $0 {start|startnow|stop}"
    exit 1;;
esac

exit 0

Add the init script and disable it. Due to system dependencies if must be started in rc.local

chkconfig --add soffice
chkconfig soffice off

[edit] Install Apache TOMCAT

Java must be installed and functional before installing Apache Tomcat!

We prefer Apache Tomcat from [source] rather than RPMs. If you use RPMs then you'll need to accomodate for path differences. We used apache-tomcat-6.0.13

cd /usr/local
tar xvfzp <!PATH!>/apache-tomcat-6.0.13.tar.gz
mv apache-tomcat-6.0.13 tomcat

Add/Create the TOMCAT user and group. Season to taste.

echo "tomcat:x:52:52:Tomcat:/usr/local/tomcat:/sbin/nologin" >> /etc/passwd
echo "tomcat:x:52:" >> /etc/group

Unpack the service scripts that help manage tomcat more easily

cd /usr/local/tomcat/bin
tar xfvzp jsvc.tar.gz
cd jsvc-src
sh ./configure
make

Change ownership and put logs in /var/log.

chown -R tomcat.tomcat /usr/local/tomcat
rm -rf /usr/local/tomcat/logs
mkdir /var/log/tomcat
chown -R tomcat.tomcat /var/log/tomcat
ln -s /var/log/tomcat /usr/local/tomcat/logs

[edit] Create New Tomcat TEMP

If it doesn't exist create it.

mkdir /alf/tmp/tomcat

Change ownership on /alf/tmp to tomcat user.

chown -R tomcat.tomcat /alf/tmp

[edit] Edit /usr/local/tomcat/conf/server.xml

  • [!REAL_SERVER_IP_ADDRESS!] The IP address assigned to eth0.
  • [!YOUR_WEB_FQDN!] Set to the Fully Qualified Domain Name for accessing the Alfresco web server. e.g. www.alfresco.company.com
  • Uncomment the 8080 HTTP Connector. This will enable you to connect via the browser for this single server setup. After the setup is complete and the server has been cloned and placed into the cluster you should re-comment out this section for security.
  • The <Alias> directive will be used later for custom site branding.

Replace the contents with this minimal set:

<Server debug="0" port="8005" shutdown="SHUTDOWN">

    <Service name="Alfresco-Service">

       <!--
       <Connector port="8080" protocol="HTTP/1.1" 
               connectionTimeout="20000" 
               redirectPort="8443" />
       -->

        <Connector protocol="AJP/1.3" address="[!REAL_SERVER_IP_ADDRESS!]"
               port="8009" minProcessors="15" maxProcessors="200"
               enableLookups="false" redirectPort="8443" emptySessionPath="true"
               acceptCount="10" debug="0" connectionTimeout="60000"
               useURIValidationHack="false" URIEncoding="UTF-8"/>

        <Engine jvmRoute="fresco1" debug="0"  name="Alfresco-Engine">

        <Realm className="org.apache.catalina.realm.MemoryRealm" />

         <!-- ============================================================
              Alfresco
              ============================================================ -->

        <Host name="[!YOUR_FQDN_WEB!]" appBase="/alf/alfy"
         debug="0" autoDeploy="false" unpackWARs="false">

	    <!-- ALIAS for additonal site names for custom branding e.g., --> 
            <!-- <Alias>alfresco.company1.com</Alias> -->
            <!-- <Alias>alfresco.company2.com</Alias> -->
            <!-- <Alias>alfresco.company3.com</Alias> -->

            <Context docBase="" path=""/>

            <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve" directory="logs"
               prefix="alfresco_access." suffix=".log" pattern="common" resolveHosts="false"/>

        </Host>

        </Engine>

      </Service>

</Server>

[edit] Create TOMCAT INIT.D Script

NOTE: java.io.tmpdir is changed to /alf/tmp/tomcat

vi /etc/init.d/tomcat
#!/bin/sh
#
# tomcat        Utilizing the jsvc start and stop the servlet engine.
#
# chkconfig: 345 99 10
# description: Starts and stops the tomcat servlet engine.

# Source function library.
. /etc/rc.d/init.d/functions

##############################################################################
#
#   Copyright 2004 The Apache Software Foundation.
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
##############################################################################
#
# Small shell script to show how to start/stop Tomcat using jsvc
# If you want to have Tomcat running on port 80 please modify the server.xml
# file:
#
#    <!-- Define a non-SSL HTTP/1.1 Connector on port 80 -->
#    <Connector className="org.apache.catalina.connector.http.HttpConnector"
#               port="80" minProcessors="5" maxProcessors="75"
#               enableLookups="true" redirectPort="8443"
#               acceptCount="10" debug="0" connectionTimeout="60000"/>
#
# That is for Tomcat-5.0.x (Apache Tomcat/5.0)
#
# Adapt the following lines to your configuration

JAVA_HOME=/usr/java/default/jre
CATALINA_HOME=/usr/local/tomcat
DAEMON_HOME=/usr/local/tomcat/bin/jsvc-src
TOMCAT_USER=tomcat
TOMCAT_GROUP=tomcat
TOMCAT_PID=/var/run/jsvc.pid
TMP_DIR=/alf/tmp/tomcat
CATALINA_OPTS=
CLASSPATH=$JAVA_HOME/lib/ojdbc14.jar:$JAVA_HOME/lib/tools.jar:$CATALINA_HOME/bin/commons-daemon.jar:$CATALINA_HOME/bin/bootstrap.jar
CATALINA_WORK_DIR=$CATALINA_HOME/work

# To get a verbose JVM
#-verbose \
# To get a debug of jsvc.
#-debug \

WAIT_MAX=2

case "$1" in
  start)
    #
    # Start Tomcat
    #

    # Wait for SOFFICE 2 times...
    WAIT=1
    while [ $WAIT -le $WAIT_MAX ];
    do
      echo -n "Waiting for SOFFICE ($WAIT/$WAIT_MAX 1min):"
      SOFFICE=`netstat -an | grep 8100`
      if [ -n "$SOFFICE" ]; then
        echo_success
        TOMCAT_CONTINUE=TRUE
        let WAIT=$WAIT_MAX+1
      else
        echo_failure
        TOMCAT_CONTINUE=FALSE
        let WAIT+=1
        sleep 1m
      fi
      echo
    done

    echo -n "Starting Tomcat: "
    if [ $TOMCAT_CONTINUE = 'FALSE' ]; then
      echo_failure
      echo
      exit 1
    fi

    # Tuned for low pause times and high throughput i-cms
    # For monitoring...
    #-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:-TraceClassUnloading \
    #-Xms2304m -Xmx2304m -Xmn768m -Xss2048k -XX:+AggressiveOpts\
    #-Xmx1024m -Xms1024m -XX:+UseLargePages -XX:LargePageSizeInBytes=1m \
    #-Xmx1536m \
    $DAEMON_HOME/jsvc \
    -user $TOMCAT_USER \
    -home $JAVA_HOME \
    -jvm server \
    -Xmx1536m \
    -Dcatalina.home=$CATALINA_HOME \
    -Djava.io.tmpdir=$TMP_DIR \
    -outfile $CATALINA_HOME/logs/catalina.out \
    -errfile '&1' \
    $CATALINA_OPTS \
    -cp $CLASSPATH \
    org.apache.catalina.startup.Bootstrap

    sleep 2s
    if [ -f $TOMCAT_PID ]; then
                echo_success
    else
                echo_failure
    fi

    echo
    ;;

  stop)
    #
    # Stop Tomcat
    #
    echo -n "Stopping Tomcat: "

    PID=`cat $TOMCAT_PID`
    kill $PID 2>/dev/null 1>/dev/null

    if [ `ps -p ${PID} > /dev/null 2>&1` ]; then
                echo_failure
    else
                echo_success
                rm -f $TOMCAT_PID > /dev/null 2>&1
    fi

    echo
    ;;

  flush)
    #
    # Stop Tomcat
    #
    echo -n "Stopping Tomcat: "

    PID=`cat $TOMCAT_PID`
    kill $PID 2>/dev/null 1>/dev/null

    if [ `ps -p ${PID} > /dev/null 2>&1` ]; then
                echo_failure
    else
                echo_success
                rm -f $TOMCAT_PID > /dev/null 2>&1
    fi

    echo

    echo -n "Flushing Tomcat CACHE ($CATALINA_WORK_DIR) : "
    rm -rf $CATALINA_WORK_DIR
    if [ -e $CATALINA_WORK_DIR ]; then
                echo_failure
                echo
                exit 1;
    fi

    mkdir $CATALINA_WORK_DIR
    chown $TOMCAT_USER.$TOMCAT_GROUP $CATALINA_WORK_DIR
    if [ ! -e $CATALINA_WORK_DIR ]; then
                echo_failure
                echo
                exit 1;
    fi
    echo_success
    echo

    ;;

  *)
    echo "Usage: $0 {start|stop|flush}"
    exit 1;;
esac

exit 0

Add the init script and disable it. Due to system dependencies if must be started in rc.local

chkconfig --add tomcat
chkconfig tomcat off

[edit] Create Services

Because of an soffice XVfb dependency wait until all other rc levels are run. Insert the following into /etc/init.d/rc.local

service soffice start
service tomcat start

[edit] Create IPTables Local NAT for Alfresco CIFS, FTP

Running Alfresco/Tomcat as the tomcat user prevents binding to ports lower than 1024. In anticipation of this local NAT rules with iptables allows connections to the standard CIFS and FTP ports while running Alfresco/Tomcat with a non-root user. I'm assuming you have iptables installed. If not you know the drill. CAUTION: This clears an existing iptables setting. If you already have an iptables policy SAVE IT and add these NAT rules to it and reload.

Clear iptables

iptables -F
iptables -X

The NAT rules for high to low routing:

iptables -t nat -A PREROUTING -p tcp --dport 445 -i eth0 -j REDIRECT --to-port 2445
iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 445 -j REDIRECT --to-port 2445
iptables -t nat -A PREROUTING -p tcp --dport 139 -i eth0 -j REDIRECT --to-port 2139
iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 139 -j REDIRECT --to-port 2139
iptables -t nat -A PREROUTING -p udp --dport 137 -i eth0 -j REDIRECT --to-port 2137
iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 137 -j REDIRECT --to-port 2137
iptables -t nat -A PREROUTING -p udp --dport 138 -i eth0 -j REDIRECT --to-port 2138
iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 138 -j REDIRECT --to-port 2138
iptables -t nat -A PREROUTING -p udp --dport 21 -i eth0 -j REDIRECT --to-port 2021
iptables -t nat -A OUTPUT -p udp -d 127.0.0.1 --dport 21 -j REDIRECT --to-port 2021
iptables -t nat -A PREROUTING -p tcp --dport 21 -i eth0 -j REDIRECT --to-port 2021
iptables -t nat -A OUTPUT -p tcp -d 127.0.0.1 --dport 21 -j REDIRECT --to-port 2021

To save state between restarts:

service iptables save

[edit] Install Alfresco CE 2.1

We will not be using the Alfresco user database for authentication. Instead we will be importing users via LDAP from the Microsoft AD and relying on NTLM passthru and JAAS/Kerberos authentication.

Get the community source WAR file [alfresco-community-war-2.1.0.tar.gz]

Create the working directory /alf/alfy and unzip the alfresco.war file.

cd /alf
tar xvfzp alfresco-community-war-2.1.0R1.tar.gz

mkdir alfy
cd alfy

unzip ../alfresco.war

chown -R tomcat.tomcat /alf/*

Create the lucene indexes directory. This will NOT be shared in the cluster. The lucene indexes must exist locally for each cluster member server.

mkdir /alf/alf_data

Create the repository directory. This will later be moved to the NFS server and automounted amongst the Alfresco cluster member servers.

mkdir /alf/alf_data_cluster

[edit] Add Language Extensions

To enable the multi-lingual menus in Alfresco web copy the translation extensions. This is optional but a very nice one.

cp -ax /alf/extensions/messages/* /alf/alfy/WEB-INF/classes/alfresco/messages

[edit] Alfresco CONFIG Changes

These are important! Most examples are reports from running diff -u. Custom configurations are created in extensions sub-directory.

[edit] /alf/alfy/WEB-INF/classes/log4j.properties

Redirect logging to a better location.

log4j.appender.File.File=/var/log/tomcat/alfresco.log

[edit] /alf/alfy/WEB-INF/classes/alfresco/domain/hibernate-cfg.properties

Accomodate for using Oracle DB.

@@ -1,7 +1,7 @@
 #
 # Hibernate configuration
 #
-hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
+hibernate.dialect=org.hibernate.dialect.Oracle9Dialect
 
 hibernate.jdbc.use_streams_for_binary=true
 hibernate.show_sql=false

[edit] /alf/alfy/WEB-INF/classes/alfresco/repository.properties

DON'T change user.name.caseSensitive=false.

Reflect the repository directory changes.

@@ -1,11 +1,10 @@
 # Directory configuration
 
-dir.root=./alf_data
+dir.root=/alf/alf_data
 
-dir.contentstore=${dir.root}/contentstore
-dir.contentstore.deleted=${dir.root}/contentstore.deleted
-
-dir.auditcontentstore=${dir.root}/audit.contentstore
+dir.contentstore=/alf/alf_data_cluster
+dir.contentstore.deleted=${dir.contentstore}/contentstore.deleted
+dir.auditcontentstore=${dir.contentstore}/audit.contentstore
 
 # The location for lucene index files
 dir.indexes=${dir.root}/lucene-indexes

Change the Index Recovery to AUTO. Likely need set to FULL on first startup.

@@ -14,7 +13,8 @@
 dir.indexes.lock=${dir.indexes}/locks
 
 # The index recovery mode (NONE, VALIDATE, AUTO, FULL)
-index.recovery.mode=VALIDATE
+index.recovery.mode=AUTO
+#index.recovery.mode=FULL
 
 # Change the failure behaviour of the configuration checker
 system.bootstrap.config_check.strict=true

Reflect the Oracle DB settings and email host settings.

@@ -70,16 +70,17 @@
 
 # Database configuration
 db.schema.update=true
-db.driver=org.gjt.mm.mysql.Driver
+db.driver=oracle.jdbc.OracleDriver
 db.name=alfresco
-db.url=jdbc:mysql:///${db.name}
-db.username=alfresco
+db.url=jdbc:oracle:thin:@<!ORACLE_DB_SERVER!>:1521:<!YOUR_DB_INSTANCE!>
+db.username=ALFRESCO
 db.password=<!ORACLE_ALFRESCO_USER_PASSWORD!>
 db.pool.initial=10
 db.pool.max=20
 
 # Email configuration
-mail.host=
+mail.host=<!YOUR_MAIL_HOST!>
 mail.port=25
 mail.username=anonymous
 mail.password=

Reflect a valid default FROM email address.

@@ -87,7 +88,7 @@
 mail.encoding=UTF-8
 # Set this value to 7bit or similar for Asian encoding of email headers as required
 mail.header=
-mail.from.default=alfresco@alfresco.org
+mail.from.default=<!YOUR_ADMIN_EMAIL_ADDRESS!>
 
 # System Configuration
 system.store=system://system

[edit] /alf/alfy/WEB-INF/classes/alfresco/scheduled-jobs-context.xml

Due to a quirk in Alfresco 2.1 CE and LDAP imports with the Quartz schedueler change the autoStartup to true.

@@ -18,7 +18,7 @@
         </property>
         <!-- Do not auto start the scheduler - this is done at the end of the bootstrap process -->
         <property name="autoStartup">
-            <value>false</value>
+            <value>true</value>
         </property>
     </bean>

[edit] /alf/alfy/WEB-INF/classes/alfresco/authentication-services-context.xml

Disable GuestLogins

@@ -155,7 +155,7 @@
             <ref bean="authenticationManager" />
         </property>
         <property name="allowGuestLogin">
-            <value>true</value>
+            <value>false</value>
         </property>
     </bean>

Disallow creating missing people. We get them form the LDAP import. The default settings for handling duplicate usernames was sufficient for our needs. Unless you know what you are doing with them I'd leave the settings for proccessing duplicates alone.

@@ -216,7 +216,8 @@
         <!--     ${server.transaction.allow-writes}                   -->
         <!--     false                                                -->
         <property name="createMissingPeople">
-           <value>${server.transaction.allow-writes}</value>
+           <!--<value>${server.transaction.allow-writes}</value>-->
+           <value>false</value>
         </property>
         <property name="userNamesAreCaseSensitive">
           <value>${user.name.caseSensitive}</value>

Added a Default Home Folder to match Personal Home Folders under User Homes. Change the location of Personal Home Folders to reside in /Company Home/User Homes.

@@ -290,12 +291,18 @@
         </property>
     </bean>
     
+    <bean name="defaultHomeFolderProvider" class="org.alfresco.repo.security.person.UIDBasedHomeFolderProvider">
+        <property name="homeFolderManager">
+            <ref bean="homeFolderManager" />
+        </property>
+    </bean>
+    
     <bean name="personalHomeFolderProvider" class="org.alfresco.repo.security.person.UIDBasedHomeFolderProvider">
         <property name="serviceRegistry">
             <ref bean="ServiceRegistry" />
         </property>
         <property name="path">
-           <value>/${spaces.company_home.childname}</value>
+           <value>/${spaces.company_home.childname}/${spaces.user_homes.childname}</value>
         </property>
         <property name="storeUrl">
            <value>${spaces.store}</value>


<pre>
     <bean name="personalHomeFolderProvider" class="org.alfresco.repo.security.person.UIDBasedHomeFolderProvider">
         <property name="serviceRegistry">
             <ref bean="ServiceRegistry" />
         </property>
         <property name="path">
-           <value>/${spaces.company_home.childname}</value>
+           <value>/${spaces.company_home.childname}/${spaces.user_homes.childname}</value>
         </property>
         <property name="storeUrl">
            <value>${spaces.store}</value>

[edit] /alf/alfy/WEB-INF/classes/alfresco/authority-services-context.xml

Add additional users from AD for administrative priveleges.

        <property name="adminUsers">
            <set>
                        <value>admin</value>
                        <value>administrator</value>
                        <value>!ADUSERNAME!</value>
            </set>
        </property>

[edit] /alf/alfy/WEB-INF/classes/alfresco/extension/ldap-authentication-context.xml

This critical extension enables the importation of users and groups from the Microsoft Active Directory via LDAP. It may take some patience to adjust this file to your needs. Once settled though make sure set clearAllChildren=false for user imports. It will minimize interuptions when users login during import jobs. User accounts if non-existant will create usernamed home directories in under the 'Company Home/User Homes'. If you want them created somewhere else you'll need to modify the cm:homeFolderProvider.

  • [!SERVER_NAME!] Can be any unique network computer name. ALFY1, FRESCO1, MONET1, etc.
  • [!YOUR_DOMAIN_NAME!] is the Microsoft AD Domain Name that the Alfresco CIFS server will be registered in.
  • [!MICROSOFT_LDAP_SERVER_ADDRESS!] The fqdn of you Microsoft AD Server that will authenticate users.
  • [!LDAP_READ_USER!] A user with READ access across the entire tree. Of the format USERNAME@AD_DOMAIN_NAME
  • [!LDAP_READ_USER_PASSWORD!]
  • [!COMPANY!] [!TLD!] is YOUR_DOMAIN_NAME in parts i.e., company and com respectively.

The personQuery can trip you up. But this works well for Microsoft AD. It only imports users that have a givenName, surname and email values in their accounts. So, if you aren't seeing users that you expect a user may be lacking one or more of these values.

        <property name="personQuery">
            <value><![CDATA[(&(objectClass=user)(givenName=*)(sn=*)(mail=*))]]></value>
        </property>

The groupQuery is also tricky. But this works well for our purposes. Rather than clutter the Alfresco group management with groups that are not of any relevance we only import alfresco groups. Modify to meet your needs.

        <property name="groupQuery">
            <value><![CDATA[(&(objectclass=group)(cn=*Alfresco*))]]></value>
        </property>

Make sure the searchBase is correct for your environment.

        <property name="searchBase">
            <value>dc=[!COMPANY!],dc=[!TLD!]</value>
        </property>

Per the notes in the file and from our experience this setting is accurate.

        <property name="userIdAttributeName">
            <value>sAMAccountName</value>
        </property>

vi /alf/alfy/WEB-INF/classes/alfresco/extension/ldap-authentication-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>

    <!-- DAO that rejects changes - LDAP is read only at the moment. It does allow users to be deleted with out warnings from the UI. -->
    
    <bean name="authenticationDao" class="org.alfresco.repo.security.authentication.DefaultMutableAuthenticationDao" >
        <property name="allowDeleteUser">
            <value>true</value>
        </property>
    </bean>    
   
    <!--
    
    This bean is used to support general LDAP authentication. It is also used to provide read only access to users and groups
    to pull them out of the LDAP reopsitory
    
    -->
    
    <bean id="ldapInitialDirContextFactory" class="org.alfresco.repo.security.authentication.ldap.LDAPInitialDirContextFactoryImpl">
        <property name="initialDirContextEnvironment">
            <map>
                <!-- The LDAP provider -->
                <entry key="java.naming.factory.initial">
                    <value>com.sun.jndi.ldap.LdapCtxFactory</value>
                </entry>
                
                <!-- The url to the LDAP server -->
                <!-- Note you can use space separated urls - they will be tried in turn until one works -->
                <!-- This could be used to authenticate against one or more ldap servers (you will not know which one ....) -->
                <entry key="java.naming.provider.url">
                    <value>ldap://[!MICROSOFT_LDAP_SERVER_ADDRESS!]:389</value>
                </entry>
                
                <!-- The authentication mechanism to use      -->
                <!-- Some sasl authentication mechanisms may require a realm to be set -->
                <!--                java.naming.security.sasl.realm -->
                <!-- The available options will depend on your LDAP provider -->
                <entry key="java.naming.security.authentication">
                    <value>simple</value>
                </entry>
                
                <!-- The id of a user who can read group and user information -->
                <!-- This does not go through the pattern substitution defined above and is used "as is" -->
                <entry key="java.naming.security.principal">
                    <value>[!LDAP_READ_USER!]</value>
                </entry>
                
                <!-- The password for the user defined above -->
                <entry key="java.naming.security.credentials">
                    <value>[!LDAP_READ_USER_PASSWORD!]</value>
                </entry>
            </map>
        </property>
    </bean>
    
    <!-- Ldap Syncronisation support -->
    
    <!-- 
        
    There can be more than one stack of beans that import users or groups. For example, it may be easier
    to have a version of ldapPeopleExportSource, and associated beans, for each sub-tree of your ldap directory
    from which you want to import users. You could then limit users to be imported from two or more sub tress and ignore 
    users found else where. The same applies to the import of groups. 
         
    The defaults shown below are for OpenLDAP.    
        
    -->
        
   
    <!-- Extract user information from LDAP and transform this to XML -->
     
    <bean id="ldapPeopleExportSource" class="org.alfresco.repo.security.authentication.ldap.LDAPPersonExportSource">
        <!-- 
        The query to select objects that represent the users to import.
        
        For Open LDAP, using a basic schema, the following is probably what you want:
        (objectclass=inetOrgPerson)
        
        For Active Directory:
        (objectClass=user)
        -->
        <property name="personQuery">
            <value><![CDATA[(&(objectClass=user)(givenName=*)(sn=*)(mail=*))]]></value>
        </property>
        
        <!--
        The seach base restricts the LDAP query to a sub section of tree on the LDAP server.
        -->
        <property name="searchBase">
            <value>dc=[!COMPANY!],dc=[!TLD!]</value>
        </property>
        
        <!--
        The unique identifier for the user.
        
        THIS MUST MATCH WHAT THE USER TYPES IN AT THE LOGIN PROMPT    
        
        For simple LDAP authentication this is likely to be "cn" or, less friendly, "distinguishedName"
        
        In OpenLDAP, using other authentication mechanisms "uid", but this depends on how you map
        from the id in the LDAP authentication request to search for the inetOrgPerson against which
        to authenticate.
        
        In Active Directory this is most likely to be "sAMAccountName" 
        
        This property is mandatory and must appear on all users found by the query defined above.
        
        -->
        <property name="userIdAttributeName">
            <value>sAMAccountName</value>
        </property>
        
        <!-- Services -->
        <property name="LDAPInitialDirContextFactory">
            <ref bean="ldapInitialDirContextFactory"/>
        </property>
        <property name="personService">
            <ref bean="personService"></ref>
        </property>
        <property name="namespaceService">
            <ref bean="namespaceService"/>
        </property>
        
        <!--
        This property defines a mapping between attributes held on LDAP user objects and
        the properties of user objects held in the repository. The key is the QName of an attribute in
        the repository, the value is the attribute name from the user/inetOrgPerson/.. object in the
        LDAP repository.     
        -->
        <property name="attributeMapping">
            <map>
                <entry key="cm:userName">
                    <!-- Must match the same attribute as userIdAttributeName -->
                    <value>sAMAccountName</value>
                </entry>
                <entry key="cm:firstName">
                    <!-- OpenLDAP: "givenName" -->
                    <!-- Active Directory: "givenName" -->
                    <value>givenName</value>
                </entry>
                <entry key="cm:lastName">
                    <!-- OpenLDAP: "sn" -->
                    <!-- Active Directory: "sn" -->
                    <value>sn</value>
                </entry>
                <entry key="cm:email">
                    <!-- OpenLDAP: "mail" -->
                    <!-- Active Directory: "???" -->
                    <value>mail</value>
                </entry>
                <entry key="cm:organizationId">
                    <!-- OpenLDAP: "o" -->
                    <!-- Active Directory: "???" -->
                    <value>o</value>
                </entry>
                <!-- Always use the default -->
                <entry key="cm:homeFolderProvider">
                    <null/>
                </entry>
            </map>
        </property>
        <!-- Set a default home folder provider -->
        <!-- Defaults only apply for values above -->
        <property name="attributeDefaults">
            <map>
                <entry key="cm:homeFolderProvider">
                    <value>personalHomeFolderProvider</value>
                </entry>
            </map>
        </property>
    </bean>
    
    <!-- Extract group information from LDAP and transform this to XML -->
    
    <bean id="ldapGroupExportSource" class="org.alfresco.repo.security.authentication.ldap.LDAPGroupExportSource">
        <!--
        The query to select objects that represent the groups to import.
        
        For Open LDAP, using a basic schema, the following is probably what you want:
        (objectclass=groupOfNames)
        
        For Active Directory:
        (objectclass=group)
        -->
        <property name="groupQuery">
            <value><![CDATA[(&(objectclass=group)(cn=*Alfresco*))]]></value>
        </property>
        
        <!--
        The seach base restricts the LDAP query to a sub section of tree on the LDAP server.
        -->
        <property name="searchBase">
            <value>dc=ad,dc=menasha,dc=com</value>
        </property>
        
        <!--
        The unique identifier for the user. This must match the userIdAttributeName on the ldapPeopleExportSource bean above.
        -->
        <property name="userIdAttributeName">
            <value>sAMAccountName</value>
        </property>
        
        <!--
        An attribute that is a unique identifier for each group found. 
        This is also the name of the group with the current group implementation.
        This is mandatory for any groups found.
        
        OpenLDAP: "cn" as it is mandatory on groupOfNames
        Active Directory: "cn"
        
        -->
        <property name="groupIdAttributeName">
            <value>cn</value>
        </property>
        
        <!-- 
        The objectClass attribute for group members.
        For each member of a group, the distinguished name is given.
        The object is looked up by its DN. If the object is of this class it is treated as a group. 
        -->
        <property name="groupType">
            <value>group</value>
        </property>
        
        <!-- 
        The objectClass attribute for person members.
        For each member of a group, the distinguished name is given.
        The object is looked up by its DN. If the object is of this class it is treated as a person. 
        -->
        <property name="personType">
            <value>person</value>
        </property>
        <property name="LDAPInitialDirContextFactory">
            <ref bean="ldapInitialDirContextFactory"/>
        </property>
        <property name="namespaceService">
            <ref bean="namespaceService"/>
        </property>
        
        <!--
        The repeating attribute on group objects (found by query or as sub groups)
        used to define membership of the group. This is assumed to hold distinguished names of
        other groups or users/people; the above types are used to determine this.
        
        OpenLDAP: "member" as it is mandatory on groupOfNames
        Active Directory: "member"
        
        -->
        <property name="memberAttribute">
            <value>member</value>
        </property>
        
        <property name="authorityDAO">
            <ref bean="authorityDAO"/>
        </property>
    </bean>
    
    <!-- Job definitions to import LDAP people and groups -->
    <!-- The triggers register themselves with the scheduler -->
    <!-- You may comment in the default scheduler to enable these triggers -->
    <!-- If a cron base trigger is what you want seee scheduled-jobs-context.xml for examples. -->
    
    <!-- Trigger to load poeple -->
    <!-- Note you can have more than one initial (context, trigger, import job and export source) set -->
    <!-- This would allow you to load people from more than one ldap store -->
    
    <bean id="ldapPeopleTrigger" class="org.alfresco.util.TriggerBean">
        <property name="jobDetail">
            <bean id="ldapPeopleJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
                <property name="jobClass">
                    <value>org.alfresco.repo.importer.ImporterJob</value>
                </property>
                <property name="jobDataAsMap">
                    <map>
                        <entry key="bean">
                            <ref bean="ldapPeopleImport"/>
                        </entry>
                    </map>
                </property>
            </bean>
        </property>
        <!-- Start after 8 minutes of starting the repository -->
        <property name="startDelay">
            <value>480000</value>
        </property>
        <!-- Repeat every 1.25 hour -->
        <property name="repeatInterval">
            <value>4500000</value>
        </property>
        <property name="scheduler">
            <ref bean="schedulerFactory" />
        </property>
    </bean>
    
    <bean id="ldapGroupTrigger" class="org.alfresco.util.TriggerBean">
        <property name="jobDetail">
            <bean id="ldapGroupJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
                <property name="jobClass">
                    <value>org.alfresco.repo.importer.ImporterJob</value>
                </property>
                <property name="jobDataAsMap">
                    <map>
                        <entry key="bean">
                            <ref bean="ldapGroupImport"/>
                        </entry>
                    </map>
                </property>
            </bean>
        </property>
        <!-- Start after 3 minutes of starting the repository -->
        <property name="startDelay">
            <value>180000</value>
        </property>
        <!-- Run every 4 hours -->
        <property name="repeatInterval">
            <value>14400000</value>
        </property>
        <property name="scheduler">
            <ref bean="schedulerFactory" />
        </property>
    </bean>

    <!-- The bean that imports xml describing people -->
    
    <bean id="ldapPeopleImport" class="org.alfresco.repo.importer.ExportSourceImporter">
        <property name="importerService">
            <ref bean="importerComponentWithBehaviour"/>
        </property>
        <property name="transactionService">
            <ref bean="transactionComponent"/>
        </property>
        <property name="authenticationComponent">
            <ref bean="authenticationComponent"/>
        </property>
        <property name="exportSource">
            <ref bean="ldapPeopleExportSource"/>
        </property>

        <!-- The store that contains people - this should not be changed -->
        <property name="storeRef">
            <value>${spaces.store}</value>
        </property>
        
        <!-- The location of people nodes within the store defined above - this should not be changed -->
        <property name="path">
            <value>/${system.system_container.childname}/${system.people_container.childname}</value>
        </property>
        
        <!-- If true, clear all existing people before import, if false update/add people from the xml -->
        <property name="clearAllChildren">
            <value>false</value>
        </property>
        <property name="nodeService">
            <ref bean="nodeService"/>
        </property>
        <property name="searchService">
            <ref bean="searchService"/>
        </property>
        <property name="namespacePrefixResolver">
            <ref bean="namespaceService"/>
        </property>
        
        
        <property name="caches">
            <set>
                <ref bean="permissionsAccessCache"/>
            </set>
        </property>
    </bean>
    
    <!-- The bean that imports xml descibing groups -->
    
    <bean id="ldapGroupImport" class="org.alfresco.repo.importer.ExportSourceImporter">
        <property name="importerService">
            <ref bean="importerComponentWithBehaviour"/>
        </property>
    </bean>
    
    <!-- The bean that imports xml descibing groups -->
    
    <bean id="ldapGroupImport" class="org.alfresco.repo.importer.ExportSourceImporter">
        <property name="importerService">
            <ref bean="importerComponentWithBehaviour"/>
        </property>
        <property name="transactionService">
            <ref bean="transactionComponent"/>
        </property>
        <property name="authenticationComponent">
            <ref bean="authenticationComponent"/>
        </property>
        <property name="exportSource">
            <ref bean="ldapGroupExportSource"/>
        </property>
        <!-- The store that contains group information - this should not be changed -->
        <property name="storeRef">
            <value>${alfresco_user_store.store}</value>
        </property>
        
        <!-- The location of group information in the store above - this should not be changed -->
        <property name="path">
            <value>/${alfresco_user_store.system_container.childname}/${alfresco_user_store.authorities_container.childname}</value>
        </property>
        
        <!-- If true, clear all existing groups before import, if false update/add groups from the xml -->
        <property name="clearAllChildren">
            <value>true</value>
        </property>
        <property name="nodeService">
            <ref bean="nodeService"/>
        </property>
        <property name="searchService">
            <ref bean="searchService"/>
        </property>
        <property name="namespacePrefixResolver">
            <ref bean="namespaceService"/>
        </property>
        
        <!-- caches to clear on import of groups -->
        <property name="caches">
            <set>
                <ref bean="userToAuthorityCache"/>
                <ref bean="permissionsAccessCache"/>
            </set>
        </property>
        
        <!-- userToAuthorityCache -->
    </bean>

</beans>

[edit] /alf/alfy/WEB-INF/classes/alfresco/extension/jaas-authentication-context.xml

This important extension enables the JAAS user authentication mechanism for the Alfresco WEB interface.

vi /alf/alfy/WEB-INF/classes/alfresco/extension/jaas-authentication-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
    <!-- The authentication component.                                      -->

    <!-- Jass authentication - most of the config goes somewhere else       -->
       
    <bean id="authenticationComponent" 
                 class="org.alfresco.repo.security.authentication.jaas.JAASAuthenticationComponent">
        <property name="realm">
            <value>AD.MENASHA.COM</value>
        </property>
        <property name="jaasConfigEntryName">
            <value>Alfresco</value>
        </property>
    </bean>

                <bean id="alfDaoImpl" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
                        <property name="proxyInterfaces">
                                        <value>
                                                org.alfresco.repo.security.authentication.MutableAuthenticationDao
                                        </value>
                        </property>
                        <property name="transactionManager">
                                        <ref bean="transactionManager" />
                        </property>
                        <property name="target">
                                        <bean class="org.alfresco.repo.security.authentication.ntlm.NullMutableAuthenticationDao" />
                        </property>
                        <property name="transactionAttributes">
                                <props>
                                        <prop key="*">${server.transaction.mode.default}</prop>
                                </props>
                        </property>
                </bean>

</beans>

[edit] /alf/alfy/WEB-INF/classes/alfresco/extension/file-servers-custom.xml

This extension will enable CIFS, FTP and NTLM Passthru to the Microsoft AD server. The Alfresco WEB interface is authenticating with JAAS.

  • [!SERVER_NAME!] Can be any unique network computer name. ALFY1, FRESCO1, MONET1, etc.
  • [!YOUR_DOMAIN_NAME!] is the Microsoft AD Domain Name that the Alfresco CIFS server will be registered in.
  • [!SERVER_REAL_IP_ADDRESS!] For now this is the address you assigned on interface eth0. Later when we enable the cluster this will change to the non-arping virtual address on eth1.
  • [!WINS_SERVER_IP_ADDRESS!] Is your WINS server IP address.
  • [!YOUR_WEB_FQDN!] Set to the Fully Qualified Domain Name for accessing the Alfresco web server. e.g. www.alfresco.company.com
  • [!MICROSOFT_AD_SERVER_ADDRESS!] The fqdn/ip of you Microsoft AD Server that will authenticate users.
vi /alf/alfy/WEB-INF/classes/alfresco/extension/file-servers-custom.xml
<alfresco-config area="file-servers">

   <config evaluator="string-compare" condition="CIFS Server" replace="true">
      <serverEnable enabled="true"/>
      <host name="[!SERVER_NAME!]" domain="[!YOUR_DOMAIN_NAME!]"/>
      <comment>CIFS</comment>
      <sessionDebug flags="Negotiate,NetBIOS,State,Tree,Search,Info,File,FileIO,Tran,Echo,Errors,IPC,Lock,Pkttype,Dcerpc,Statecache,Notify,Streams,Socket"/>

      <!-- Set to the broadcast mask for the subnet -->
      <bindto>[!SERVER_REAL_IP_ADDRESS!]</bindto>
      <broadcast>X.X.X.255</broadcast>

      <!-- Use Java socket based NetBIOS over TCP/IP and native SMB on linux -->
      <tcpipSMB port="2445" platforms="linux"/>
      <netBIOSSMB bindto="[!SERVER_REAL_IP_ADDRESS!]" sessionPort="2139" namePort="2137" datagramPort="2138" platforms="linux"/>
      <hostAnnounce interval="5"/>

      <!-- Use Win32 NetBIOS interface on Windows
      <Win32NetBIOS/>
      <Win32Announce interval="5"/>
      -->

      <WINS>
         <primary>[!WINS_SERVER_IP_ADDRESS!]</primary>
      </WINS>

   </config>

   <config evaluator="string-compare" condition="FTP Server" replace="true">
     <serverEnable enabled="true"/>
     <debug flags="File,Search,Error,Directory,Info,DataPort"/>
     <port>2021</port>
     <bindto>[!SERVER_REAL_IP_ADDRESS!]</bindto>
     <rootDirectory>/Alfresco</rootDirectory>
   </config>

   <config evaluator="string-compare" condition="NFS Server" replace="true">
      <serverEnable enabled="false"/>
      <enablePortMapper/>
      <rpcAuthenticator>
         <userMappings>
            <user name="admin" uid="0" gid="0"/>
            <user name="auser" uid="501" gid="501"/>
         </userMappings>
      </rpcAuthenticator>
   </config>

   <config evaluator="string-compare" condition="Filesystems" replace="true">
          <filesystems>

         <filesystem name="Alfresco">
            <store>workspace://SpacesStore</store>
            <rootPath>/app:company_home</rootPath>

            <!-- Add a URL file to each folder that links back to the web client -->
            <urlFile>
               <filename>__Alfresco.url</filename>
               <webpath>https://[!YOUR_WEB_FQDN!]/</webpath>
            </urlFile>

            <!-- Mark locked files as offline --> 
            <offlineFiles/>

            <desktopActions>
               <global>
                  <path>alfresco/desktop/Alfresco.exe</path>
                  <webpath>https://[!YOUR_WEB_FQDN!]/</webpath>
               </global>
               <action>
                  <class>org.alfresco.filesys.smb.server.repo.desk.CheckInOutDesktopAction</class>
                  <name>CheckInOut</name>
                  <filename>__CheckInOut.exe</filename>
               </action>
               <action>
                  <class>org.alfresco.filesys.smb.server.repo.desk.JavaScriptDesktopAction</class>
                  <name>JavaScriptURL</name>
                  <filename>__ShowDetails.exe</filename>
                  <script>alfresco/desktop/showDetails.js</script>
                  <attributes>anyFiles</attributes>
                  <preprocess>copyToTarget</preprocess>
               </action>

            </desktopActions>

         </filesystem>

                 <!-- AVM virtualization view of all stores/versions for WCM -->
         <avmfilesystem name="AVM">
            <virtualView/>
         </avmfilesystem>

      </filesystems>
   </config>

   <config evaluator="string-compare" condition="Filesystem Security" replace="true">

      <authenticator type="passthru">
        <Server>[!MICROSOFT_AD_SERVER_ADDRESS!]</Server>
        <Domain>[!YOUR_DOMAIN_NAME!]</Domain>
      </authenticator>

   </config>


</alfresco-config>

[edit] /alf/alfy/WEB-INF/classes/alfresco/extension/index-tracking-context.xml

This extension will keep keep the search index refreshed as documents are added/removed from the repository.

vi /alf/alfy/WEB-INF/classes/alfresco/extension/index-tracking-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>

    <!-- Schedule index tracking every 10s -->
    <bean id="indexTrackerTrigger" class="org.alfresco.util.TriggerBean">
        <property name="jobDetail">
            <bean class="org.springframework.scheduling.quartz.JobDetailBean">
                <property name="jobClass">
                    <value>org.alfresco.repo.node.index.IndexRecoveryJob</value>
                </property>
                <property name="jobDataAsMap">
                    <map>
                        <entry key="indexRecoveryComponent">
                            <ref bean="indexTrackerComponent" />
                        </entry>
                    </map>
                </property>
            </bean>
        </property>
        <!-- Start after 5 minutes of starting the repository -->
        <property name="startDelay">
            <value>300000</value>
        </property>
        <!-- Repeat every 10 seconds -->
        <property name="repeatInterval">
            <value>10000</value>
        </property>
        <property name="scheduler">
            <ref bean="schedulerFactory" />
        </property>
    </bean>
    
    <bean
          id="indexTrackerComponent"
          class="org.alfresco.repo.node.index.IndexRemoteTransactionTracker"
          parent="indexRecoveryComponentBase">
        <property name="remoteOnly">
            <value>true</value>
        </property>
    </bean>

</beans>

[edit] /alf/alfy/WEB-INF/classes/alfresco/extension/web-client-config-custom.xml

This enables the multi-lingual features and overriding email address and max search results. This is also where custom aspects will be added.

vi 
<alfresco-config>

   <!-- Example of overriding the from email address -->
   <config>
      <client>
         <from-email-address>[!ALFRESCO_ADMIN_EMAIL_ADDRESS]</from-email-address>
         <search-max-results>300</search-max-results>
      </client>
   </config>

   <!-- Example of adding languages to the list in the login page -->
   <config evaluator="string-compare" condition="Languages">
      <languages>
         <language locale="ca_ES">Catalan</language>
         <language locale="da_DK">Danish</language>
         <language locale="de_DE">German</language>
         <language locale="es_ES">Spanish</language>
         <language locale="el_GR">Greek</language>
         <language locale="fr_FR">French</language>
         <language locale="it_IT">Italian</language>
         <language locale="ja_JP">Japanese</language>
         <language locale="nl_NL">Dutch</language>
         <language locale="pt_BR">Portuguese (Brazilian)</language>
         <language locale="ru_RU">Russian</language>
         <language locale="fi_FI">Finnish</language>
         <language locale="tr_TR">Turkish</language>
         <language locale="zh_CN">Simplified Chinese</language>
      </languages>
   </config>

</alfresco-config>

[edit] /alf/alfy/WEB-INF/classes/alfresco/ehcache-default.xml =

In preparation for Alfresco in a cluster the cache mechanism must be set to synchronize with other members. Thankfully with the EHCache and multicasting the servers can join/exit without always re-configuring. Cache synchronization allows changes conducted on one Alfresco server to be evident on the other member servers. So, in the highly unlikely event that the same user is logged on to two seperate Alfresco instances, any changes in the users state will be reflected on all member servers. (There is a slight update delay but hardly noticable.)

Cache Synchronization is TIME SENSITIVE! Make sure that the member servers have a precise time agreement between them. I'll assume that NTP is configured and running on each respective server.

@@ -3,294 +3,658 @@
     <!-- defaults -->
     <diskStore 
         path="java.io.tmpdir"/>
-<!--
+
     <cacheManagerPeerProviderFactory
         class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
-        properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
+        properties="peerDiscovery=automatic, multicastGroupAddress=233.128.128.13,
                     multicastGroupPort=4446"/>
     <cacheManagerPeerListenerFactory
-        class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
--->
+        class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
+        properties="port=40001, socketTimeoutMillis=90000"/>
+
     <defaultCache 
         maxElementsInMemory="5000" 
         eternal="true" 
         timeToIdleSeconds="0" 
         timeToLiveSeconds="0" 
-        overflowToDisk="false" 
-        >
-<!--
+        overflowToDisk="false" > 
         <cacheEventListenerFactory
                 class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
-                properties="replicateAsynchronously=false, replicatePuts=false,
+                properties="replicateAsynchronously=true, replicatePuts=true,
                             replicateUpdates=true, replicateUpdatesViaCopy=false,
                             replicateRemovals=true"/>
--->
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+
+
     </defaultCache>
     <!-- Hibernate usage -->
     <cache 
         name="org.hibernate.cache.StandardQueryCache" 
         maxElementsInMemory="50" 
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 0.4MB memory required -->
     <cache 
         name="org.hibernate.cache.UpdateTimestampsCache" 
         maxElementsInMemory="2000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 40MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.NodeImpl" 
         maxElementsInMemory="10000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 0.1 MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.QNameEntityImpl" 
         maxElementsInMemory="100"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 40MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.NodeStatusImpl" 
         maxElementsInMemory="10000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 15MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.NodeImpl.aspects" 
         maxElementsInMemory="10000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 10MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.NodeImpl.properties" 
         maxElementsInMemory="10000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 250MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.ChildAssocImpl" 
         maxElementsInMemory="200000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- general use node associations are not common  -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.NodeAssocImpl" 
         maxElementsInMemory="5000" 
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- low numbers of objects expected -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.StoreImpl" 
         maxElementsInMemory="100" 
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- version counters -->
     <!-- approx 0.4MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.VersionCountImpl" 
         maxElementsInMemory="100" 
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 0.1MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.AppliedPatchImpl" 
         maxElementsInMemory="100"
         timeToLiveSeconds="300" 
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- Permission related caches -->
     <!-- approx 1MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl" 
         maxElementsInMemory="1000"        
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 1MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.DbAccessControlListImpl.entries" 
         maxElementsInMemory="1000"        
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 5MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl" 
         maxElementsInMemory="5000"        
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 1MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.DbPermissionImpl" 
         maxElementsInMemory="500"          
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- approx 10MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.DbAuthorityImpl" 
         maxElementsInMemory="10000"        
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
      <!-- approx 5MB memory required -->
     <cache 
         name="org.alfresco.repo.domain.hibernate.DbAuthorityImpl.externalKeys" 
         maxElementsInMemory="5000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <!-- Audit caches -->
     <cache 
         name="org.alfresco.repo.audit.hibernate.AuditConfigImpl" 
         maxElementsInMemory="2"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <cache 
         name="org.alfresco.repo.audit.hibernate.AuditDateImpl" 
         maxElementsInMemory="2"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
     <cache 
         name="org.alfresco.repo.audit.hibernate.AuditSourceImpl" 
         maxElementsInMemory="2000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
        <!-- Attribute Service Caches. -->
        <cache
            name="org.alfresco.repo.attributes.AttributeImpl"
            maxElementsInMemory="5000"
            eternal="true"
-           overflowToDisk="false"
-           />
+           overflowToDisk="false">
+            <cacheEventListenerFactory
+                    class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                    properties="replicateAsynchronously=true, replicatePuts=true,
+                                replicateUpdates=true, replicateUpdatesViaCopy=true,
+                                replicateRemovals=true"/>
+
+            <bootstrapCacheLoaderFactory
+                    class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                    properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+           </cache>
        <cache
            name="org.alfresco.repo.attributes.ListEntryImpl"
            maxElementsInMemory="2000"
            eternal="true"
-           overflowToDisk="false"
-           />
+           overflowToDisk="false">
+            <cacheEventListenerFactory
+                    class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                    properties="replicateAsynchronously=true, replicatePuts=true,
+                                replicateUpdates=true, replicateUpdatesViaCopy=true,
+                                replicateRemovals=true"/>
+
+            <bootstrapCacheLoaderFactory
+                    class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                    properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+           </cache>
        <cache
                name="org.alfresco.repo.attributes.MapEntryImpl"
                maxElementsInMemory="2000"
                eternal="true"
-               overflowToDisk="false"
-               />
+               overflowToDisk="false">
+                <cacheEventListenerFactory
+                        class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                        properties="replicateAsynchronously=true, replicatePuts=true,
+                                    replicateUpdates=true, replicateUpdatesViaCopy=true,
+                                    replicateRemovals=true"/>
+
+                <bootstrapCacheLoaderFactory
+                        class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                        properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+               </cache>
        <cache
            name="org.alfresco.repo.attributes.GlobalAttributeEntryImpl"
            maxElementsInMemory="1000"
            eternal="true"
-           overflowToDisk="false"
-           />
+           overflowToDisk="false">
+            <cacheEventListenerFactory
+                    class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                    properties="replicateAsynchronously=true, replicatePuts=true,
+                                replicateUpdates=true, replicateUpdatesViaCopy=true,
+                                replicateRemovals=true"/>
+
+            <bootstrapCacheLoaderFactory
+                    class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                    properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+           </cache>
 
     <!-- AVM caches -->
      <cache
         name="org.alfresco.repo.avm.AVMNodeImpl" 
         maxElementsInMemory="5000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
      <cache
         name="org.alfresco.repo.avm.AVMStoreImpl" 
         maxElementsInMemory="100"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
      <cache
         name="org.alfresco.repo.avm.VersionRootImpl" 
         maxElementsInMemory="200"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
      <cache
         name="org.alfresco.repo.avm.ChildEntryImpl" 
         maxElementsInMemory="10000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
      <cache
         name="org.alfresco.repo.avm.HistoryLinkImpl" 
         maxElementsInMemory="200"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
      <cache
         name="org.alfresco.repo.avm.MergeLinkImpl" 
         maxElementsInMemory="200"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+                properties="replicateAsynchronously=true, replicatePuts=true,
+                            replicateUpdates=true, replicateUpdatesViaCopy=true,
+                            replicateRemovals=true"/>
+
+        <bootstrapCacheLoaderFactory
+                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
+                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
+        </cache>
      <cache
         name="org.alfresco.repo.avm.AVMNodePropertyImpl" 
         maxElementsInMemory="2000"
         eternal="true"
-        overflowToDisk="false"
-        />
+        overflowToDisk="false">
+        <cacheEventListenerFactory
+                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
+