Arquitectura(s) Cluster en Alfresco

Desde un punto de vista más teórico y funcional, dentro de los escenarios de posibles instalaciones en cluster de Alfresco ECM encontramos dos escenarios básicos, uno que utiliza repositorio por cada nodo y estos son sincronizados y la más usada actualmente, en la que se usa un repositorio común así como índices separados por cada nodo.

Dentro del segundo escenario, existe multitud de documentación sobre posibles arquitecturas de Alfresco ECM que podemos encontrar tanto en la documentación oficial de Alfresco (http://docs.alfresco.com) como en otros recursos tanto personales (http://blyx.com), como públicos (Junta de Andalucía, Ministerio de Justicia, etc.)

Pero generalmente, cuando se habla de arquitecturas en cluster para Alfresco, estas se limitan solo al producto en sí, es lógico pues Alfresco ECM es capaz de clusterizarse mediante el uso de EHCache y JGroups sin necesidad de más componentes.

Una posible arquitectura a desarrollar es la siguiente:

Se pueden observar varias capas o niveles en las cuales se pueden introducir de forma intermedia distintos elementos adicionales como nuevos balanceadores (hardware y software), firewalls, etc. según las necesidades de cada entorno.

Existen además varios tipos de usuarios, los usuarios externos o que acceden desde fuera de la organización, estos a su vez pueden ser usuarios “anónimos” o “autenticados” y también usuarios que son trabajadores de la organización y que deben tener los recursos como si estuvieran dentro de esta, incluidos los de administración.

Intranet Network: Es la red de intranet principal a través de la cual se conectan todos los usuarios. Generalmente es la red local de la empresa, bien, sementada, o por el contrario para un solo segmento p.e. 192.168.1.0/24 (Clase C). Los accesos a la mayoría de los protocolos por parte de los usuarios y concretamente a través de HTTP para el Share se realizan dentro de esta red. En algunos casos se puede segmentar esta para el uso de otros protocolos como CIFS pero requiere más complejidad de la que ya hay en sí misma.

Capa de WEB/Balanceo (1): Esta capa está planteada desde un doble punto de vista, por un lado es un servidor web (Apache) y por otro lado también es un balanceador software (Apache+mod_jk o Apache+mod_proxy) si bien también contempla el uso de alta disponibilidad por sí mismo (HA_Proxy) usando una IP “flotante” o virtual (192.168.1.100) a través de lo que se llama un Heartbeat (HB). De esta forma mantenemos la alta disponibilidad y sistema en “failover” para esa capa y usando un único punto de entrada para todos los usuarios.

Como esta capa es de entrada exclusiva por HTTP (Web y WebDAV), los accesos de otros protocolos que no pueden ser accedidos a través de Apache y el balanceo, como CIFS y FTP se realizarán directamente sobre la capa de aplicación.

Capa de presentación o FrontEnd (2): En esta capa se utiliza el servidor de aplicaciones como sistema de acceso al repositorio donde se instala Alfresco Share.

Esta capa puede contener también servidores HTTP como Apache para servir partes estáticas e incluso para balancear también hacia la siguiente capa (repositorio de Alfresco). En este diagrama no viene indicado para no complicar en exceso pero en muchas ocasiones es muy buena idea que también esta capa balancee e incluya un “Heartbeat” para mantener alta disponibilidad también en este punto.

Tampoco viene reflejado pero se da por supuesto que los servidores Tomcat están en cluster a nivel de servidor de aplicaciones, que , aunque esto no afecte directamente a Alfresco es una buena idea tener todas las capas también con sus propios métodos de redundancia y sincronización.

Capa de aplicaciones o repositorio (3): Aquí están localizados los repositorios o Alfresco Explorer, no los repositorios físicos que son más propios de la siguiente capa. Es decir, la instalación de Alfresco (sin Share) estaría en este sitio. Alfresco está configurado por JGroups como cluster y conectado su “Heartbeat” mediante una red propia para este que realiza la sincronización mediante TCP y RMI (EHCache).

Capa de persistencia y repositorio (4): Esta capa es la encargada de almacenar físicamente los ficheros/documentos en el sistema de ficheros así como toda la información asociada en la base de datos. Se da por supuesto que el almacenamiento está redundado mediante cabinas de discos así como de un sistema como Oracle RAC o MySQL Cluster NDB. Cada sistema tiene su propia subred que debería estar conectada por “fibra channel”.

Otros elementos: Como se ve en el diagrama de arquitectura, existen elementos que al no estar directamente conectados mediante HTTP ni tener posibilidad de balanceo además de por sus características de aplicaciones, se conectan directamente a uno de los nodos de Alfresco. Así mismo, algunos accesos (p.e. CIFS, etc.) se realizan al otro nodo en una forma de repartir la carga de estos sistemas que no son balanceables.

Estos elementos son usados en muchas ocasiones para inyección masiva de datos o bien para conectar aplicaciones como SAP, AutoCAD, etc.

Administration & Monitoring Network: Por último, se usa también una red aparte también para los administradores y para monitorización, de esta forma se aíslan estas transmisiones y evita mayor carga en la red.

Más información:

Problema con Edit Online en CIFS con Alfresco (y solución)

En Alfresco se puede producir un problema si se activa CIFS y la edición en línea para crear y modificar documentos en MS-Office.

Problema:
Cuando se crea o modifica un documento en Word/Excel, Alfresco asigna el aspecto “ownable” con el valor de “owner” a null. Esto provoca que si dentro del espacio de trabajo, el usuario que lo ha creado o modificado tiene el rol “Editor”, no se puede borrar (no aparece el icono de la papelera ni se puede invocar a la acción de borrar).

Investigando un poco vemos que efectivamente al documento creado desde Word en la carpeta compartida de CIFS que controla Alfresco se le asigna el aspecto con el valor “owner” a null.

Este efecto puede observarse mejor en el explorador de nodos.

Entorno:
– Alfresco ECM 3.3.5
– RedHat 5
– MySQL 5
– Autenticación: Kerberos+SSO (Active Directory)

Solución:
He creado dos soluciones con scripts en JavaScript. Una de ellas lo que hace es eliminar el aspecto directamente y la segunda lo que hace es asignar el valor del campo “creator” al campo “owner” del aspecto “ownable”.

Ambas soluciones son válidas y solo hay que elegir la que mejor venga en cada ocasión. En mi caso, la primera es quizás más acorde con el funcionamiento normal del Alfresco, ya que al subir un nuevo documento, este no asigna nunca el aspecto “ownable”.

Implementación:
Tanto una como otra solución consisten en scripts en JavaScript que tienen que ser llamados por ejemplo desde una regla asignada al espacio donde se crean los documentos directamente en Word vía unidad compartida con CIFS. Deben crearse dos reglas, una para los nuevos documentos y otra para cuando sean modificados.

Scripts:
Fichero: removeAspect_ownable.js

// Elimina aspecto owneable (problema en edit-online con office – 1a solución)
if (document.properties[“cm:owner”] == null)
{
    logger.log(“Eliminando owner vacío para el documento “+document.properties[“name”]);
    document.removeAspect(“cm:ownable”);
}

Fichero: creatorToOwner.js

// Asigna como owner el creator (problema en edit-online con office – 2a solución)
if (document.properties[“cm:owner”] == null)
{
    logger.log(“Asignando “+document.properties[“cm:creator”]+” al owner vacío para el documento “+document.properties[“cm:name”]);
    document.properties[“cm:owner”] = document.properties[“cm:creator”];
    document.save();
}

De esta forma, queda solucionado este problema hasta que sea solucionado por parte de Alfresco en siguientes versiones.

Más información:
JavaScript API para Alfresco: http://wiki.alfresco.com/wiki/JavaScript_API
Reglas y acciones en Alfresco: http://wiki.alfresco.com/wiki/Actions_and_Rules
Crear reglas y acciones (en Share): http://www.youtube.com/watch?v=1NL8a-6dU7Y

Mantenimiento diario de Alfresco

Una vez instalado Alfresco, sea el método que sea el que se ha usado para su instalación (bundle, instalador, instalación separada de servidor de aplicaciones más producto, etc.), es necesario realizar una serie de tareas de mantenimiento diarias para no encontrarnos posteriormente con desagradables sorpresas.

El problema más común en este sentido es que se llene alguna de las particiones o discos que se están usando por parte de Alfresco para almacenar ficheros temporales, documentos borrados o logs.

Para ello, se pueden crear una serie de tareas y scripts para tener un mantenimiento automatizado. En mi caso uso un mantenimiento que termina borrando definitivamente ficheros, en otras situaciones es preferible realizar copias antes de estos borrados.

Lo primero que hay que hacer es tener bien colocado el fichero de logs de Alfresco,  para esto, copiamos el fichero log4j.properties como custom-log4j.properties dentro del extension. Por ejemplo:

cp /opt/alfresco/tomcat/webapps/alfresco/WEB-INF/classes/log4j.properties /opt/alfresco/tomcat/shared/classes/alfresco/extension/custom-log4j.properties

Y seguidamente ponemos el directorio correcto donde se almacenarán los logs. En este caso el siguiente valor:

Fichero: custom-log4j.properties

[…]
log4j.appender.File.File=/opt/alfresco/tomcat/logs/alfresco.log
[…]

Ahora los ficheros de logs propios de Alfresco se crearán dentro del directorio apropiado.

Seguidamente, aunque Alfresco ya realiza una rotación de estos logs por fechas, se puede crear un pequeño script para comprimir los ficheros de logs que han sido rotados:

Fichero: cron_gzip_logs.sh

#!/bin/bash
. `dirname “$0″`/config
for i in `ls ${CATALINA_HOME}/logs/alfresco.log.*[!.gz] 2>/dev/null`
do
        gzip $i
done

Además, podemos borrar los ficheros de logs aunque ya hayan sido rotados y comprimidos pero que tengan un número de días y por tanto ya no nos interesen. Para ello he creado el siguiente script:

Fichero: cron_del_logs.sh

#!/bin/bash

. `dirname “$0″`/config
find ${CATALINA_HOME}/logs/alfresco.log.*.gz -mtime +${AFTER_DAYS} -delete 2>/dev/null

También necesitamos borrar ficheros temporales que hayan sido creados hace días y no se hayan eliminado:

Fichero: cron_del_temp.sh

#!/bin/bash
. `dirname “$0″`/config
find ${CATALINA_HOME}/temp -mtime +${AFTER_DAYS_TEMP} -delete 2>/dev/null

En Alfresco, los ficheros/documentos que ya no están en la papelera y han pasado más de 14 días son movidos a un directorio generalmente llamado contentstore.deleted. Este directorio no es borrado nunca por Alfresco por lo que si se realizan muchas modificaciones de documentos y borrados puede llenarse y ocupar mucho espacio. Para solucionar esto he creado el siguiente script:

Fichero: cron_del_deleted.sh
#!/bin/bash
. `dirname “$0″`/config
find ${CONTENTSTORE_DELETED}/* -mtime +${AFTER_DAYS_DELETED} -delete 2>/dev/null

Ahora solo queda crear el fichero de configuración donde se asignan los valores a las variables usadas:

Fichero: config

CATALINA_HOME=/opt/alfresco/tomcat
AFTER_DAYS=30
AFTER_DAYS_DELETED=5
AFTER_DAYS_TEMP=5
CONTENTSTORE_DELETED=/opt/alfresco/alf_data/contentstore.deleted

Y el fichero maestro que llamará a todos los scripts (al que he llamado adt.sh en alusión a Alfresco Daily Tasks) y que servirá para ponerlo en el cron diario:

Fichero: adt.sh
#!/bin/bash
#
# @Author: Fernando Gonzalez
# @Version: 1.0
# @Fecha: 2011
#
UTILS_DIR=/opt/alfresco/utils
${UTILS_DIR}/cron_gzip_logs.sh
${UTILS_DIR}/cron_del_logs.sh
${UTILS_DIR}/cron_del_temp.sh
${UTILS_DIR}/cron_del_deleted.sh

Y hacer el enlace simbólico dentro del directorio /etc/cron.daily o crearlo en la “crontab”:

cd /etc/cron.daily

ln -s /opt/alfresco/utils/adt.sh .

Esto es simplemente un ejemplo de las tareas que hay que hacer después de la instalación de Alfresco para el mantenimiento diario y así evitar problemas futuros.

Otras consideraciones: Dentro de este mantenimiento, recien instalado Alfresco y si estamos ante un sistema en cluster, recomiendo la lectura del siguiente artículo escrito por Toni de la Fuente sobre el sistema de “jobs” de Alfresco: http://blyx.com/2011/07/01/alfresco-scheduled-jobs-tareas-automaticas-de-mantenimiento/

Actualización (23/08/2011).

En el uso de OpenOffice.org, los ficheros temporales se crean por lo general en el directorio temporal del sistema operativo, en la mayor parte de los casos cuando se trata de Linux es en /tmp. El script para borrar los ficheros temporales del usuario que ejecuta Alfresco sería:

Fichero: cron_del_tmp.sh
#!/bin/bash
. `dirname “$0″`/config
find ${TMP}/ -mtime +${AFTER_DAYS_TMP} -uid $UID -delete 2>/dev/null

Hay que añadir al fichero config las dos líneas de variables usadas:
Fichero: config
[…]
AFTER_DAYS_TMP=5
TMP=/tmp

Y también la ejecución del nuevo script al fichero adt.sh:
[…]
${UTILS_DIR}/cron_del_tmp.sh

Es MUY IMPORTANTE que Alfresco siempre sea ejecutado por un usuario no root ya que de lo contrario si ocurre algún problema en el borrado de ficheros podría desestabilizarse e incluso romperse el sistema.

Procesos de OpenOffice.org y JodConverter en Alfresco

Alfresco aconseja el uso de JODConverter en contraposición de OpenOffice.org directamente para la transformación de documentos.

En la configuración típica de estos programas en el fichero alfresco-global.properties se establecen unos parámetros parecidos a los siguientes:

ooo.exe=soffice
ooo.enabled=false
jodconverter.officeHome=/usr/lib/openoffice.org3
jodconverter.portNumbers=8101
jodconverter.enabled=true

En este caso, se desactiva expresamente el uso de OpenOffice.org y se activa el uso de JODConverter.

Si comprobamos el proceso arrancado podremos ver que hay un OpenOffice.org en modo “escucha” para el puerto seleccionado, el 8101:

[root@alfpru1 bin]# ps -fea | grep openoffice
root      5057  4746  0 11:55 pts/0    00:00:00 /usr/lib/openoffice.org3/program/soffice.bin -accept=socket,host=127.0.0.1,port=8101;urp; -env:UserInstallation=file:///tmp/.jodconverter_socket_host-127.0.0.1_port-8101 -headless -nocrashreport -nodefault -nofirststartwizard -nolockcheck -nologo -norestore


[root@alfpru1 bin]# pstree -a
  ├─java -Declipse.security -Dwas.status.socket=52934 -Dosgi.install.area=/opt/IBM/WebSphere/AppServer-Dosgi.configuration.area=/opt/IBM/WebSphe
  │   ├─soffice.bin -accept=socket,host=127.0.0.1,port=8101;urp; -env:UserInstallation=file:///tmp/.jodconverter_socket_host-127.0.0.1_port-8101-headless
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   └─{soffice.bin}

Pero, en muchas ocasiones se dejan activados ambos sistemas o simplemente se comentan las líneas que comienzan con ooo. ¿Que pasa entonces?, ¿es excluyente el uso de JODConverter con respecto al uso directo de OpenOffice.org?
Pues bien, una vez comentadas las dos líneas siguientes:
#ooo.exe=soffice
#ooo.enabled=false

El resultado ha sido el siguiente:

[root@alfpru1 bin]# ps -fea | grep openoffice
root     24843 24510  0 12:55 pts/0    00:00:00 /usr/lib/openoffice.org3/program/soffice.bin -accept=socket,host=127.0.0.1,port=8101;urp; -env:UserInstallation=file:///tmp/.jodconverter_socket_host-127.0.0.1_port-8101 -headless -nocrashreport -nodefault -nofirststartwizard -nolockcheck -nologo -norestore
root     24851 24818  0 12:55 pts/0    00:00:00 /usr/lib/openoffice.org3/program/soffice.bin -accept=socket,host=127.0.0.1,port=8100;urp;StarOffice.ServiceManager -env:UserInstallation=file:///opt/alf343WAS7_data_cluster/oouser -nologo -headless -nofirststartwizard -nocrashrep -norestore


[root@alfpru1 bin]# pstree -a

  ├─java -Declipse.security -Dwas.status.socket=55052 -Dosgi.install.area=/opt/IBM/WebSphere/AppServer-Dosgi.configuration.area=/opt/IBM/WebSphe
  │   ├─soffice /usr/bin/soffice -accept=socket,host=127.0.0.1,port=8100;urp;StarOffice.ServiceManager -env:UserInstallation=file:///opt/alf343WAS7_data_cluster/oouser …
  │   │   └─soffice.bin -accept=socket,host=127.0.0.1,port=8100;urp;StarOffice.ServiceManager-env:UserInstallation=file:///opt/alf343WAS7_data_cluster/oouser
  │   │       ├─{soffice.bin}
  │   │       ├─{soffice.bin}
  │   │       ├─{soffice.bin}
  │   │       ├─{soffice.bin}
  │   │       ├─{soffice.bin}
  │   │       └─{soffice.bin}
  │   ├─soffice.bin -accept=socket,host=127.0.0.1,port=8101;urp; -env:UserInstallation=file:///tmp/.jodconverter_socket_host-127.0.0.1_port-8101-headless
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   ├─{soffice.bin}
  │   │   └─{soffice.bin}

Como se observa, efectivamente NO SON EXCLUYENTES, con lo que hay que forzar la activación del método que se desea, desactivando el que no corresponda de forma explícita.

Alfresco 3.4.x ECM, WAS 7 y JGroups

Cuando se configuran tanto Alfresco ECM como IBM Websphere Application Server 7 (WAS7) en clúster, pueden presentarse algunos problemas o dificultades propias de este particular servidor de aplicaciones, entre estos está la configuración de JGroups.

La configuración es la siguiente:

2 nodos llamados alfpru1 (IP: 192.168.56.101/24) y alfpru2 (IP: 192.168.56.102/24).

En alfpru1:

– Está instalado MySQL con acceso para ambos nodos.
– Los recursos compartidos por NFS del repositorio y las configuraciones.
– WebSphere Application Server 7.0.17 (WAS7) con el Deploy Manager instalado.

En alfpru2:

– Websphere Application Server  7.0.17 (WAS7)

En la configuración del WAS7 se han creado dos servidores (alfpru1Cluster y alfpru2Cluster) que son miembros del cluster (alfrescoCluster).

La configuración del alfresco-global.properties es:

# Repository and indexes
#
dir.shared.root=/opt/alf343WAS7_data_cluster
dir.local.root=/opt/alf343WAS7_index_cluster
dir.root=${dir.shared.root}
dir.contentstore=${dir.shared.root}/contentstore
dir.contentstore.deleted=${dir.shared.root}/contentstore.deleted
dir.auditcontentstore=${dir.shared.root}/audit.contentstore
dir.indexes=${dir.local.root}/lucene-indexes
dir.indexes.backup=${dir.local.root}/backup-lucene-indexes
dir.indexes.lock=${dir.indexes}/locks
#
# Cluster config
#
alfresco.cluster.name=AlfpruClusterWAS7
alfresco.jgroups.defaultProtocol=TCP
alfresco.tcp.initial_hosts=alfpru1[7800],alfpru2[7800]
#
# Audit config
#
audit.enabled=false
audit.useNewConfig=false
#
# Tuning config
#
db.pool.initial=50
db.pool.min=50
db.pool.max=275
db.pool.idle=-1
hibernate.jdbc.fetch_size=150
#
# Tools config
#
jodconverter.officeHome=/usr/lib/openoffice.org3
jodconverter.portNumbers=8101
jodconverter.enabled=true
img.root=/usr
swf.exe=/usr/bin/pdf2swf
#
# Hibernate
#
hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
#
# Lucene indexes config
#
index.recovery.mode=AUTO
index.tracking.cronExpression=0/5 * * * * ?
index.tracking.reindexLagMs=5000
#
# For WAS config
#
mbean.server.locateExistingServerIfPossible=false

El problema que puede presentarse ahora es que salga un error de este tipo:

[8/4/11 10:56:41:865 CEST] 0000001b ContextLoader E org.springframework.web.context.ContextLoader initWebApplicationContext Context initialization failed
                                 org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘propertyBackedBeanExporter’ defined in file [/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/installedApps/alfpru1Cell01/Alfresco.ear/alfresco.war/WEB-INF/classes/alfresco/enterprise/repository-jmx-context.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.alfresco.enterprise.repo.management.subsystems.PropertyBackedBeanExporter]: Constructor threw exception; nested exception is org.alfresco.error.AlfrescoRuntimeException: 07040000 Failed to initialise JGroups channel:
   Cluster prefix:    AlfpruClusterWAS7
   App region:        org.alfresco.enterprise.repo.management.subsystems.PropertyBackedBeanExporter
   Channel:           org.jgroups.JChannel@48a648a6
   Configuration URL: file:/opt/IBM/WebSphere/AppServer/profiles/AppSrv01/installedApps/alfpru1Cell01/Alfresco.ear/alfresco.war/WEB-INF/classes/alfresco/jgroups/alfresco-jgroups-TCP.xml
[…]
Caused by: org.jgroups.ChannelException: failed to start protocol stack
[…]
Caused by: java.net.BindException: No available port to bind to

Este error es debido a que en el inicio de JGroups la dirección IP no es encontrada (o hay algún tipo de conflicto)

Para solucionar esto hay varias formas, pero la más sencilla es incluir el valor de jgroups.bind_addr para cada servidor directamente en los parámetros de inicio de la máquina virtual de Java. Para ello en WAS7 la introducimos en:

Servidores de aplicaciones de WebSphere/alfpru1Cluster/Java y gestión de procesos/Definición de proceso/Máquina virtual de java/Argumentos de JVM genéricos el valor: -Djgroups.bind_addr=alfpru1

Y en:

Servidores de aplicaciones de WebSphere/alfpru2Cluster/Java y gestión de procesos/Definición de proceso/Máquina virtual de java/Argumentos de JVM genéricos el valor: -Djgroups.bind_addr=alfpru2

Con esto se soluciona este error de manera sencilla y tendremos funcionando un clúster WAS7 + un clúster JGroups en Alfresco ECM

Para más información:
Alfresco Docs (Installing Alfresco in WebSphere)
Alfresco Docs (Configuring JGroups)
The JGroups 3.x Tutorial

Trucos para mantener un cluster en WebSphere 7 con Alfresco 3.4.x

Para instalar Alfresco ECM 3.4.x en WebSphere 7, lo mejor es seguir el siguiente enlace: http://docs.alfresco.com/3.4/index.jsp?topic=%2Fcom.alfresco.Enterprise_3_4_0.doc%2Ftasks%2Falf-websphere-install.html

Si además creamos un clúster de nodos en el mismo WebSphere además de la configuración para Alfresco ECM mediante JGroups, tenemos todavía algunas cosas que el servidor de aplicaciones no controla. Una de ellas es el control de los ficheros de configuración, comunmente llamados “ficheros del extension” o extension/alfresco.

Esto es fácil si compartimos mediante NFS estos ficheros. Imaginemos que tenemos dos servidores, alfpru1 y alfpru2, en ambos hay una instalación de WAS 7 en: /opt/IBM/WebSphere/AppServer

En esta existe un directorio llamado lib donde generalmente se crea un directorio llamado alfresco y dentro de este se introducen los ficheros de configuración. Bien, solo hay que exportar el recurso en la máquina o cabina, en este caso es la misma máquina alfpru1 y en la misma localización por defecto (lo suyo es tener evidentemente una cabina de discos para esto):

El fichero /etc/exports tendrá la siguiente línea:

/opt/IBM/WebSphere/AppServer/lib/alfresco       alfpru2(rw,sync)

Además deberá exportar el recurso compartido para el repositorio, etc.

Bien, ahora en alfpru2 creamos un directorio alfresco dentro de /opt/IBM/WebSphere/AppServer/lib y montamos el recurso como:

mount alfpru1:/opt/IBM/WebSphere/AppServer/lib/alfresco /opt/IBM/WebSphere/AppServer/lib/alfresco

Y listo!!… bueno, no del todo. Resulta que hay un fichero llamado alfresco-global.properties que está en el mismo nivel que el directorio alfresco. Como no queremos (ni podemos) exportar todo el directorio lib, lo que se puede hacer es mover este fichero dentro de lib y crear dos enlaces simbólicos, uno en cada servidor, de esta forma:

En alfpru1:

mv alfresco-global.properties ./alfresco
ln -s alfresco/alfresco-global.properties .

En alfpru2 (después de haber montado el recurso):

ln -s alfresco/alfresco-global.properties .

Para prácticamente toda la configuración vale que se compartan los ficheros, en algún caso excepcional necesitaremos otros valores, en el caso además de subsistemas podrémos realizar este truco también de forma que parte esté como recurso compartido y parte no.

Alfresco Team… por fin Alfresco para PYMES

Ayer salió la versión de Alfresco Team, es la versión 3.5 pero esta vez adaptada a PYMES. Con un precio más que reducido para 10 usuarios (o bien 5 en la versión Trial) es la nueva apuesta de Alfresco Software para entrar en las pequeñas y medianas empresas. Por cierto, se puede bajar e instalar o contratar su servicio “en la nube”.

He probado la versión para Windows 64bits en un Windows 7 y la instalación ha ido perfectamente, el arranque (mediante servicio de Windows) se ha creado de forma satisfactoria y ha instalado un PostgreSQL, si, en lugar de MySQL… 🙂

La documentación podemos encontrarla en http://docs.alfresco.com/3.5/index.jsp y su página web oficial es http://team.alfresco.com/

La versión “Free” como digo, tiene la limitación de 5 usuarios como máximo y 500 documentos. Para una limitadísima oficina vamos, pero parece un buen comienzo 😉

Ahora, seguramente nuestra ficticia aseguradora FEGOR podrá desarrollar todo su pontencial sin invertir demasiado y sin tener una versión sin soporte como era la Community.

¿Replica la información de sesión Alfresco ECM en clúster?

Una de las críticas que más recibe Alfresco es que este no clona/replica las sesiones y variables asociadas, como por ejemplo la variable alf_ticket, por lo que en aplicaciones que llaman a nodos de un clúster de Alfresco se produce un error de autenticación.


Pero ¿es esto cierto?… bueno, creo que en parte si pero en parte no y por eso he realizado unas pruebas.

Una solución realizada hasta ahora era la de configurar en el balanceador lo que se llaman sesiones “sticky” que usan una variable de sesión como JSESSIONID para dirigir todas las peticiones de esa sesión al mismo nodo. De esta forma, al ser siempre el mismo nodo quien recibe las peticiones y tener la información del ticket de validación correcta no hay problemas.

Esta solución es válida para aplicaciones que usan sesiones, pero ¿que ocurre si una aplicación no las usa?, como el balanceador no sabe a donde dirigirlo porque no tiene esta información puede dirigirlo siempre al mismo nodo, lo que sería un mal menor, o redirigir a uno u otro nodo indistintamente, lo que provoca el error.

Bien, vamos a comprobar que Alfresco ECM versión 3.3.4 si clona al menos la información del ticket de autenticación para que cuando se entra en cualquier nodo usando un ticket de validación válido, Alfresco permite la entrada.

Lo primero es configurar correctamente el cluster de Alfresco ECM. En mi caso esta es la información de alfresco-global.properties:

alfresco.cluster.name=alfprucluster
alfresco.jgroups.defaultProtocol=TCP
alfresco.tcp.initial_hosts=alfpru1[7800],alfpru2[7800]

Además de renombrar el fichero ehcache-custom.xml.sample.cluster como ehcache-custom.xml

Creamos el fichero para probar el acceso entre los nodos usando la variable alf_ticket y sin usarla (o usando si quieremos una ficticia no válida)

Fichero: prueba_auth_cluster.sh

#!/bin/bash

ALF_USER=admin
ALF_PASSWD=admin
ALF_NODE1=alfpru1:8080
ALF_NODE2=alfpru2:8080
ALF_SEARCH_TERM=readme.ftl
ALF_ROOT=Company%20Home

echo “AUTENTICACION Y RECOGIDA DEL TICKET EN EL PRIMER NODO.”
ALF_TICKET=`curl “http://${ALF_NODE1}/alfresco/service/api/login?u=${ALF_USER}&pw=${ALF_PASSWD}” | grep TICKET_ | sed ‘s:::g’ | sed ‘s:::g’ | tr -d ‘r’`

echo “BUSCAR EN EL PRIMER NODO USANDO EL TICKET DE AUTENTICACION DEL PRIMER NODO.”
curl “http://${ALF_NODE1}/alfresco/service/api/search/keyword.text?q=${ALF_SEARCH_TERM}&p=${ALF_ROOT}&c=1&l=es&alf_ticket=${ALF_TICKET}”

echo “BUSCAR EN EL SEGUNDO NODO USANDO EL TICKET DE AUTENTICACION DEL PRIMER NODO.”
curl “http://${ALF_NODE2}/alfresco/service/api/search/keyword.text?q=${ALF_SEARCH_TERM}&p=${ALF_ROOT}&c=1&l=es&alf_ticket=${ALF_TICKET}”

echo “BUSCAR EN EL SEGUNDO NODO SIN USAR EL TICKET DE AUTENTICACION DEL PRIMER NODO.”
curl “http://${ALF_NODE2}/alfresco/service/api/search/keyword.text?q=${ALF_SEARCH_TERM}&p=${ALF_ROOT}&c=1&l=es”

Una vez configurado todo y arrancados ambos nodos de Alfresco, ejecutamos el script.

La salida en mi caso ha sido la siguiente:

./prueba_auth_cluster.sh
AUTENTICACION Y RECOGIDA DEL TICKET EN EL PRIMER NODO.
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   106    0   106    0     0     10      0 –:–:–  0:00:10 –:–:–    31
BUSCAR EN EL PRIMER NODO USANDO EL TICKET DE AUTENTICACION DEL PRIMER NODO.
readme.ftl
BUSCAR EN EL SEGUNDO NODO USANDO EL TICKET DE AUTENTICACION DEL PRIMER NODO.
readme.ftl
BUSCAR EN EL SEGUNDO NODO SIN USAR EL TICKET DE AUTENTICACION DEL PRIMER NODO.
Apache Tomcat/6.0.18 – Informe de Error

Estado HTTP 401 –


type Informe de estado

mensaje

descripci�n Este requerimiento requiere autenticaci�n HTTP ().


Apache Tomcat/6.0.18

Que demuestra efectivamente que con el ticket obtenido en la autenticación del primer nodo, nos sirve para usarlo con el segundo. Además se realiza otra llamada sin usar el ticket para afirmar esta prueba ya que de esta forma sí debe salir un error.

Ahora vamos a eliminar el clúster, de forma que no se comuniquen los nodos entre sí y no pasen la información. Para ello quitamos las líneas de configuración del clúster de alfresco-global.properties y volvemos a ejecutar el script, el resultado, obviamente será el siguiente:

./prueba_auth_cluster.sh

AUTENTICACION Y RECOGIDA DEL TICKET EN EL PRIMER NODO.
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   106    0   106    0     0    182      0 –:–:– –:–:– –:–:–     0
BUSCAR EN EL PRIMER NODO USANDO EL TICKET DE AUTENTICACION DEL PRIMER NODO.
readme.ftl
BUSCAR EN EL SEGUNDO NODO USANDO EL TICKET DE AUTENTICACION DEL PRIMER NODO.
Apache Tomcat/6.0.18 – Informe de Error

Estado HTTP 401 –


type Informe de estado

mensaje

descripci�n Este requerimiento requiere autenticaci�n HTTP ().


Apache Tomcat/6.0.18

BUSCAR EN EL SEGUNDO NODO SIN USAR EL TICKET DE AUTENTICACION DEL PRIMER NODO.
Apache Tomcat/6.0.18 – Informe de Error

Estado HTTP 401 –


type Informe de estado

mensaje

descripci�n Este requerimiento requiere autenticaci�n HTTP ().


Apache Tomcat/6.0.18

Por tanto, como puede observarse Alfresco ECM sí funciona correctamente como un clúster. Hay que indicar también que sigue siendo necesario (a mi entender) el uso de las Sticky Sessions para controlar mejor el tráfico a través del balanceador.

También se ha probado a recoger el ticket del primer nodo, parar este nodo y acceder con el resultado del ticket al segundo funcionando correctamente también.

Pero ¿que pasa entonces con las interfaces web?, bien, tanto el Explorer como Share, así como todas las interfaces que se desarrollen deberán controlar al menos el uso de la variable alf_ticket para que ninguno de los nodos reciba un ticket de validación erróneo o no lo reciba ya que de esta forma volverá a solicitar el inicio de sesión. Esto no es un problema del clúster en sí, si no de las interfaces de usuario y como he podido comprobar, en este caso ambas fallan cuando no tienen activadas las Sticky Sessions porque al parecer no pasan entre las peticiones (GET y POST) la variable alf_ticket. Según Alfresco, en la versión 3.4.3 esto estará solucionado.

NOTA: He usado el WebScript de búsqueda y que realiza una salida en texto de mi post http://www.fegor.com/2011/05/calculando-metricas-en-alfresco.html al que he declarado como autenticado mediante usuario user”.

Alfresco, WebDAV y WebSphere

WebDAV (Web-based Distributed Authoring and Versioning) es un protocolo que implementa Alfresco para poder conectar unidades y/o recursos compartidos y poder subir, modificar y borrar documentos de una forma sencilla.

Este protocolo además es bastante simple y se basa en comunicaciones vía HTTP y HTTPS con lo que es además muy sencillo de configurar entre balanceadores y firewalls. No obstante presenta algunos problemas cuando se conecta a través de Windows.

Uno de ellos es la codificación, Windows usa generalmente la ISO-8859-15 (en España) y Alfresco usa generalmente UTF-8 a nivel de Java y sobre todo si está instalado en máquinas Linux. Este problema de codificación cambia caracteres con tildes, eñes, etc. por lo que hay que prestar atención a la configuración y establecer todo al mismo sistema de codificación para evitar sorpresas.

Otro problema que me he encontrado en un cliente ha sido la conexión directamente con el servidor desde las estaciones de trabajo tanto Windows XP como Windows 7.

Realizando pruebas con un Apache 2.2 y mod_proxy_balancer hacia dos máquinas virtuales con Alfresco 3.3.4 no he tenido problemas siempre y cuando se sigan las indicaciones de los siguientes enlaces:

http://wiki.alfresco.com/wiki/File_Server_Configuration
http://wiki.alfresco.com/wiki/Client_WebDAV
http://support.microsoft.com/kb/841215
http://support.microsoft.com/kb/912152
http://www.webdavsystem.com/server/documentation/authentication/basic_auth_vista

Básicamente es actualizar el software del cliente WebDAV así como activar la autenticación básica tanto en plano como por SSL usando el registro de Windows.

Ejecutar regedit.exe

En Windows XP, crear un valor DWORD en HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesWebClientParameters llamado UseBasicAuth con el valor a 1.

En Windows 7, crear un valor DWORD en HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesWebClientParameters llamado BasicAuthLevel con el valor a 2.

Una vez realizados estos cambios solo hay que reiniciar el servicio cliente de WebDAV, reiniciando la máquina o parando e iniciando el servicio como:

NET STOP webclient
NET START webclient

Hasta aquí todo correcto, en las máquinas virtuales de laboratorio todo fue correctamente, pero en el cliente seguía habiendo problemas cuando se intentaba conectar con Windows XP, no así con Windows 7, ¿cual era la diferencia básica?

En el cliente, los accesos se realizan hacia un balanceador hardware que a su vez dirige las peticiones a sendos IBM HTTP Servers que no son más que un Apache modificado y estos se conectaban a los nodos de Alfresco en clúster (estos están en WAS o IBM WebSphere Application Server).

Para el balanceo de los servidores IBM HTTP Server se usa un plugin propio. Este se configura a través de un fichero llamado plugin-cfg.xml

En el siguiente enlace hay una descripción de los parámetros y valores posibles: http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/rwsv_plugincfg.html

En su configuración, el parámetro AcceptAllContent está desactivado y su misión es la de si permite que los usuarios puedan incluir o no el contenido en las peticiones POST, PUT, GET, y HEAD cuando la cabecera de petición incluye una cabecera de longitud del contenido o de codificación de la transferencia.

Se puede especificar True para leer todas las peticiones o False en la que se espera sólo el contenido y sólo para las peticiones POST y PUT.

Poniendo este valor a True todo se ha solucionado y ahora los Windows XP (SP1, SP2 y SP3) pueden conectarse sin ningún problema.

Agradezco la ayuda aportada a la solución de este problema a Roberto Herrero Guindal, experto en WebSphere del departamento de sistemas del cliente que ha tenido este problema.

Calculando métricas en Alfresco mediante scripts

Para el cálculo de métricas en las búsquedas, subidas de ficheros, etc. podemos usar alguno de los programas que existen para estas tareas como es JMeter.

JMeter es un programa en Java, del proyecto Apache, que permite testear aplicaciones web. Es una aplicación de escritorio y muy usada en los entornos en los que se utilizan servidores de aplicaciones, sobre todo Tomcat.

Para más información podemos ir a la URL del proyecto: http://jakarta.apache.org/jmeter/

Si no tenemos tiempo de crear una batería de pruebas siempre podemos recurrir al intérprete BASH de Linux, Unix, MacOS, etc. que puede facilitarnos esta tarea.

Haciendo uso de algún WebScript ya implementado en Alfresco o modificando alguno de estos podemos usar las funciones y comandos de BASH para tomar tiempos.

Búsqueda aleatoria:

Una de las pruebas más importantes es el de las búsquedas en Alfresco. En este caso vamos a usar un WebScript de la parte OpenSearch que tiene Alfresco. El problema de este sistema es que solo devuelve los resultados en 3 formatos, HTML, RSS y ATOM, y en nuestro caso necesitamos que los devuelva en formato texto (TEXT) para poder tratarlo. Para esto, solo tenemos que modificar el descriptor para incluir en el contexto la llamada a una plantilla FreeMarker que devuelva texto plano e incluir esta plantilla. Para no “ensuciar” el despliegue lo haremos creando el directorio en alfresco/extension de forma que además podamos conservarlo entre distintas actualizaciones del producto. El sitio para introducir los dos ficheros será: /alfresco/extension/templates/webscripts/org/alfresco/repository/

Los dos ficheros serán los siguientes:

Fichero: keywordsearch.get.desc.xml


  Alfresco Keyword Search (OpenSearch Enabled)
  Execute Keyword Search against Alfresco Repository (Company Home and below)
  /api/search/keyword.html?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /search/keyword.html?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /api/search/keyword.texp?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /search/keyword.text?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /api/search/keyword.atom?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /search/keyword.atom?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /api/search/keyword.rss?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /search/keyword.rss?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  /api/search/keyword.portlet?q={searchTerms}&p={startPage?}&c={count?}&l={language?}
  guest
  required

Fichero: keywordsearch.get.text.ftl 

${row.name}

Una vez introducidos los ficheros en el sitio correspondiente solo queda ir a la URL http://servidoralfresco:8080/alfresco/service/index y pulsar el botón de refresco de los WebScripts.

Ahora podemos ejecutar el script creado para las búsquedas. Este script lo que hace es una búsqueda por una palabra o KEYWORD, guardar los resultados en un fichero y después realizar el número de búsquedas solicitadas de forma automática guardando los tiempo y redirigiéndolos a un fichero para poder editar  posteriormente los resultados.

La sintaxis del script es:

./random_search usuario password keyword núm_iteracciones url_alfresco

Fichero: random_search

#!/bin/bash

# Pruebas de busquedas en Alfresco usando el webscript “search”
# www.fegor.com

if [ ! $# -eq 5 ]
then
        echo Sintaxis: random_search user passwd keyword num_randoms alfresco_url
        exit 1
fi

echo Realizando consulta completa…
IFSPREV=$IFS
IFS=$’x0A’$’x0D’
curl –user “$1:$2” “$5/service/api/search/keyword.text?q=$3&p=Company%20Home&c=10000&l=es” > random_search_result.txt

echo Comenzando las busquedas…
LINEAS=`cat random_search_result.txt | wc -l`
echo -e “Results of random_search.sh” > random_search_times.csv SG_FOR_I=`date +%s`
for n in $(seq 1 $4);
do
        RNM=$RANDOM
        let “RNM %= $LINEAS”
        let “RNM += 1”
        KEYWORD=`head -n $RNM random_search_result.txt | tail -1 | tr -d ‘r’`
        NS_CURL_I=`date +%s%N`
        curl –user “$1:$2” “$5/service/api/search/keyword.text?q=${KEYWORD}&p=Company%20Home&c=1&l=es”
        NS_CURL_F=`date +%s%N`
        let TOTAL_NS_CURL=$NS_CURL_F-$NS_CURL_I
        let TOTAL_MS_CURL=TOTAL_NS_CURL/1000000
        echo -e “`date`t$KEYWORDt$TOTAL_NS_CURLt$TOTAL_MS_CURL” >> random_search_times.csv 

done
SG_FOR_F=`date +%s`
let TOTAL_SG_FOR=$SG_FOR_F-$SG_FOR_I
echo -e “rrTotal time” >> random_search_times.csv
echo -e “`date`t$TOTAL_SG_FOR” >> random_search_times.csv
IFS=$IFSPREV

Un ejemplo de ejecución del comando sería:

[root@alfpru1 scripts]# ./random_search admin admin “*ftl” 5 http://192.168.56.1:8080/alfresco
Realizando consulta completa…
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   671    0   671    0     0   7910      0 –:–:– –:–:– –:–:–     0
Comenzando las busquedas…
categorysearch.get.atom.404.ftl
show_audit.ftl
categorysearch.get.atom.ftl
my_docs.ftl
general_example.ftl

Y posteriormente en la hoja de cálculo:

Subidas masivas:

Otra de las pruebas es la de realizar subidas masivas y en profundidad de un número de ficheros y comprobar los tiempos de estas subidas. El script es bastante parecido al anterior y hace uso de un WebScript que también está incluido en Alfresco. En este caso hay que crear un “site” llamado “tests” para poder subir los ficheros a dicho espacio.

Fichero: directory_upload
#!/bin/bash

# Pruebas de subidas en Alfresco usando el webscript “upload”
# www.fegor.com

if [ ! $# -eq 5 ]
then
        echo Sintaxis: directory_upload user passwd path ext alfresco_url
        exit 1
fi

echo “Subiendo…”
IFSPREV=$IFS
IFS=$’x0A’$’x0D’

echo -e “Results of directory_upload.sh” > directory_upload_times.csv
SG_FOR_I=`date +%s`
for f in $(find $3 ( -name *.$4 ));
do
        NS_CURL_I=`date +%s%N`
        curl -k -X POST –user “$1″:”$2″ -F filedata=@$f -F siteid=”tests” -F containerid=”documentLibrary” -F uploaddirectory=”testsDir” -F filename=”`basename $f`” -F contenttype=”`file –brief –mime $f`” “$5/service/api/upload” | grep ‘description’ | cut -d ‘:’ -f 2 | tr -d ‘”‘
        NS_CURL_F=`date +%s%N`
        let TOTAL_NS_CURL=$NS_CURL_F-$NS_CURL_I
        let TOTAL_MS_CURL=TOTAL_NS_CURL/1000000
        echo -e “`date`t$ft$TOTAL_NS_CURLt$TOTAL_MS_CURL” >> directory_upload_times.csv
done
SG_FOR_F=`date +%s`
let TOTAL_SG_FOR=$SG_FOR_F-$SG_FOR_I
echo -e “rrTotal time” >> directory_upload_times.csv
echo -e “`date`t$TOTAL_SG_FOR” >> directory_upload_times.csv
IFS=$IFSPREV

Este script también devuelve un fichero (directory_upload_times.csv) para su edición y estudio mediante cualquier sistema de hoja de cálculo como MS-Excel o OpenOffice.org

Un ejemplo de ejecución podría ser el siguiente:

[root@alfpru1 scripts]# ./directory_upload admin admin AMCM2011/ pdf http://192.168.56.1:8080/alfresco
Subiendo…
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 3252k  100   278  100 3252k    253  2968k  0:00:01  0:00:01 –:–:– 3014k
 File uploaded successfully
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  772k  100   253  100  772k    424  1295k –:–:– –:–:– –:–:– 1435k
 File uploaded successfully
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  311k  100   257  100  311k    414   502k –:–:– –:–:– –:–:–  510k
 File uploaded successfully
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2485k  100   251  100 2485k    385  3814k –:–:– –:–:– –:–:– 3976k
 File uploaded successfully
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  415k  100   251  100  415k    245   406k  0:00:01  0:00:01 –:–:–  429k
 File uploaded successfully
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  481k  100   255  100  481k    443   836k –:–:– –:–:– –:–:–  859k
 File uploaded successfully
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 2471k  100   249  100 2471k    242  2402k  0:00:01  0:00:01 –:–:– 2444k
 File uploaded successfully

… procesando los datos: