Mostrando entradas con la etiqueta Alfresco. Mostrar todas las entradas
Mostrando entradas con la etiqueta Alfresco. Mostrar todas las entradas

viernes, 23 de mayo de 2014

Alfresco cambiará su política de precios

El popular gestor documental Alfresco había mantenido desde el principio una política de precios para su versión Enterprise basada en el número de CPUs o instancias donde se desplegaba el producto, indpendientemente del número de usuarios del sistema. Este tipo de licenciamiento lo hacía una opción muy competitiva tanto para pequeñas o medianas empresas como para grandes corporaciones que quisieran evitar el licenciamiento por usuario que utilizan casi todos los gestores documentales del mercado. Ahora esta situación va a cambiar ya que Alfresco va a licenciar su versión Enterprise basándose en el número de usuarios.

La opción más asequible, conocida como Alfresco One Departamental, está pensada para un máximo de 300 usuarios y no permitirá instalaciones en alta disponibilidad. Esta versión ofrecerá la misma funcionalidad que ofrece la versión actual, incluyendo un espacio de almacenaje en el cloud de Alfresco y el gestor de procesos Activiti. Esta versión tendrá un precio de 48.000€ anuales y no incluirá módulos como el Record Management, el Transformation Server o el próximo módulo de integración con Outlook que se pagarán a parte. Haciendo números, sale que cada usuario tendrá un coste de 160€ anuales, aunque se tendrá que pagar por los 300, independientemente de si la instalación de Alfresco vaya a usar dos usuarios o doscientos.


La segunda opción será conocida como Alfresco One Enterprise y estará pensada para un máximo de 1.000 usuarios y permitirá alta disponibilidad. El precio de esta versión será de 109.000€ e incluirá algún conector adicional y soporte 24x7. A partir de ahí, los usuarios adicionales se tendrán que contratar en bloques de 500.


Esta nueva política de precios, que entrará en vigor el 3 de Junio, no sólo implica un incremento de casi el 100% en el precio de la licencia más básica, sino que elimina una de las ventajas más competitivas que tenía Alfresco, que era la posibilidad de tener un número ilimitado de usuarios manteniendo estable el coste de la plataforma. Sin duda se trata de un movimiento arriegado por parte de Alfresco que envía un mensaje claro sobre el tipo de clientes que está buscando: las grandes empresas, dejando de lado el segmento de mercado de las pequeñas y medianas empresas en las que Alfresco tenía una fuerte implantación en algunos mercados.

De momento la versión Community no se verá afectada por estos cambios y seguirá permitiendo un número ilimitado de usuarios sin coste alguno.

viernes, 3 de enero de 2014

Tratamiento de URLs largas en el protocolo Sharepoint de Alfresco

Una de las funcionalidades más apreciadas por los usuarios de Alfresco Share es la posibilidad de editar ficheros de MS Office directamente desde la aplicación sin necesidad de descargarse el fichero en local. Alfresco ofrece esta funcionalidad mediante el protocolo Sharepoint incluido en el módulo vti. A pesar de su buena funcionalidad, el módulo no está exento de problemas y uno de ellos es el tratamiento de URLs demasiado largas.

Las URLs utilizadas por Alfresco para invocar la edición online de documentos tienen la siguiente forma:

{protocol}://{host}:{vti_port}/{alfresco_context}/{site}/{path_to_file}/{filename}

Todos estos elementos tienen una longitud más o menos acotada excepto que representa toda la ruta de carpetas desde la raíz del Sitio hasta el fichero. El protocolo Sharepoint tiene un problema cuando esta ruta es demasiado larga y el resultado puede ser que el fichero no se pueda abrir para su edición online. Hasta ahora la única solución era acortar estos paths cambiando el nombre de las carpetas que lo forman, pero esto puede ser una solución que no todos los usuarios de Alfresco estén dispuestos a aceptar.

Buscando una solución a este problema se me ocurrió la posibilidad de trabajar con los indentificadores de fichero de Alfresco en lugar del path. La ventaja de usar identificadores es que su longitud es fija y no excede el límite fijado por el protocolo. Con este enfoque la URL a construir sería del siguiente estilo:

{protocol}://{host}:{vti_port}/{alfresco_context}/{site}/__ID/{content_uuid}/{filename}

En esta nueva URL he usado una partícula (__ID) que me permite identificar que se trata de una URL que contiene un identificador de contenido y he añadido el UUID del contenido que quiero editar. Una vez definida la URL adecuada es hora de construirla y para ello hay que modificar la librería JavaScript que se utiliza en Alfresco para generar estas URLs. En concreto hay que buscar el fichero documentlibrary-actions.js (o su versión minificada para ser más exactos) y buscar el siguiente trozo de código:

var onlineEditUrl = this.doclistMetadata.custom.vtiServer.host + ":" + this.doclistMetadata.custom.vtiServer.port + "/" + $combine("alfresco", loc.site.name, loc.container.name, loc.path, loc.file);

Aquí se puede ver como la URL se compone a partir del site, el path y el nombre del fichero. Hay que sustituir dicho código por el siguiente:

