Desarrollo de extracción de datos

RECU-0053 (Recurso Manual)

Descripción

Un fichero tiene asociada una serie de información particular que define las principales caracteristicas del mismo. Esta información esta contenida en el propio fichero y es conocida como metadatos. Por ejemplo el título, el autor, la fecha de creación o el nombre son metadatos por defecto para cualquier tipo de fichero.

Obviamente cada tipo de fichero puede tener asociados distintos tipos de metadatos (por ejemplo, un fichero MP3 tiene tiene atributos como el nombre del album, el titulo de la canción, un artista, un genero y así). Estos meta-datos son muy diferentes en un fichero Word. Cuando un reproductor de MP3 carga un fichero, trata de extreaer todos los meta-datos conocidos para mostrarselos al usuario. Alfresco realiza esta labor de una manera muy similar. Cuando un fichero es subido al repositorio, un extractor de metadatos - si esta asociado explicitamente - trata de realizar la extracción y cargar valores a propiedades asociadas con los meta-datos. Las properties serían visibles entonces en Alfresco a traves del navegador web.

Ademas una vez que el metadato asociado al fichero a sido extraido, el usuario podra ejecutar busquedas avanzadas usando ese metadato: cada metadato puede ser usado como un filtro para ejecutar consultas contra el repositorio.

Alfresco proporciona unos extractores por defecto (para pdf, office, html, mp3, opendocument, openoffice, mail)pero ademas permite realizar un mapeo de metadatos en un modelo personalizado desde un documento que se agregue o sea actualizado en el repositorio. Vamos a ver dos ejemplos de como configurar alfresco para que realice esta labor.

Ejemplos

Solución simple

Lo primero es crear una regla sobre el espacio donde vamos a crear los nuevos contenidos y sobre los tipos de objetos del ámbito que vayamos a utilizar, para que al crear nuevo contenido aplique la acción Extract common metadata fields from content. Esto es fundamental, y necesario para todas las soluciones, ya que es lo que permite que se utilice una extracción distinta a la estándar. Sería deseable restringir la aplicación de esta regla solo a los contenidos del tipo correspondiente a nuestro modelo personalizado. Ampliando la funcionalidad de esta regla y mediante el uso de script también podremos ampliar y desarrollar la incorporación de metadatos sin necesidad de ningún otro código específico.

A continuación, para una solución sencilla bastaría con añadir los campos a sustituir en custom-metadata-extrators-context.xml (si estamos trabajando con Tomcat en /shared/classes/alfresco/extensión/), como en el ejemplo siguiente.

Para este ejemplo tendremos las siguientes consideraciones:

sc:test1

Donde test1 sería el metadato del documento OpenOffice y sc:test1 sería el metadato en nuestro modelo personalizado.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"[]>
<!-- Este ejemplo muestra como añadir en nuestro modelo, campos específicos -->
<beans>
  <bean id="extracter.OpenDocument" class="org.alfresco.repo.content.metadata.OpenDocumentMetadataExtracter" parent="baseMetadataExtracter">
    <property name="inheritDefaultMapping">
      <value>true</value>
    </property>
    <property name="mappingProperties">
        <props>
            <prop key="namespace.prefix.sc">http://www.sample.com/model/content/1.0</prop>
            <prop key="test1">sc:test1</prop>
            <prop key="test2">sc:test2</prop>
        </props>
     </property>
  </bean>
</beans>

Si el fichero custom-metadata-extrators-context.xml ya existe, bastará con añadir una nueva sección . La modificación (o creación) de este fichero también será necesaria en todo caso.

En el caso del ejemplo sc:test1 se corresponde con un metadato asociado a un aspecto, que a su vez es el aspecto por defecto ("mandatory") para el tipo de documentos que define nuestro modelo. Esto es importante, ya que al añadir el contenido e indicar su tipo, para que el mecanismo funcione, debe tener los metadatos adecuados.

Una vez realizados los cambios en el fichero, será necesario reiniciar el servidor para que se cargue la nueva configuración.

