Translate

jueves, 26 de mayo de 2011

Bug: No se ven las categorías

Me he encontrado más de una vez que al actualizar de versión de Alfresco han habido problemas con perdidas de permisos. En este caso voy a comentar cuando esto pasa con las categorías.

Cuando un usuario, que no es el admin, intenta navegar por categorías se encuentra con esto:



Lo que ha pasado es que durante el upgrade se han perdido los permisos del grupo EVERYONE. Como podemos comprobarlo?
Abriendo el buscador de nodos y realizar la siguiente consulta XPath: cm:categoryRoot

Nos debería dar dará un único resultado con el primary path:

{http://www.alfresco.org/model/content/1.0}categoryRoot)

Si vamos a ver los permisos del nodo y lo comparamos con los valores que debería tener veremos lo siguiente:


A la izquierda tendriamos los valores correctos y a la derecha vemos cómo han desaparecido estos permisos.

Podremos solucionarlo de forma sencilla a través de JavaScript, crearemos un fichero con el siguiente contenido:


nodes = search.xpathSearch("/cm:categoryRoot/cm:generalclassifiable");
generalclassNode = nodes[0];
generalclassNode.setPermission("Read");

y lo copiaremos dentro del Diccionario de datos en el espacio Scripts. Ahora ejecutaremos el script desde los detalles del fichero con la opción Ejecutar acción -> Ejecutar un script

Depués de esto nos deberían volver a aparecer los permisos

Backup en Alfresco

Una de las tareas más importantes del administrador de Alfresco es planificar una correcta estrategia de backup

En Alfresco nos encontramos con diferentes elementos sobre los que hacer backup:

Datos estáticos:
  • Servidor de aplicaciones
  • Ficheros de configuración  y personalización de Alfresco
Datos dinámicos:
  • Almacen de datos.
  • Índices.
  • Metadatos.
Vistas las dependencias que hay entre ellos en el articulo de arquitectura se entiende que no es posible restaurar Alfresco si no se dispone de la base de datos o del almacén. El caso de los índices es caso a parte puesto que es posible regenerarlos teniendo los otros dos elementos, pero este proceso puede llegar a ser costoso en tiempo dependiendo del volumen de datos (puede llegar al orden de días si el volumen es grande).

El almacen de datos es el directorio al que apunta la propiedad dir.root
Los índices están ubicados en el directorio apuntado por lucene-indexes aunque estos no deben ser copiados para evitar tener datos corruptos. En su lugar se debe copiar el directorio backup-lucene-indexes el cual es una copia que se realiza de forma automática a las 03:00 am.
Es importante tener en cuenta que la copia de este directorio debe producirse preferentemente después de esta hora. Si es necesario es posible cambiar la hora de este job en el fichero scheduled-jobs-context.xml:

    <bean id="indexBackupTrigger" class="org.alfresco.util.CronTriggerBean">
        <property name="jobDetail">
            <ref bean="indexBackupJobDetail" />
        </property>
        <property name="scheduler">
            <ref bean="schedulerFactory" />
        </property>
        <!-- trigger at 3am each day -->
        <property name="cronExpression">
            <value>0 0 3 * * ?</value>
        </property>
    </bean>

La base de datos debe ser copiada con las herramientas que proporcione el fabricante o terceros.

De esta forma el backup se podría resumir en:
  1. Hacer backup de backup-lucene-indexes
  2. Hacer backup de base de datos
  3. Hacer backup de la ubicación del almacen de datos


Es muy importante realizar las copias de seguridad en el orden indicado arriba por varios motivos, imaginemos que se sube un fichero durante la copia:
  • Si se crea una entrada en la base de datos después de realizar el backup de índices se puede regenerar el índice de la última transacción realizada con la reindexación en modo AUTO.
  • Si tenemos un fichero que existe en base de datos pero no en el sistema de ficheros tendremos un nodo huerfano. Aparecería en Alfresco como si existiera pero tendriamos un error al intentar abrirlo.
  • Si existe en el sistema de ficheros pero no en base de datos tendriamos un fichero sin referenciar pero mantendriamos la coherencia de los datos. 
Restauración completa

Los pasos que deberemos seguir para recuperar una copia completa serán:
  1. Parar Alfresco
  2. Mover dir.root a otra ubicación si es posible/necesario
  3. Mover backup-lucene-indexes a lucene-indexes, si la copia automática es buena se puede utilizar la misma que esté en el servidor, en caso contrario se tendrá que usar la del último backup
  4. Restaurar la base de datos
  5. Arrancar Alfresco
Si tenemos una copia valida  de backup-lucene-indexes y la propiedad index.recovery.mode=AUTO se generaran los indices de los elementos subidos después de la última copia ahorrandonos mucho tiempo en el arranque.


Restauración de un documento


Si queremos recuperar un documento que ya hemos eliminado de la papelera, es posible recuperarlo a partir de una consulta a la base de datos que nos indique cual es la ubicación en el sistema de ficheros del documento. Recordatorio del proceso de borrado de ficheros en Alfresco

Para ello deberemos recuperar la base de datos completa desde la última copia y a continuación ejecutar la siguiente consulta, que en mi caso va a recuperar el fichero 'trashcleaner-0.0.2.amp':

select a.node_id,
a.string_value,
e.content_url
from
alf_node_properties a,
alf_qname b,
alf_content_data c ,
alf_node_properties aa,
alf_content_data d,
alf_content_url e
where
a.string_value='trashcleaner-0.0.2.amp' and
a.qname_id=b.id and
b.local_name='name' and
c.id=aa.long_value and
aa.node_id=a.node_id and
aa.long_value=d.id and
d.content_url_id=e.id;

Esto nos devuelve el id del nodo, el nombre del fichero y la ubicación en disco:

+---------+------------------------+------------------------------------------------------------------+
| node_id | string_value           | content_url                                                      |
+---------+------------------------+------------------------------------------------------------------+
|     551 | trashcleaner-0.0.2.amp | store://2011/5/26/10/14/5579b1ba-a7f7-4a5e-a5d2-e5c1b27afe3c.bin |
+---------+------------------------+------------------------------------------------------------------+


podremos encontrar el contenido en el directorio:
alf_data/contentstore/2011/5/26/10/14/5579b1ba-a7f7-4a5e-a5d2-e5c1b27afe3c.bin

Si esta consulta nos devolviera varios valores porque tuvieramos el fichero en varios espacios podríamos mirar el espacio del que cuelga con la siguiente consulta buscando por el identificador del nodo:

select c.string_value
from
alf_child_assoc a,
alf_qname b,
alf_node_properties c,
alf_qname bb
where
child_node_id=551 and
a.type_qname_id=b.id and
b.local_name='contains' and
a.parent_node_id=c.node_id and
c.qname_id=bb.id and
bb.local_name='name';

+--------------+
| string_value |
+--------------+
| Sistemas     |
+--------------+
1 row in set (0.00 sec)

Para consultas más complejas, como por ejemplo filtrar por tipo de contenido, o documentos versionados y demás recomiendo la descarga de Mysql Workbench para sacar un Diagrama EER y poder ver de una forma visual las dependencias que existen entre cada una de las tablas.

La clave en este punto es que el la tabla alf_node_properties tiene un  long_value, el cual es el ID para una columna en la tabla alf_content_data que referencia a la tabla alf_content_url

Aqui teneis el diagrama para esta consulta:



    Introducción a Spring

    Voy a intentar explicar de una manera básica cual es el papel de Spring dentro de Alfresco desde el enfoque de sistemas para ayudar a comprender el proceso de configuración y de inicio de Alfresco.

    Spring es un framework de J2EE que pretende facilitar la programación y promover las buenas prácticas 

    IoC
    Estas son las siglas de Inversion de Control, que es un mecanismo muy popular en Spring y que permite mover fuera del código la configuración de objetos y ubicarla en ficheros de configuración. Esto se realiza típicamente a través de ficheros XML.

    Se le llama inversión de control porque en lugar de que la clase tome unos valores predeterminados dentro del código lo que hacemos es leer el fichero XML que contiene estos parámetros e instanciará la clase que esté configurada en el Bean con los valores definidos. De esta manera si se quiere cambiar el valor de un parámetro no es necesario recompilar código. Para los más curiosos al final pondré un ejemplo de como se definen estos ficheros.

    En Alfresco encontramos el inicio dentro del fichero web.xml

       <!-- Spring Application Context location -->
       <context-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/web-application-context.xml</param-value>
          <description>Spring config file location</description>
       </context-param>

    El cual hace a su vez un import del fichero application-context.xml
    .
    .
    <import resource="classpath:alfresco/application-context.xml" />
    .
    .

    Es precisamente este fichero el punto de inicio de cualquier aplicación  que usa el framework de Spring, así que en realidad cuando iniciamos el servidor de aplicaciones no estamos arrancando Alfresco en primera instancia, si no que iniciamos Spring y  este mediante la inversión de control y los ficheros de configuración va iniciando todos y cada uno de los servicios de Alfresco.

    El classpath alfresco hace referencia al directorio WEB-INF\classes\alfresco y se hace referencia normalmente como configRoot

    A partir de este punto se puede ver como se configura Alfresco a través de:

    – Referencia in-line a XML
    {configRoot}/alfresco/application-context.xml
    – Referencias a ficheros XML externos. Incluye la importación de todo el classpath *(:alfresco/extension/*-context.xml
    files) mediante el uso de wildcard.
    – Referencias a ficheros de propiedades externas.

    La ubicación del classpath extension depende del servidor de aplicaciones pero típicamente son :

    Tomcat: <TOMCAT_HOME>/shared/classes/alfresco/extension/
    Jboss: <JBOSS_HOME>/instancia/conf/alfresco/extension/

    Esto proporciona un método de carga de ficheros en 2 fases

    1. classpath://alfresco/*-context.xml
    2. classpath://alfresco/extension/*-context.xml
    Se realiza la carga de los ficheros de configuración ubicados en el configRoot y a continuación los ubicados en el directorio extension. En caso de conflicto se toma el último valor cargado. Esto es necesario para poder disponer de un mecanismo de actualización limpio ya que unos ficheros están ubicados dentro del directorio de despliegue y los otros están fuera. Debido a esto no es buena práctica hacer modificaciones dentro del configRoot y se deben hacer en el directorio extension.

    Otros ficheros importantes:

    - core-services-context.xml: Incluye la definición de beans del nucleo de Alfresco e incluye la importación del fichero repository.properties
    - repository.properties: Define las propiedades por defecto que toma alfresco como por ejemplo:

    dir.root: define la ubicación del contentstore y de los indices de lucene
    db.*: define usuario, contraseña y tipo de base de datos

    Vemos entonces que tenemos 2 lugares para realizar nuestra configuración y que además se importan ficheros con wildcards *.
    • En caso de conflicto tenemos que el orden de mayor a menor prioridad es:
      • cambiosJMX persistidos en la BBDD (sólo Enterprise y a partir de la versión 3.2)
      • <extensionRoot>/alfresco/extension/subsystems/<category>/<typeName>/*.properties
      • <extensionRoot>/alfresco/subsystems/<category>/default/*.properties
      • <extensionRoot>/alfresco-global.properties
      • <extensionRoot>/alfresco/module/*/alfresco-global.properties
      • <extensionRoot>/alfresco/repository.properties
    Y los últimos y con prioridad más baja serán los ubicados dentro del configRoot.

    Arquitectura de Alfresco (I)

    La arquitectura de Alfresco es la típica de una aplicación web basada en JAVA:


     
    Los usuarios acceden típicamente mediante un navegador a la interfaz de usuario de Alfresco (gestión documental) o Share (entorno colaborativo). Estas interfaces pueden variar en función de los componentes instalados (WCM,RM) para adecuarse a la lógica de estos.


    Todos los documentos subidos al repositorio de Alfresco con toda su información adicional (Metadatos) se persisten en la capa de almacenamiento según su naturaleza:
    • Contenido -> Sistema de Ficheros
    • Metadatos -> Base de datos
    Tal como muestra el diagrama vemos que Alfresco separa a nivel lógico el repositorio (lugar donde se almacenan nuestros documentos) de las aplicaciones que acceden a él (DM,RM,WCM,SHARE).

    A nivel físico tenemos únicamente 2 ficheros war:
    •  Alfresco.war (DM + Repositorio)
    •  Share.war (SHARE)
    A nivel lógico seria posible deshacernos de la interfaz de Alfresco y programar nuestra propia interfaz o acceder al repositorio a través de una tercera aplicación

    Esto es posible gracias a los Foundation Services de Alfresco, los cuales nos permiten acceder al contenido del repositorio y realizar acciones sobre él. Se dispone entre otros de servicios de autenticación, búsquedas, permisos, contenido..etc). El listado completos de los servicios lo podemos obtener dentro del fichero public-services-context.xml

    Estos servicios son la capa pública más baja de la API de Alfresco son publicados a través de una interfaz JAVA. Cada uno de ellos está mapeado a un componente que a modo de caja negra es capaz de ejecutar el código necesario para obtener los resultados de los servicios solicitados. Además disponemos de un registro de servicios que, a modo de listín, nos permite obtener los diferentes servicios disponibles.

    Dado que Alfresco usa el framework de Spring, cada uno de estos servicios y componentes está configurado mediante ficheros XML. En este artículo podeis encontrar cual es la estructura de estos ficheros y como funciona spring dentro de Alfresco.

    Las 3 acciones más típicas que realizaremos con Alfresco serán las de subir, bajar y buscar documentos. Estas acciones se realizan mediante 3 de estos servicios apoyandose también en otros elementos ya existentes y probados dentro del mundo Open Source como Hibernate o Lucene:
    - NodeService: En alfresco todos los elementos son tratados como nodos pero con diferentes propiedades. Por ejemplo, un fichero es un nodo con ciertas propiedades o metadatos como son el título, autor, fecha de creación e incluso el propio contenido no es más que un metadato. A su vez un espacio es también un nodo que tiene una asociación del tipo "contiene a" con otros nodos.
    El servicio encargado de trabajar con los nodos será el NodeService y toda la información sobre los nodos, se almacenará en base de datos.

    - ContentService: El contenido suele estar referido a los ficheros binarios que subimos o creamos dentro de Alfresco, es decir nuestro fichero Power Point, Word o PDF o cualquier tipo MIME que se nos ocurra. Estos ficheros binarios se almacenarán en el sistema de ficheros del servidor (contentstore) siguiendo una estructura de directorios ordenada por fecha.
    Este servicio se encargará de la leer o escribir el contenido en el repositorio asi como de transformarlo de un tipo MIME a otro.

    - SearchService: Cada vez que se sube contenido a Alfresco este es indexado de forma automática, tanto sus metadatos como el contenido de forma que no solo podemos buscar mediante el nombre del fichero o su autor si no también por el contenido.
    Este servicio utiliza por debajo Apache Lucene para el indexado y las consultas.
    Los indices generados por Lucene se almacenan también en el sistema de ficheros

    Parte de esta información se encuentra orginalmente en:
    http://wiki.alfresco.com/wiki/Alfresco_Repository_Architecture