var onlineEditUrl = this.doclistMetadata.custom.vtiServer.host + ":" + this.doclistMetadata.custom.vtiServer.port + "/" + $combine("alfresco", loc.site.name, "__ID", record.jsNode.nodeRef.uri.substring(record.jsNode.nodeRef.uri.lastIndexOf("/")+1), loc.file);

En este nuevo código se utiliza el site, el UUID (calculado a partir del nodeRef del documento) y el nombre del fichero.

El siguiente paso consiste en tratar estas URLs. Hay varias opciones para ello pero yo he optado por modificar una clase del módulo vti de Alfresco que se utiliza para realizar operaciones con el path de los ficheros. La clase en cuestión es:

org.alfresco.module.vti.handler.alfresco.VtiPathHelper

y el código de esta clase se puede obtener del repositotio público de svn de Alfresco (por ejemplo de aquí: /alfresco/COMMUNITYTAGS/V4.0e/root/modules/sharepoint/amp/source/java/org/alfresco/module/vti/handler/alfresco). También se puede obtener del repositorio enterprise si se tiene acceso.

En esta clase hay un método que devuelve la información del fichero a partir del path que le llega por la URL. Este método se utiliza en varias operaciones del protocolo por lo que hay que tener cuidado a la hora de cambiar el código para que no se vea alterado el correcto funcionamiento del módulo. El método que hay que modificar es: resolvePathFileInfo. En el código original del método aparecen las siguientes líneas:

           List splitPath = Arrays.asList(initialURL.split("/"));
           fileInfo = fileFolderService.resolveNamePath(rootNodeRef, splitPath);

Estas líneas hay que sustituirlas por estas otras para poder tratar las nuevas URLs:

List splitPath = Arrays.asList(initialURL.split("/"));
if(splitPath.contains("__ID")){
  if(splitPath.size()==4){
    NodeRef fileNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE

                                   , splitPath.get(2));
    fileInfo = fileFolderService.getFileInfo(fileNodeRef);
  } else if (splitPath.size()==3) {
    NodeRef fileNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE

                                   , splitPath.get(2));
    NodeRef parentNodeRef = nodeService.getPrimaryParent(fileNodeRef)

                                       .getParentRef();
    fileInfo = fileFolderService.getFileInfo(parentNodeRef);                 
  }

}else{
  fileInfo = fileFolderService.resolveNamePath(rootNodeRef, splitPath);
}


El nuevo código permite tratar tanto las nuevas URLs como las antiguas, manteniendo así la compatibilidad con el módulo de Alfresco. La primera parte del condicional (cuando el path contiene cuantro elementos) sirve para localizar la información del fichero que se quiere editar en el momento que se abre el fichero por primera vez. La segunda parte del condicional (cuando el path contiene 3 elementos) se usa cuando se guardan los cambios en el documento y se necesita recuperar la carpeta que lo contiene.

Con este cambio se puede generar un nuevo fichero JAR que contenga la implementación del protocolo vti y desplegarlo en el servidor de aplicaciones.

Este problema afecta a todas las versiones del módulo vti y en Alfresco están trabajando en una solución que estará disponibles en futuras versiones del módulo.

martes, 28 de agosto de 2012

Alfresco Custom Behavior

El repositorio de Alfresco permite la automatización de acciones sobre los contenidos que alberga. Hay varias formas que automatizar estas acciones como pueden ser las reglas sobre carpetas o las tareas programadas. En este artículo exploraremos una tercera opción: los behaviors.

Los behaviors son porciones de lógica de negocio que están vinculadas a políticas del repositorio. En el repositorio hay definido un conjunto de políticas que no son más que interfaces Java que son llamadas por los servicios de Alfresco. Las políticas disponibles en Alfresco para el servicio de nodo (NodeService) son:


Interface
Inner Interface
NodeServicePolicies
BeforeCreateStorePolicy

OnCreateStorePolicy

BeforeCreateNodePolicy

OnCreateNodePolicy

BeforeMoveNodePolicy

OnMoveNodePolicy

BeforeUpdateNodePolicy

OnUpdateNodePolicy

OnUpdatePropertiesPolicy

BeforeDeleteNodePolicy

OnDeleteNodePolicy

BeforeAddAspectPolicy

OnAddAspectPolicy

BeforeRemoveAspectPolicy

OnRemoveAspectPolicy

OnRestoreNodePolicy

BeforeCreateNodeAssociationPolicy

OnCreateNodeAssociationPolicy

OnCreateChildAssociationPolicy

BeforeDeleteChildAssociationPolicy

OnDeleteChildAssociationPolicy

OnCreateAssociationPolicy

OnDeleteAssociationPolicy

BeforeSetNodeTypePolicy

OnSetNodeTypePolicy

Tambíen hay políticas para el ContentService, el CopyService y el VersionService.

Un behavior sólo es una clase Java que implementa una de estas interfaces. La ventaja de un behavior sobre una regla es que los behavior aplican a todo el reposiotio mientras que las reglas aplican sobre carpetas concretas del reposiotio. Respecto a las tareas programadas, un behavior tiene la ventaja que se aplica en tiempo real y no hay que esperar hasta la hora de ejecución de la tarea para comprobar sus efectos.