A continuación bastaría con utilizar la opción de "Añadir contenido" e indicar el tipo correspondiente a nuestro modelo personalizado. Al mostrarse las opciones de propiedades de contenido, si al crear el modelo hemos definido las opciones de visualización, podremos comprobar que los metadatos del documento de origen se asignan automáticamente a los metadatos del nuevo contenido de nuestro modelo personalizado, basándonos en las definiciones que hemos realizado.

Si ya necestitamos algo más específico o con una funcionalidad más personalizada habría que desarrollar una clase que herede del extractor de Open Document y modifique el contenido de manera específica, como se detalla en el siguiente punto.

Solución con extractor personalizado

Esta solución sería un mecanismo ampliado, para realizar una manipulación o un procesamiento de los metadatos a incorporar. También puede ser necesario cuando los metadatos en el origen se encuentren comprimidos o sea necesario extraerlos de una manera particular, todos los metadatos del documento OpenOffice en un solo metadatos de nuestro contenido por ejemplo.

Sigue siendo necesaria la creación de una regla y del fichero custom-metadata-extrators-context.xml como se ha explicado en el punto anterior (si trabajamos con TOMCAT en /shared/classes/Alfresco/extensión/).

La clase Java a realizar será una subclase de AbstractMappingMetadataExtracter del paquete org.alfresco.content.metadata, que es la clase abstracta donde están definidas las funcionalidades básicas de un extractor personalizado.

Veamos la clase CustomOpenOfficeMetadataExtracter como un ejemplo de la clase a construir. En esta clase se han sobrecargado los distintos métodos necesarios:

package org.alfresco.module.CustomMetadata;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.content.*;
import org.alfresco.repo.content.metadata.AbstractMappingMetadataExtracter;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.namespace.QName;
import com.catcode.odf.ODFMetaFileAnalyzer;
import com.catcode.odf.OpenDocumentMetadata;
public class CustomOpenOfficeMetadataExtracter extends
        AbstractMappingMetadataExtracter {
    //private static final Log logger = LogFactory.getLog(CustomOpenOfficeMetadataExtracter.class);
   
    private static final String KEY_CREATION_DATE = "creationDate";
    private static final String KEY_CREATOR = "creator";
    private static final String KEY_DATE = "date";
    private static final String KEY_DESCRIPTION = "description";
    private static final String KEY_GENERATOR = "generator";
    private static final String KEY_INITIAL_CREATOR = "initialCreator";
    private static final String KEY_KEYWORD = "keyword";
    private static final String KEY_LANGUAGE = "language";
    private static final String KEY_PRINT_DATE = "printDate";
    private static final String KEY_PRINTED_BY = "printedBy";
    private static final String KEY_SUBJECT = "subject";
    private static final String KEY_TITLE = "title";
    //custom
    private static final String KEY_TEST1 = "Asunto";
    private static final String KEY_TEST2 = "Autor";
  
    public static String[] SUPPORTED_MIMETYPES = new String[] {
            MimetypeMap.MIMETYPE_OPENDOCUMENT_TEXT,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_TEXT_TEMPLATE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_GRAPHICS,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_GRAPHICS_TEMPLATE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_PRESENTATION,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_PRESENTATION_TEMPLATE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_SPREADSHEET,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_SPREADSHEET_TEMPLATE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_CHART,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_CHART_TEMPLATE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_IMAGE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_IMAGE_TEMPLATE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_FORMULA,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_FORMULA_TEMPLATE,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_TEXT_MASTER,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_TEXT_WEB,
            MimetypeMap.MIMETYPE_OPENDOCUMENT_DATABASE };
    public CustomOpenOfficeMetadataExtracter()
    {
        super(new HashSet<String>(Arrays.asList(SUPPORTED_MIMETYPES)));
    }
    @Override
    public Map<String, Serializable> extractRaw(ContentReader reader) throws Throwable
    {
        Map<String, Serializable> rawProperties = newRawMap();
       
        ODFMetaFileAnalyzer analyzer = new ODFMetaFileAnalyzer();
        InputStream is = null;
        try
        {
            is = reader.getContentInputStream();
            // stream the document in
          
            OpenDocumentMetadata docInfo = analyzer.analyzeZip(is);
            if (docInfo != null)
            {
                putRawValue(KEY_CREATION_DATE, docInfo.getCreationDate(), rawProperties);
                putRawValue(KEY_CREATOR, docInfo.getCreator(), rawProperties);
                putRawValue(KEY_DATE, docInfo.getDate(), rawProperties);
                putRawValue(KEY_DESCRIPTION, docInfo.getDescription(), rawProperties);
                putRawValue(KEY_GENERATOR, docInfo.getGenerator(), rawProperties);
                putRawValue(KEY_INITIAL_CREATOR, docInfo.getInitialCreator(), rawProperties);
                putRawValue(KEY_KEYWORD, docInfo.getKeyword(), rawProperties);
                putRawValue(KEY_LANGUAGE, docInfo.getLanguage(), rawProperties);
                putRawValue(KEY_PRINT_DATE, docInfo.getPrintDate(), rawProperties);
                putRawValue(KEY_PRINTED_BY, docInfo.getPrintedBy(), rawProperties);
                putRawValue(KEY_SUBJECT, docInfo.getSubject(), rawProperties);
                putRawValue(KEY_TITLE, docInfo.getTitle(), rawProperties);
              
              
                // Handle user-defined properties dynamically
                Map<String, Set<QName>> mapping = super.getMapping();
                Hashtable userDefinedProperties = docInfo.getUserDefined();
               // Extract those user properties for which there is a mapping
               for (String key : mapping.keySet())
               {
                  if (userDefinedProperties.containsKey(key))
                   {
                       Object value = userDefinedProperties.get(key);
                       if (value != null && value instanceof Serializable)
                       {
                           putRawValue(key, (Serializable) value, rawProperties);
                       }
                       if(docInfo.getDescription() == null){
                          docInfo.setDescription("");
                       }
                       docInfo.setDescription(docInfo.getDescription() + ";" + key + ";" + value.toString());
                   }
               }
              
            }
        }
        finally
        {
            if (is != null)
            {
                try { is.close(); } catch (IOException e) {}
            }
        }
        // Done
        return rawProperties;
    }
   
}