Realizaré un ejemplo práctico extendiendo el ejemplo que propuse en este artículo. La idea es que todos los contenidos que se creen cuyo mimetype sea image/tiff se almacenen físicamente en un alamcen de contenidos especial. Para ello crearé un behavior vinculado a la política onCreateNodePolicy que añada el aspecto storeSelector e informe la propiedad storeName.

La definición de la clase será:

public class StoreSelectorBehavior implements NodeServicePolicies.OnCreateNodePolicy 


A continuación definimos las propiedades que inyectaremos desde Spring:


private NodeService nodeService;
private PolicyComponent policyComponent;



Creamos un método para inicializar el behavior y registrar la clase con la política adecuada. En este caso lo haremos para todos los contenidos de tipo cm:content :



public void init() {
    // Create behavior
    this.onCreateNode = new JavaBehaviour(this,"onCreateNode",NotificationFrequency.TRANSACTION_COMMIT);
    // Bind behavior to node policy
    this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI,"onCreateNode"),QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI ,"content"),this.onCreateNode);
}



Definimos el método que se ejecutará al crearse un nodo y donde comprobamos el mimetype y asignamos el aspecto storeSelector cuando sea necesario:


    public void onCreateNode(ChildAssociationRef childAssocRef) {
        // get the child node
        NodeRef node = childAssocRef.getChildRef();

        // check if the node has the correct mimetype
        ContentData contentData = (ContentData) nodeService.getProperty(node, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"));
        if (contentData.getMimetype().equalsIgnoreCase("image/tiff")) {
        // ass the aspect
            Map storeSelectorProps = new HashMap(1, 1.0f);
            storeSelectorProps.put(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"storeName"), "images"); 
            nodeService.addAspect(node, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "storeSelector"), storeSelectorProps);
        }
    }



Por último hay que definir el bean para cargar el behavior desde el contexto de Spring.



    <bean class="org.alfresco.sample.StoreSelectorBehavior" id="store-selector-behavior" init-method="init">
        <property name="nodeService">
            <ref bean="nodeService">
        </ref></property>
        <property name="policyComponent">
            <ref bean="policyComponent">
        </ref></property>
    </bean>
 
 

Ahora sólo falta desplegar los cambios y reiniciar Alfresco. A partir de este momento cada vez que se cree un contenido que tenga mimetype image/tiff se almacenará en el store images.

jueves, 16 de agosto de 2012

Modificar User Dashboards

El cliente web Alfresco Share está muy orientado a usuario y por eso el punto de entrada a la aplicación es un panel de inicio donde se muestra la información de interés para el usuario. Este panel de inicio (user dashboard) está personalizado para cada usuario.

El panel de inicio de los usuarios de Alfresco Share se configura usando presets. Estos presets se definen en el fichero /WEB-INF/classes/alfresco/site-data/presets/presets.xml (o en su equivalente de la carpeta de extensión). Un preset contiene la definición del layout de la página y la lista de componentes que aparecerán en ella. En el caso del panel de inicio de los usuarios, cuando estos acceden por primera, se crea una instancia de panel de inicio siguiendo la plantilla definida en el preset y a partir de ahí los usuarios pueden modificar la apariencia y contenido de su panel de inicio como quieran.

Este enfoque es válido cuando se quiere que los usuarios tengan el control total sobre su panel de inicio pero no lo es cuando se quiere que sea el administrador el responsable de mantener la apariencia del panel de inicio común para todos los usuarios. Imaginemos la siguiente situación. El administrador define un preset y los usuarios empiezan a acceder. Se ha hecho una modificación en el cliente web para que los usuarios no tengan disponible el botón que les permite editar su panel de inicio, por lo que todos los usuarios conservan el mismo panel de inicio desde que acceden por primera vez y todos los paneles de inicio son iguales. Hasta aquí todo va según las necesidades del administrador. Ahora bien, ¿qué pasará si el administrador hace un cambio en el preset y quiere que se aplique a todos los usuarios? Pasará que el cambio en el preset sólo se aplicará a los usuarios que accedan por primera vez después de la modificación. Todos aquellos usuarios que hubieran accedido antes del cambio conservarán el panel de inicio anterior. ¿Cómo se puede solucionar esto?

En Alfresco 4.0, la información de los paneles de inicio de los usuarios se guarda en el propio repositorio, en la ruta /company_home/Sites/surf-config . Esta ruta no se puede ver desde Alfresco Share y sólo se puede alcanzar desde Alfresco Explorer (o desde cualquiera de las APIs de Alfresco). En esta ruta hay varios espacios, entre ellos dos llamados pages y components. El primero corresponde a las páginas de los paneles de inicio de los usuarios y el segundo corresponde a cada uno de los componentes que aparecen en cada página.

Para solucionar el problema planteado, bastaría con borrar el contenido de estos dos espacios y reiniciar Alfresco para que todos los usuarios tengan su panel de inicio según lo indicado en el nuevo preset.

Esta solución también nos permitiría tratar problemas más concretos, como el de eliminar el panel de inicio de un usuario concreto o quitar un componente de un panel de inicio que esté dando problemas. También se podría aplicar esta solución para aplicar cambios mediante código. El único problema que he encontrado en esta solución es la necesidad de reiniciar Alfresco para que los cambios tengan efecto.

domingo, 15 de abril de 2012

Document library custom actions

La version 4.0 del gestor documental Alfresco incluye un nuevo mecanismo para personalizar y extender las acciones de la biblioteca de documentos del cliente Share. Este mecanismo pretende ser una simplificación del modelo anterior y permite tener más control sobre como se presentan las acciones.

La configuración por defecto de las acciones se encuentra en el fichero share-documentlibrary-config.xml. Este cambio permite que las acciones se pueden personalizar o extender en el fichero share-config-custom.xml.

Para ver como funciona este mecanismo voy a realizar un ejemplo aprovechando parte de lo que presenté en este post. La idea es crear una acción que cambie el store en el que está almacenado un documento.

Web client tier 

El primer paso es definir la acción en el fichero share-config-custom.xml. La acción se debe definir dentro del elemento de configuración llamado DocLibActions.

      <actions>
        <action id="set-content-store-selector" type="javascript" label="actions.set-content-store-selector">
            <param name="function">onActionChangeStore</param> 
            <permissions>
               <permission allow="true">Write</permission>
            </permissions>
            <evaluator negate="true">evaluator.doclib.action.isLocked</evaluator>
        </action>     
      </actions>

De esta forma hemos definido una acción llamada set-content-store-selector, de tipo javascript, que llamará a la función de javascript onActionChangeStore, que sólo estará disponible para los usuarios con permiso de escritura sobre el contenido y que aparecerá cuando el documento no esté bloqueado por otro usuario.

El icono para la acción se ha de llamar set-content-store-selector-16.png y ha deir ubicado en la carpeta \components\documentlibrary\actions.

A continuación hay que indicar en qué grupo de acciones se ha de mostrar la nueva acción. Con el nuevo mecanismo, las acciones se han agrupado según el lugar donde se han de mostrar y no según el estado del nodo como sucedía en las versiones previas de Alfresco. Esta definición va en el mismo bloque de configuración que la definición de las acciones. En el ejemplo, queremos que aparezca en el conjunto de acciones del detalle de un documento y en las acciones de navegación de los documentos:

      <actionGroups>
         <actionGroup id="document-browse">

            <action index="340" id="set-content-store-selector" />
         </actionGroup>
         <actionGroup id="document-details">
            <action index="360" id="set-content-store-selector" />
         </actionGroup>       
      </actionGroups>

Con esta configuración ya se puede comprobar como quedará la acción en el menú:



El siguiente paso es escribir la función javascript a la que se invocará desde el botón. Hay varias opciones para ello ya que en el fichero donde se definen las opciones por defecto existen algunos métodos genéricos que se pueden reutilizar. El fichero por defecto es documentlibrary-actions.js. En el ejemplo vamos a escribir una función nueva:

      onActionChangeStore: function dlA_onActionChangeStore(record)
      {
         var displayName = record.displayName;

         this.modules.actions.genericAction(
         {
            success:
            {
               event:
               {
                  name: "metadataRefresh"
               },
               message: this.msg("message.set-content-store-selector.success", displayName)
            },
            failure:
            {
               message: this.msg("message.set-content-store-selector.failure", displayName)
            },
            webscript:
            {
               method: Alfresco.util.Ajax.POST,
               name: "set-content-store-selector/node/{nodeRef}",
               params:
               {
                  nodeRef: record.jsNode.nodeRef.uri
               }
            }
         });
      },

Esta función hace una llamada AJAX por método POST a un webscript de Alfresco pasándole como parámetro la referencia al nodo sobre el que se está ejecutando la acción. Este webscript ha de ser  del package slingshot/doclib/action. Como resultado de la llamada, mostrará un mensaje de éxito o de fracaso.

Server tier

En la parte del servidor hemos de crear el web script invocado desde el método javascript. La construcción de este webscript es estándar pero aprovecharé el mecanismo de ejecución de acciones que proporciona Alfresco para simplificar la tarea.

En primer lugar definiremos el webscript:

<webscript>
  <shortname>set-content-store-selector</shortname>
  <description>Document List Action - Changes the store</description>
  <url>/slingshot/doclib/action/set-content-store-selector/node/{store_type}/{store_id}/{id}</url>
  <format default="json">argument</format>
  <authentication>user</authentication>
  <transaction>required</transaction>
  <lifecycle>internal</lifecycle>
</webscript>

Lo único a destacar es que el formato de respuesta por defecto será JSON y que el nivel de autenticación será user puesto que la acción implica un cambio en los metadatos del objeto afectado.

Para el controlador del webscript utilizaré la librería de accciones action.lib.js, que proporciona métodos para recoger los parámetros y formatear la respuesta:

<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.js">
/**
 * Entrypoint required by action.lib.js
 */