Junto con esta clase podríamos desarrollar también un fichero de propiedades (CustomOpenOfficeMetadataExtracter.properties) que nos permitirá definir las equivalencias de metadatos directamente. En nuestro caso, un ejemplo sería:

#
# CustomOpenOfficeMetadataExtracter - default mapping
#
# author: Intecna
# Namespaces
namespace.prefix.sc=http://www.someco.com/model/content/1.0
namespace.prefix.cm=http://www.alfresco.org/model/content/1.0
# Mappings
creationDate=cm:created
creator=cm:author
date=
description=cm:description
generator=
initialCreator=
keyword=
language=
printDate=
printedBy=
subject=
title=cm:title
test1=sc:test1
test2=sc:test2

En este caso, si incluimos el fichero de propiedades, nuestro fichero custom-metadata-extrators-context.xml , podría simplificarse quedando como:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"[]>
<!-- Este ejemplo muestra como añadir en nuestro modelo, campos específicos -->
<beans>
    <bean id="extracter.OpenDocument"  class="org.alfresco.module.CustomMetadata.CustomOpenOfficeMetadataExtracter" parent="baseMetadataExtracter">
        <property name="inheritDefaultMapping">
     <value>true</value>
 </property>
        <property name="mappingProperties">
            <bean   class="org.springframework.beans.factory.config.PropertiesFactoryBean">
               <property name="location">
                  <valueasspath>cl:alfresco/extension/ CustomOpenOfficeMetadataExtracter.properties</value>
               </property>
            </bean>
        </property>
    </bean>

En el caso de que no hayamos definido un fichero de propiedades, podremos mantener custom-metadata-extrators-context.xml como en el punto 1.1, con la salvedad de hacer referencia a nuestra nueva clase:

<bean id="extracter.OpenDocument" class="org.alfresco.module.CustomMetadata.CustomOpenOfficeMetadataExtracter" parent="baseMetadataExtracter">

A continuación y una vez realizadas las tareas y actualizados todos los ficheros reiniciaremos el servidor para que la nueva configuración sea cargada.