function runAction(p_params)
{
   var results;

   try
   {
      var aspectAdded = p_params.destNode.addAspect("cm:storeSelector");
      p_params.destNode.properties["cm:storeName"] = "storeA";
      p_params.destNode.save();
      if (!aspectAdded)
      {
         status.setCode(status.STATUS_INTERNAL_SERVER_ERROR, "Could not change store " + url.extension);
         return;
      }

      var resultId = p_params.destNode.name,
         resultNodeRef = p_params.destNode.nodeRef.toString();

      // Construct the result object
      results = [
      {
         id: resultId,
         nodeRef: resultNodeRef,
         action: "changeStore",
         success: true
      }];
   }
   catch(e)
   {
      e.code = status.STATUS_INTERNAL_SERVER_ERROR;
      e.message = e.toString();     
      throw e;
   }

   return results;
}

main();

Por último tenemos que crear la plantilla de visualización para el formato JSON. En este caso también utilizaremos la librería de plantillas action.lib.ftl. La plantilla sería la siguiente:

<#import "action.lib.ftl" as actionLib />
<@actionLib.resultsJSON results=results />

Una vez grabados los ficheros, hay que copiarlos a la carpeta de extenxión de Alfresco, por ejemplo en /alfresco/extension/templates/webscripts/org/alfresco/components/documentlibrary/actions. Ac ontinuación refrescaremos la lista de webscripts de Alfresco y la acción ya estará disponible para probar.

Para más detalles, se puede consultar la documentación de Alfresco.

jueves, 5 de abril de 2012

Alfresco Certified Administrator

Alfresco inició el año pasado un nuevo proceso basado en certificaciones para reconocer el nivel de los profesionales que trabajan con sus productos.

Actualmente hay disponibles dos certificaciones:

  • Alfresco Certified Administrator (ACA) : orientado a administradores de sistemas basados en Alfresco
  • Alfresco Certifies Engineer (ACE) : orientado a desarrolladores sobre Alfresco.

Dada mi experiencia profesional, la certificación que me interesaba era la primera, puesto que llevo desde el 2007 trabajando instalando y configurando Alfresco en sus múltiples variantes.

El procedimiento para obtener la certificación es aprobando un examen online. Este examen cubre un amplio temario repartido de la siguiente forma:

Competency%age of Exam
Architecture 9%
Managing the Installation 27%
Installation and Configuration
Upgrades
Monitoring health
Preventative maintenance
Installing Applications
Repository Management31%
User and group management
Security
Backup and recovery
Workflow management
Content rules
Auditing
Server management23%
Managing Storage and Content
Transformers and Metadata Extraction
Load balancing
Performance tuning
High availability
Troubleshooting5%
Provide support to developers1%
Establish test environments3%
Support end users1%

No hay mucho material espécífico para preparar este examen aunque la gente de Alfresco ha publicado algunos artículos donde hacen referencia a material de preparación. Se puede encontrar esta información aquí y aquí.

El formato del examen es un test online de 80 preguntas a resolver en 60 minutos. Hay varios tipos de preguntas: escoger la respuesta correcta (single choice), escoger las respuestas correctas (multiple choice) y escoger la respuesta más acertada (best match). El nivel de las preguntas va desde las que preguntas cosas generales (“qué características tiene la versión Enterprise que no tiene la Community ”) hasta las muy específicas (“qué valor hay que darle a la propiedad lucene.maxAtomicTransformationTime para que se fuerce la indexación de todos los contenidos”). Para superar el examen hay que acertar el 75% de las preguntas.

En mi caso opté por prepararme el examen por mi cuenta y usé como material de estudio la documentación publicada en http://docs.alfresco.com . Lo bueno de esta documentacion es que sigue bastante el guión del examen y tiene el nivel suficiente para superar el examen con éxito. Prueba de ello es que tras una lectura de este material me pude presentar al examen y aprobarlo con un 82% de aciertos. Así que ya puedo decir que soy:


miércoles, 4 de abril de 2012

Content Store Selector

Una de las consultas más habituales cuando se está haciendo una instalación de Alfresco es si existe la posibilidad de guardar los documentos en diferentes discos dependiendo de alguna característica como, por ejemplo, su tamaño. La respuesta es sí.

El mecanismo más simple para conseguir repartir los contenidos en diferentes discos es utilizar el aspecto storeSelector. Este aspecto va incluído por defecto en Alfresco pero no es accesible para los usuarios a través de los clientes web. La definición de este aspecto es muy simple ya que incluye una única propiedad llamada storeName y que indica el nombre del store donde se almacenará físicamente el contenido. 

En la configuración por defecto de Alfresco, sólo existe un store definido por el bean fileContentStore. Para definir nuevos stores hay que crear nuevos beans que apunten a la ubicación donde queremos que se guarden los contenidos. Por jemplo:

<bean id="imagesFileContentStore" class="org.alfresco.repo.content.filestore.FileContentStore">
      <constructor-arg>
         <value>${dir.root}/images</value>
      </constructor-arg>
   </bean>

En la definición del bean el elemento importante es la ruta a la ubicación física donde se guardarán los contenidos.

El siguiente elemento a definir es el contentStoreSelector, que hace referencia a todos los stores válidos definidos en Alfresco. Para el store anterior, este elemento sería así:

<bean id="storeSelectorContentStore" parent="storeSelectorContentStoreBase">
       <property name="defaultStoreName">
            <value>default</value>
       </property>
       <property name="storesByName">
           <map>
               <entry key="default">
                   <ref bean="fileContentStore" />
               </entry>
               <entry key="images">
                   <ref bean="imagesFileContentStore" />
               </entry>
          </map>
       </property>
   </bean>

Con este bean identificamos cual es el store por defecto de todos los contenidos (default) y la lista completa de los stores disponibles (default, images).

Una vez definidos los stores sólo falta decidir como aplicar el aspecto storeSelector a los contenidos. El mecanismo más seguro es mediante un behaviour, aunque para este ejemplo lo haremos con una regla. Esta regla realizará dos acciones: primero, asignar el aspecto al contenido, y segundo, ejecutar un script que dé valor a la propiedad storeName.

El contenido del script puede ser tan sencillo como:

document.properties["cm:storeName"]="images";
document.save();

Como se puede comprobar, el valor que hay que darle a la propiedad storeName coincide con el que se haya usado en el bean storeSelectorContentStore.

La definición de la regla podría ser esta:



De esta forma, cualquier documento de tipo TIFF que se cree en el repositorio se guardará en el nuevo store, y el resto de contenidos se almacenará en el store por defecto.

Hay que recordar que el aspecto storeSelector no aparece por defecto en los clientes web de Alfresco por lo que para crear la regla desde Share habrá que añadir el aspecto en el elemento DocumentLibrary del fichero share-config-custom.xml.

<aspects>
         <visible>
            <aspect name="cm:generalclassifiable" />
            <aspect name="cm:complianceable" />
            <aspect name="cm:dublincore" />
            <aspect name="cm:effectivity" />
            <aspect name="cm:summarizable" />
            <aspect name="cm:versionable" />
            <aspect name="cm:templatable" />
            <aspect name="cm:emailed" />
            <aspect name="emailserver:aliasable" />
            <aspect name="cm:taggable" />
            <aspect name="app:inlineeditable" />
            <aspect name="gd:googleEditable" />
            <aspect name="cm:geographic" />
            <aspect name="exif:exif" />
            <aspect name="cm:storeSelector"/>
         </visible>
         ... 

      </aspects>

Un hecho destacable es que tal como está implementado este mecanismo, el efecto sobre el espacio de disco ocupado por los contenidos puede no ser el esperado a priori. Cuando se sube un contenido, siempre se guarda en el store por defecto, y cuando se ejecuta la regla, el contenido se copia a la ubicación indicada por el nuevo store. El hecho de que el contenido se copie implica que ocupa espacio tanto en el store por defecto como en el nuevo store. Esta duplicidad se resuelve cuando se ejecuta el proceso de eliminación de huerfanos del store por defecto.

Si se quiere evitar la duplicidad de contenidos por esta causa, se ha de añadir esta propiedad en la configuración del Alfresco:

system.content.eagerOrphanCleanup=true

Esto hará que la copia que se quedaba en el store por defecto sea eliminada al momento.

martes, 8 de noviembre de 2011

Alfresco 4 y Solr

Hace poco se ha presentado en sociedad la versión 4.0 Community de Alfresco que incluye el que posiblemente sea el cambio arquitectónico más importante desde la aparición de este ECM: la desvinculación de los índices del motor de gestión documental mediante la utilización de Solr.

La arquitectura de Alfresco desde su primera versión hasta las últimas versiones Enterprise siempre se ha basado en tres componentes fundamenteles: la base de datos, el repositorio de documentos y los índices de Lucene. Los dos primeros componentes, por su naturaleza y por las tecnologías usadas para implementar Alfresco, han estado desvinculados del núcleo de Alfresco de forma que su instalación se podía hacer de forma independiente a la aplicación Alfresco. No ha sido así en el caso de los índices de Lucene, que siempre se deben desplegar en el mismo servidor físico que la aplicación principal de Alfresco.

La principal repercusión de esta arquitectura es que la escalabilidad global de la plataforma estaba condicionada por uno de los elementos fundamentales. Mientras que la base de datos y el repositorio de documentos se podían escalar de forma independiente a la aplicación principal, los índices siempre tenían que crecer al mismo ritmo que las instancias de Alfresco que formarán la plataforma. Aunque existían mecanismos para paliar esta situación algunos inconvenientes eran inevitables, sobre todo en instalaciones en clúster:
  • Más espacio en disco ocupado: los índices completos se deben replicar en todas las instancias de Alfresco. Para un cluster de dos nodos con un repositorio de 40Gb eso representa 8Gb. Para un repositorio de 4Tb estaríamos hablando de casi 800Gb.
  • Más tráfico de red: la necesidad de replicar los índices entre los nodos de un clúster incrementa el tráfico de red.Para instalaciones de más de 4 nodos de Alfresco este tráfico supone una cantidad importante y la necesidad de equipos de red dedicados en exclusiva a esta función.
  • Más costes de licencias: para incrementar el rendimiento de los índices es necesario utilizar más instancias de Alfresco, lo que implica nuevas licencias para las versiones Enterprise.
  • Penalización del rendimiento: las operaciones de indexación de contenidos de texto pueden ser bastante costosas y repercuten en el rendimiento de la aplicación pudiendo causar un aumento de los tiempos de respuesta de las peticiones de los clientes.

La nueva versión de Alfresco ofrece una solución a todos estos inconvenientes y abre una nueva vía para construir implantaciones de Alfresco muchísimo más escalables y con un gran potencial de crecimiento y ahorro de costes.

La solución consiste en sustituir los índices de Lucene que se han utilizado hasta ahora por un nuevo sistema de indexación basado en Apache Solr.

Apache Solr es la plataforma de búsquedas corporativas de Apache Lucene. Sus características principales incluyen potentes búsquedas de texto completo, resaltadao de resultados, búsqueda por facetas, clustering dinámico, integración con bases de datos, documentos ricos (por ejemplo, Word, PDF), manipulación y búsquedas geoespaciales. Solr es altamente escalable, proporcionando búsquedas distribuidas y replicación de índices, y el motor de búsqueda y navegación de muchos de los sitios de internet más grandes del mundo.

Dentro de la arquitectura de Alfresco la inclusión de soporte para Solr quiere decir que la función de los índices ya no está forzosamente vinculada a cada instancia de Alfresco sino que se delega en un sistema separado que se gestiona de forma independiente. A priori, este enfoque ofrece interesantes ventajas:
  • Reduce la carga de los servidores de Alfresco: las costosas operaciones de indexación ahora se realizarán en un servidor a parte por lo que los servidores de Alfresco dispondrán de mayor capacidad para servir peticiones.
  • Mayor escalabilidad: se puede dar mayor potencia a la herramienta de indexación y búsqueda cuando se necesite, sin tener que aumentar el número de servidores de Alfresco.
  • Reducción de costes: se puede conseguir mayor eficiencia con menos licencias de Alfresco y reaprovechando la infraestructura de búsquedas corporativa.
  • Reducción del espacio: en caso de tener un cluster, los índices pueden estar en una única ubicación y no replicados en todos los servidores de Alfresco.
  • Mayor fiabilidad en los clústers: al tener unos índices compartidos por las diferentes instancias de Alfresco se reduce el riesgo de sufrir problemas de sincronización y de corrupción de índices.


Además de todas estas ventajas, la gente de Alfresco también ha pensado en todos aquellos que están contentos con Lucene o que tienen alguna dependencia de esta solución y por eso ha mantenido el soporte a Lucene. Esto quiere decir que el uso de Solr o de Lucene es una decisión de los implantadores de Alfresco para que puedan escoger la solución que mejor se ajuste a las necesidades de cada uno. De esta forma aumenta la riqueza que el producto ofrece a sus usuarios.

jueves, 21 de abril de 2011

Soluciones de backup y restore para Alfresco

Uno de los requisitos habituales que piden las organizaciones cuando están estudiando implantar una herramienta de gestión documental, como Alfresco, es que la gestión de los backups de los contenidos sea lo más sencilla posible.

La gestión de los bakups tiene dos vertientes de interés:
  • La definición de una política de backup y restore de todo el repositorio de contenidos. Normalmente vinculada a la estrategia de Disaster Recovery del sistema.
  • La definición de una estrategia para la recuperación de contenidos individuales "perdidos" durante la operativa habitual del sistema.
En el caso particular de Alfresco, no hay herramientas concretas proporcionadas por el fabricante para ninguna de estas dos tareas. Lo máximo de lo que se dispone es de un conjunto de recomendaciones o best practices para conseguir estos objetivos.

Full backups

Un backup completo de Alfresco implica tres elementos: el repositorio de contenidos, la base de datos y los índices de Lucene.

De los tres elementos mencionados los más importantes son el repositorio de contenidos y la base de datos. Estos dos elementos se han de guardar conjuntamente y recuperar también juntos. Restaurar un elemento sin estar sincronizado con el otro implica acabar con un repositorio corrupto. Los índices no son tan importantes porque siempre se pueden recuperar a partir de la información de la base de datos y el repositorio.

La mejor estrategia de backup consiste en detener el Alfresco y hacer el backup de cada componente usando las herramientas proporcionadas por los fabricantes de cada sistema. Llamaremos a esta estrategia cold bakcup.

En el caso de que no se pueda detener el servicio de Alfresco para hacer la copia se tendrá que proceder a una copia en caliente del sistema. Llamaremos a esta estrategia hot backup. Esta solución es más delicada y depende sobre todo de la capacidad del sistema de base de datos de realizar copias seguras (respetando la transaccionalidad del sistema). En esta solución el orden en el que se guardan los elementos es fundamental y se debe respetar para garantizar una copia consistente de los datos. El orden es el siguiente:
  1. Asegurarse de que el backup automático de los índices se ha completado. Ha de existir la carpeta backup-lucene-indexes en el directorio apuntado por dir.root.
  2. Hacer la copia de seguridad de la base de datos utilizando las herramientas proporcionadas por el fabricante del RDBMS.
  3. Inmediatemente después de guardar la base de datos, hay que copiar los subdirectorios del directorio dir.root. Esta operación se puede hacer con rsync, xcopy, robocopy o cualquier otra herramienta de copia.
La razón de este orden es que los elementos de base de datos que se hayan añadido después de guardar los índices, se pueden regenerar mediante una reindexación (AUTO o FULL) de los índices de Lucene. Los contenidos que se hayan añadido al repositorio después de que se haya guardado la base de datos se consideraran fuera del backup y podrán entrar en el siguiente (si se guardara antes el repositorio que la base de datos, podrían aparecer elementos huérfanos que son más difíciles de recuperar).

La operación de restore consiste en detener el Alfresco y restaurar la copia de la base de datos y la copia del sistema de ficheros. Al arrancar de nuevo el Alfresco, puede ser necesario modificar el parámetro index.recovery.mode y ponerlo a FULL o a AUTO para que regenere los índices que falten.

Restore de ficheros únicos

Cuando un contenido de Alfresco se pierde por el motivo que sea (aunque lo más seguro sea por negligencia del usuario) se puede recuperar de diversas formas:
  • Si el documento tiene activado el versionado, se puede recuperar la versión anterior del historial de versiones y guardarla como la versión actual. Esta es una buena práctica para documentos de trabajo con actualizaciones frecuentes.
  • Si el documento ha sido borrado accidentalmente, se puede recuperar de la papelera de reciclaje de Alfresco. Esta papelera sólo es accesible desde Alfresco Explorer. Los usuarios o administradores pueden acceder a ella en su Profile.
  • Si el documento no está versionado y no está en la papelera (porque se ha borrado de ahí) hay que recurrir a los back ups de base de datos y file system.
Alfresco no proporciona ninguna herramienta para recuperar un archivo único de una copia de backup. La única opción "oficial" es restaurar las copias en un entorno independiente/nuevo de Alfresco y recuperar el fichero de dicho entorno. Esta solución es poco práctica cuando el volumen del repositorio es muy grande porque no siempre se dispondrá del espacio y tiempo necesarios para recuperar el file system completo en un nuevo entorno.

La solución al problema consiste en explorar directamente la base de datos de Alfresco y obtener la referencia al documento buscado para poder recuperar el archivo directamente del dispositivo de backup donde esté almacenado. Para ello, el único elemento que se ha de recuperar es la base de datos de Alfresco. Este elemento es más fácil de restaurar porque ocupa mucho menos espacio que el file system.

A partir de la información de un contenido almacenada en la base de datos se puede reconstruir el path del archivo conrrespondiente a dicho contenido. Los datos que se necesitan para hacer esta recuperación son el nombre del fichero y la última fecha en la que el contenido estaba correctamente almacenado en el repositorio. La consulta* que se ha de ejecutar para recuperar la ubicación de un fichero llamado 'demo.txt' sería:

select a.node_id, db.content_url
from

alfresco.ALF_NODE_PROPERTIES a,

alfresco.ALF_QNAME b,

alfresco.ALF_NAMESPACE c,

alfresco.ALF_NODE_PROPERTIES ab,

alfresco.ALF_QNAME bb,

alfresco.ALF_NAMESPACE cb,

alfresco.ALF_CONTENT_URL db

where
a.STRING_VALUE='demo.txt'

and
a.QNAME_ID = b.ID

and
b.NS_ID = c.ID

and
a.NODE_ID = ab.NODE_ID

and
ab.QNAME_ID = bb.ID

and
bb.NS_ID= cb.ID

and
ab.LONG_VALUE = db.ID

and
b.LOCAL_NAME = 'name'

and
c.URI = 'http://www.alfresco.org/model/content/1.0'

and
bb.LOCAL_NAME = 'content'

and
cb.URI = 'http://www.alfresco.org/model/content/1.0'



Esta consulta devuelve el identificador interno del nodo correspondiente al contenido y la url de la ubicación del fichero en el sistema de ficheros.

La estructura de la url de la ubicación del fichero del ejemplo es la siguiente:

store://2011/2/22/16/21/e7067340-9ca6-4e5b-b53b-b17dfaecf1b7.bin
  • store:/ hace referencia al almacén del fichero.
  • /2011/2/22/16/21 es el path donde está el fichero a partir del directorio raíz del alf_data contentstore.
  • e7067340-9ca6-4e5b-b53b-b17dfaecf1b7.bin es el nombre del fichero en el disco. Este nombre se puede substituir por el nombre original (p.e. demo.txt) y así se recuperaría el archivo perdido.
En el caso de que la consulta devuelva más de un registro, correspondientes a las diferentes versiones del documento, se puede utilizar el valor de la columna NODE_ID para recuperar la entrada adecuada a la versión buscada. La siguiente columna es un ejemplo de como recuperar la etiqueta de la versión:

select a.string_value
from
alfresco.ALF_NODE_PROPERTIES a,

alfresco.ALF_QNAME b,

alfresco.ALF_NAMESPACE c

where
a.NODE_ID = 540

and
a.QNAME_ID = b.ID

and
b.NS_ID = c.ID

and
b.LOCAL_NAME = 'versionLabel'

and
c.URI = 'http://www.alfresco.org/model/content/1.0'



A partir de la versión se puede recuperar el fichero correcto.

*Las consultas de este artículo están probadas en una versión 3.4.1 Enterprise.