Author Archives: fegor

Resetear contraseñas

Entrar como root en Linux:

En lilo:
1. Teclear: linux single
2. Cambiar la contraseña con: passwd
En grub:
1. Pulsar E en Grub.
2. Añadir a la línea “kernel…” lo siguiente: init=/bin/sh Single
3. Pulsar B para arrancar.
4. Remontar en lectura/escritura: mount / -o rw,remount

Cambiar password del root de MySQL:
1. Parar MySQL
2. Ejecutar MySQL como: mysqld --skip-grant-tables --user=root &

3. Entrar en la bd mysql: mysql mysql

4. UPDATE user SET Password=PASSWORD('clavenueva') WHERE user='root';
  flush privileges;
  exit; 
5. Arrancar MySQL

Ocultar las “Desktop Actions” en Alfresco

En algunas empresas es necesario quitar las llamadas “desktop actions” o acciones de escritorio que se visualizan en los recursos compartidos cuando se activa el protocolo CIFS. Estas acciones son en algunas ocasiones muy útiles pero en otras, además de producir un mal conteo de ficheros en las carpetas, en muchas otras ocasiones lo único que pueden derivar es en la “contaminación” de las propias acciones por virus ya que son programas ejecutables.

Para eliminar/ocultar estas acciones se puede proceder como sigue:

Copiar el fichero:

${ALF_HOME}/tomcat/webapps/alfresco/WEB-INF/classes/alfresco/subsystems/fileServers/default/file-servers-context.xml

En:

${ALF_HOME}/tomcat/shared/classes/alfresco/extension/subsystems/fileServers/default/default

Y comentar las siguientes líneas:


—- file-servers-context.xml —-

[…]
  
<!–
  
      __Alfresco.url
  
–>
[…]
<!–
  
     
        
            alfresco/desktop/Alfresco.exe
        
     
  
  
     
        
           
               CheckInOut
           
           
               __CheckInOut.exe
           
        
        
           
               JavaScriptURL
           
           
               __ShowDetails.exe
           
           
               alfresco/desktop/showDetails.js
           
           
               anyFiles
           
           
               copyToTarget
           
        
–>
        
         <!–
           
            Echo __AlfrescoEcho.exe
            <property
            name=”name”> URL
            __AlfrescoURL.exe <bean
            class=”org.alfresco.filesys.repo.desk.CmdLineDesktopAction”>
            CmdLine __AlfrescoCmd.exe
           
            JavaScript
            __AlfrescoScript.exe
            alfresco/desktop/dumpRequest.js
            anyFiles, multiplePaths, allowNoParams
            confirm, copyToTarget
         –>
<!–
     
  
–>
[…]

—- file-servers-context.xml —-

${ALF_HOME} hace referencia al “path” o directorio donde está instalado Alfresco.

Bajar SVN de Alfresco con Subclipse a través de un proxy

En el artículo sobre la compilación de la versión Community de Alfresco bajada desde del SVN oficial:

Hay un caso en el que hay que configurar algo más; cuando estemos en un sitio donde haya un proxy por medio, habrá que configurar el SVN para que pueda salir a través de dicho proxy.

Para configurarlo hay que descomentar las líneas de proxy necesarias así como la autenticación utilizada, en mi caso he usado solamente 3 de dichas líneas. El fichero de configuración se llama “servers” y se encuentra en el perfil del usuario o dentro del directorio Subclipse respectivo.

Por ejemplo, en una máquina Windows XP podríamos encontrarlo en:

C:Documents and SettingsfegorApplication DataSubversion

En una máquina con Linux generalmente estará dentro del “home” del usuario, como:

/home/fegor/.subversion

 

Y las líneas principales para hacerlo funcionar:

http-proxy-exceptions = 127.0.0.1, *.intranet.fegor.com
http-proxy-host = proxyserver.fegor.com
http-proxy-port = 8080

Además, hay que tener en cuenta que es necesario bajarse un cliente de SVN en el caso de windows, como puede ser Slik o TortoiseSVN:

http://www.sliksvn.com/en/download/
http://tortoisesvn.net/downloads.html

Así mismo, subclipse utiliza JavaHL (JNI) por defecto, por tanto, si es una distribución Linux/Ubuntu tendrémos que cargar lal librerías correspondientes, como:

sudo apt-get install libsvn-java

Y poner la referencia (pe. -Djava.library.path=/usr/lib/jni) en eclipse.ini
 
Un ejemplo:
-showsplash
org.eclipse.platform
-framework
plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar
-vmargs
-Djava.library.path=/usr/lib/jni
-Dosgi.requiredJavaVersion=1.5
-Xms40m
-Xmx512m
-XX:MaxPermSize=256m


		

SSO con Active Directory Kerberos en Win2k3 para Alfresco 3.4 en CentOS 5.5

El escenario propuesto es el siguiente:

Servidor de Alfresco:
Linux: CentOS 5.5 (i686)
Alfresco: 3.4.0 Enterprise
Tomcat: 6.0.29
MySQL: 5.0.77 (i686)
JVM (Sun): 1.6.0_22-b04 (32 bits)

SAMBA: 3.0.33
Nombre de la máquina: alfpru

Servidor PDC (Primario del dominio):
Windows: 2003 Server SP1
Nivel funcional: Windows Server 2003
Active Directory: in2pruebas
Nombre de la máquina: winsrv

Primero hay que preparar el Active Directory para Kerberos, y sobre todo para que funcione la aplicación share de Alfresco.

Si se tiene alguna duda se puede consultar la siguiente dirección: http://wiki.alfresco.com/wiki/Alfresco_Authentication_Subsystems#Kerberos

Se crean en Active Directory dos usuarios:

Nombre: Alfresco HTTP
Usuario: alfrescohttp
Passwd: laquesea

Nombre: Alfresco CIFS
Usuario: alfrescocifs
Passwd: laquesea

En ambos, después de la contraseña, se desmarca la casilla de “El usuario debe cambiar la contraseña en el siguiente inicio de sesión”, y se marcan las casillas “La contraseña nunca caduca” y “No pedir la autenticación Kerberos previa”. Así mismo, si estuviera marcada, hay que desmarcar la casilla “Usar tipos de cifrado DES para esta cuenta”. Estas casillas se encuentran en las propiedades del usuario dentro de “Usuarios y equipos de Active Directory”, en la pestaña “Cuenta”.

Bien, ahora hay que usar un comando que está dentro del Kit de Recursos de Windows 2003 Server, dentro de “Utilidades de soporte”. Este comando se llama ktpass.exe y es el que genera las tablas de claves para poder identificar el servicio.

Aquí es donde me he encontrado problemas, las mayores dificultades para configurar Alfresco con AD-Kerberos está en la generación de estos ficheros, ya que existen varias versiones de la utilidad ktpass.exe. En mi caso tengo dos comandos ktpass.exe, uno del 24/03/2005 y otro del 17/02/2007. Finalmente he creado los ficheros con la última versión.

Los comandos son:

ktpass -princ cifs/alfpru.in2pruebas@IN2PRUEBAS -pass elquesea -mapuser IN2PRUEBASalfrescocifs -crypto RC4-HMAC-NT -ptype KRB5_NT_PRINCIPAL -out c:tempalfrescocifs.keytab

ktpass -princ HTTP/alfpru.in2pruebas@IN2PRUEBAS -pass elquesea -mapuser IN2PRUEBASalfrescohttp -crypto RC4-HMAC-NT -ptype KRB5_NT_PRINCIPAL -out c:tempalfrescohttp.keytab

Luego hay que crearlos como servicios dentro del Active Directory como sigue:
setspn -a cifs/alfpru alfrescocifs
setspn -a cifs/alfpru.in2pruebas alfrescocifs

setspn -a HTTP/alfpru alfrescohttp
setspn -a HTTP/alfpru.in2pruebas alfrescohttp

Y se copian los ficheros (alfrescocifs.keytab y alfrescohttp.keytab) creados en la máquina Linux en, por ejemplo, /etc/alfresco.

Ahora, en la máquina Linux:

Seguidamente vamos a unir la máquina Linux al dominio de Windows 2003 (PDC) ya que si no, sería imposible establecer una “confianza” para realizar la autenticación para que los administradores de Windows tengan constancia de esta máquina, además este proceso también modifica el DNS de Windows 2003 Server para tener acceso vía TCP/IP aunque como bien me ha señalado iblanco no es necesario este paso para realizar simplemente la autenticación con Active Directory Kerberos.

Para ello se usa SAMBA, se instala:

yum install samba

…se configura el fichero /etc/samba/smb.conf como:

[global]
    workgroup = in2pruebas
    server string = Samba Server Version %v
    password server = in2pruebas
    realm = IN2PRUEBAS
    security = ads
    idmap uid = 10000-20000
    idmap gid = 10000-20000
    winbind separator = +
    template shell = /bin/false
    winbind use default domain = false
    winbind offline logon = false
    security = ADS

…se arranca:

service smb start

…se utiliza el comando “net” para unir la máquina al dominio de la seguiente forma:

net ads join –S winsrv.in2pruebas –n alfpru –U Administrador

…y ya se puede parar el servicio de SAMBA:
service smb stop

(Nota: Hay que repasar el fichero /etc/hosts y que las IPs apunten a los host y dominio correspondiente)

Se configura el fichero /etc/krb5.conf como:
[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log
[libdefaults]
 default_realm = IN2PRUEBAS
 default_tkt_enctypes = rc4-hmac
 default_tgs_enctypes = rc4-hmac
 dns_lookup_realm = false
 dns_lookup_kdc = true
[realms]
 IN2PRUEBAS = {
  kdc = winsrv.in2pruebas:88
  admin_server = winwrv.in2pruebas
 }
[domain_realm]
 .winsrv.in2pruebas = IN2PRUEBAS
 winsrv.in2pruebas  = IN2PRUEBAS
[appdefaults]
  forward=true
  forwardable=true
  proxiable=true

Dentro de la máquina virtual (JVM de Sun), concretamente en la parte de la JRE y en una ruta parecida a la siguiente: /usr/java/jre/lib/security existen dos ficheros que hay que configurar. El primero es java.login.config y se configurará como sigue:
Alfresco {
    com.sun.security.auth.module.Krb5LoginModule sufficient;
};
AlfrescoCIFS {
    com.sun.security.auth.module.Krb5LoginModule required
    storeKey=true
    useKeyTab=true
    keyTab=”/etc/alfresco/alfrescocifs.keytab”
    principal=”cifs/alfpru.in2pruebas”;
};
AlfrescoHTTP {
    com.sun.security.auth.module.Krb5LoginModule required
    storeKey=true
    useKeyTab=true
    keyTab=”/etc/alfresco/alfrescohttp.keytab”
    principal=”HTTP/alfpru.in2pruebas”;
};
ShareHTTP {
    com.sun.security.auth.module.Krb5LoginModule required
    storeKey=true
    useKeyTab=true
    keyTab=”/etc/alfresco/alfrescohttp.keytab”
    principal=”HTTP/alfpru.in2pruebas”;
};
com.sun.net.ssl.client {
    com.sun.security.auth.module.Krb5LoginModule sufficient;
};
other {
    com.sun.security.auth.module.Krb5LoginModule sufficient;
};

Y el fichero java.security también hay que editarlo para indicarle donde está el fichero de configuración anterior, en la línea siguiente:

login.config.url.1=file:/usr/java/jre/lib/security/java.login.config

Como se observa, en el fichero java.login.config se establecen a su vez los directorios donde se encuentran los ficheros keytab.

Para la instalación de Alfresco se ha utilizado el fichero alfresco-enterprise-3.4.0.zip, se descomprime y se copian los directorios que hay dentro de web-server de forma recursiva dentro del servidor de aplicaciones que ya tengamos instalado.
Por ejemplo:

cd /opt
make alfinst
cd alfinst
unzip /home/fegor/Downloads/alfresco-enterprise-3.4.0.zip
cp -rf webserver/conf /opt/alfresco_340/tomcat
cp -rf webserver/lib /opt/alfresco_340/tomcat
cp -rf webserver/shared /opt/alfresco_340/tomcat
cp -rf webserver/webapps /opt/alfresco_340/tomcat

Se crea la base de datos:

mysql -u root -p

CREATE DATABASE alfresco340;
GRANT ALL ON alfresco340.* to ‘alfresco’@’localhost’ identified by ‘alfresco’;
FLUSH PRIVILEGES;
EXIT

Se realiza la configuración global o general de Alfresco en el fichero alfresco-global.properties:

dir.root=/opt/alfresco_340/repositorio
db.name=alfresco340
db.username=alfresco
db.password=alfresco
db.host=localhost
db.port=3306
db.driver=org.gjt.mm.mysql.Driver
db.url=jdbc:mysql://${db.host}:${db.port}/${db.name}
authentication.chain=kerberos:kerberos

 Lo primero es comentar la línea “authentication.chain” y arrancar la instancia de Alfresco para comprobar su funcionamiento y su despliegue. Una vez desplegado ya podemos usar el subsistema “Authentication” para poder establecer la configuración de Kerberos.

Hay que copiar desde webapps/alfresco/WEB-INF/classes/alfresco/subsystems/Authentication/kerberos a shared/classes/alfresco/extension/subsystems/Authentication/kerberos/, quedando finalmente como: shared/classes/alfresco/extension/subsystems/Authentication/kerberos/kerberos y dentro habrá cuatro ficheros que son:
kerberos-authentication-context.xml
kerberos-authentication.properties
kerberos-filter-context.xml
kerberos-filter.properties

De estos, los ficheros con extensión “properties” son los que hay que configurar como sigue:

El fichero kerberos-authentication.properties:

kerberos.authentication.realm=IN2PRUEBAS
kerberos.authentication.user.configEntryName=Alfresco
kerberos.authentication.defaultAdministratorUserNames=Administrador
kerberos.authentication.cifs.configEntryName=AlfrescoCIFS
kerberos.authentication.cifs.password=laquesea
kerberos.authentication.authenticateCIFS=true

El fichero kerberos-filter.properties:
kerberos.authentication.http.configEntryName=AlfrescoHTTP
kerberos.authentication.http.password=laquesea
kerberos.authentication.sso.enabled=true
kerberos.authentication.browser.ticketLogons=true

Ahora, para tener acceso a CIFS vía Kerberos hay que copiar también la rama webapps/alfresco/WEB-INF/classes/alfresco/subsystems/fileServers/default a shared/classes/alfresco/extension/subsystems/fileServers/default/default (igualmente debe haber dos niveles de directorio llamados default), y se modifica el fichero file-servers.properties como sigue:
filesystem.name=alfpru
filesystem.acl.global.defaultAccessLevel=
cifs.enabled=true
cifs.serverName=alfpru
cifs.domain=in2pruebas
cifs.broadcast=255.255.255.255
cifs.bindto=
cifs.ipv6.enabled=false
cifs.hostannounce=true
cifs.disableNIO=false
cifs.disableNativeCode=false
cifs.sessionTimeout=900
cifs.urlfile.prefix=http://alfpru
cifs.tcpipSMB.port=445
cifs.netBIOSSMB.sessionPort=139
cifs.netBIOSSMB.namePort=137
cifs.netBIOSSMB.datagramPort=138
cifs.WINS.autoDetectEnabled=false
cifs.WINS.primary=1.2.3.4
cifs.WINS.secondary=5.6.7.8


Además recomiendo desconectar tanto FTP como NFS dentro del mismo fichero como:

ftp.enabled=false
nfs.enabled=false

Ya solo queda la configuración de la parte “share”, esta se encuentra en: shared/classes/alfresco/extension/web-extension en el fichero share-config-custom.xml.sample, hay que renombrar este fichero como share-config-custom.xml y configurar la sección “KerberosDisabled”, eliminando la palabra Disabled y descomentando el bloque que viene a continuación con la condición “Remote”. Ambos bloques se configuran como sigue:
  
  
  
     
         <!–
            Password for HTTP service account.
            The account name *must* be built from the HTTP server name, in the format :
               HTTP/@
            (NB this is because the web browser requests an ST for the
            HTTP/ principal in the current realm, so if we’re to decode
            that ST, it has to match.)
         –>
         Poli1970
         <!–
            Kerberos realm and KDC address.
         –>
         IN2PRUEBAS
         <!–
            Service Principal Name to use on the repository tier.
            This must be like: HTTP/host.name@REALM
         –>
         HTTP/alfpru.in2pruebas@IN2PRUEBAS
         <!–
            JAAS login configuration entry name.
         –>
         ShareHTTP
     
  

   <!–
        Overriding endpoints to reference an Alfresco server with external SSO enabled
        NOTE: If utilising a load balancer between web-tier and repository cluster, the “sticky
              sessions” feature of your load balancer must be used.
        NOTE: If alfresco server location is not localhost:8080 then also combine changes from the
              “example port config” section below.
        *Optional* keystore contains SSL client certificate + trusted CAs.
        Used to authenticate share to an external SSO system such as CAS
        Remove the keystore section if not required i.e. for NTLM.
       
        NOTE: For Kerberos SSO rename the “KerberosDisabled” condition above to “Kerberos”
   –>
  
     
        
             alfresco/web-extension/alfresco-system.p12
             pkcs12
             alfresco-system
        
        
        
            alfrescoCookie
            Alfresco Connector
            Connects to an Alfresco instance using cookie-based authentication
            org.springframework.extensions.webscripts.connector.AlfrescoConnector
        
        
        
            alfresco
            Alfresco – user access
            Access to Alfresco Repository WebScripts that require user authentication
            alfrescoCookie
            http://localhost:8080/alfresco/wcs
            user
            true
        
     
  

Una parte importante para poder realizar SSO Kerberos con Alfresco Share, es que hay que darle al usuario alfrescohttp la posibilidad de obtener la delegación de permisos por parte del otro servicio, en este caso, Alfresco (repositorio). Esto se obtiene dentro de Usuarios y equipos de Active Directory, en la rama Users y encima del usuario “Alfresco HTTP” pulsamos botón derecho y propiedades.

Aquí, en la pestaña “Delegación” hay que activar “Confiar en este usuario para la delegación a cualquier servicio (solo Kerberos)”.

Si esta pestaña no está visible habrá que “elevar el nivel funcional del dominio…”. Esto se consigue en la raíz del dominio (en nuestro caso in2pruebas de “Usuarios y equipos de Active Directory”, se pulsa botón derecho del ratón y se selecciona “Elevar el nivel funcional del dominio”). Se encuentra más información en: http://technet.microsoft.com/en-us/library/cc757194%28WS.10%29.aspx

Para poder depurar correctamente, recomiendo usar las siguientes líneas en los ficheros log4j.properties:

log4j.logger.org.alfresco.repo.security=debug
log4j.logger.org.alfresco.web.app.servlet.KerberosAuthenticationFilter=debug
log4j.logger.org.alfresco.web.site.servlet.SSOAuthenticationFilter=debug
log4j.logger.org.alfresco.web.app.servlet.WebScriptsSSOAuthenticationFilter=debug

Al igual, también se puede activar la depuración de Kerberos a nivel de la JVM añadiendo los parámetros a la línea que ya se tenga de JAVA_OPTS como:
export JAVA_OPTS=”${JAVA_OPTS} -Dsun.security.krb5.debug=true -Dsun.security.jgss.debug=true”

Bien, ahora vamos a la parte cliente, el Windows XP que estemos usando, el Linux o el mismo Windows 2003 Server que puede servir para probar al final si todo funciona correctamente desde el navegador Internet Explorer o Firefox.

En el caso de Internet Explorer hay que indicarle en Herramientas->Opciones de Internet->Seguridad->Intranet Local la URL a la que vamos a acceder para que funcione el SSO. En este caso se ha incluido http://alfpru.in2pruebas que es el servidor donde está instalado Alfresco. Además en Herramientas->Opciones de Internet->Seguridad->Nivel Personalizado hay que comprobar que está seleccionada la opción “Inicio de sesión automático sólo en la zona de Intranet” en “Autenticación de Usuario”.

Si no tenémos infraestructura suficiente usando DNS para resolución de nombres, podemos usar el fichero hosts de C:WindowsSystem32driversetc e incluir la línea “IP host host.dominio”, por ejemplo:
192.168.1.12   alfpru   alfpru.in2pruebas

Para el caso de Firefox, hay que poner en este (en el campo URL) about:config y confirmar que deseamos entrar en la configuración. Buscamos las siguientes opciones y le damos los valores  aquí señalados o los que correspondan según la configuración de dominio del Active Directory:
network.negotiate-auth.delegation-uris = http://alfpru.in2pruebas:8080/share
network.negotiate-auth.trusted-uris = http://alfpru.in2pruebas:8080/alfresco
network.negotiate-auth.using-native-gsslib = false

(Nota: Actualmente en la versión 3.4.0, el SSO en Alfresco no funciona con Firefox en Linux; tampoco Alfresco Share con máquinas JVM de IBM).

Ya solo queda realizar los ajustes que se necesiten, poner los nombres correctos según la configuración de cada uno y probar su funcionamiento. 

Más información de como configurar este escenario en:
http://wiki.alfresco.com/wiki/Alfresco_Authentication_Subsystems#Kerberos
http://www.bdat.com/documentos/samba/html/domain-member.html
http://technet.microsoft.com/en-us/library/cc757194%28WS.10%29.aspx

Avisos de Alfresco sobre saturación de la Cache (EHCache / Hibernate)

En algunas ocasiones, como migraciones, reindexaciones completas, etc. pueden salir determinados avisos como el siguiente:

… WARN [org.alfresco.repo.cache.TransactionalCache.org.alfresco.storeAndNodeIdTransactionalCache] Transactional update cache ‘org.alfresco.storeAndNodeIdTransactionalCache’ is full (10000).

Este tipo de avisos se refiere a que se ha llenado la memoria de intercambio o caché de segundo nivel (también llamada L2 Cache).

** SOLUCIÓN **

La solución pasa por agrandar el tamaño de esta tocando los parámetros del fichero cache-context.xml

Unos valores más acorde con situaciones de instalaciones en producción podrían ser los siguientes:

[…]
  
     
        
     
     
        
     
     
         org.alfresco.userToAuthorityTransactionalCache
     
     
         10000
     
  
[…]
  
     
        
     
     
        
     
     
     
         org.alfresco.personTransactionalCache
     
     
         25000
     
  
[…]
  
     
        
     
     
        
     
     
         org.alfresco.storeAndNodeIdTransactionalCache
     
     
         100000
     
  

[…]

Cada uno de estos valores hay que adaptarlo al número de elementos que tengamos, como son asignaciones de autorizaciones (roles en espacios de trabajo), usuarios y ficheros o documentos.

** CONSIDERACIONES **

Esto juega negativamente sobre el recolector de basura (GC) de la máquina Java (JVM) ya que tardará más en intentar recoger los objetos obsoletos.

Para esto podemos usar dos soluciones alternativas y parciales pero que impactarán sobre nuestra instalación, una es desactivar directamente la caché.

Para desconectar la caché de segundo nivel (L2 cache) podemos poner el siguiente valor en alfresco-global.properties:

hibernate.cache.use_second_level_cache=false

La otra forma de intentar resolver el tiempo de ralentización será hacer que se guarden los valores de la cache de forma más continuada. Para ello podemos reajustar los valores de los beans sessionSizeResourceInterceptor y sessionSizeResourceManager en el fichero hibernate-context.xml como sigue:

[…]
  
     
        
           
        
     
     
         10000
     
     
         100
     
  
  
     
        
     
     
         100
     
     
         100
     
     
         0
     
  
[…]

Esta última opción solo es recomendable para usarla durante una actualización, una reindexación completa, y cualquier operación que conlleve mucha carga de memoria de intercambio o cache. Una vez finalizada la operación, deberían restaurarse los valores originales.

No hay que olvidarse de hacer copia de estos ficheros para poder restaurarlos posteriormente a los valores originales.

Es recomendable además que se copien los ficheros ehcache-context.xml y hibernate-context.xml del directorio de despliegue al de configuración (extension) como custom-ehcache-context.xml y custom-hibernate-context.xml. De esta forma se pueden dejar los ficheros originales.

Más información y otras fuentes:

http://wiki.alfresco.com/wiki/Repository_Cache_Configuration
http://issues.alfresco.com/jira/browse/ETHREEOH-3294

La siguiente web es muy recomendable, tiene una serie de artículos sobre migraciones en los que se trata este tema.

http://alfrescoshare.wordpress.com/category/alfresco-dm/

Revisiones:

17/08/2011: En el caso de cluster, al usar ehcache-custom.xml en /extension, hay que modificar los valores ahí, no hace falta copiar el fichero ehcache.xml

Protección antivirus en Alfresco ECM (tercera parte)

Finalmente escribo esta tercera parte porque quedaron algunos puntos pendientes y me gustaría comentarlos.
El impacto que se produce al realizar llamadas a un comando o programa al sistema operativo desde Java es muy alto, solo estaría recomendable para sitios donde no haya una gran cantidad de subida de documentos o donde esta no sea  masiva.
La primera opción, la “desatendida” que escanea directamente el repositorio es menos intrusiva y puede ser programada además mediante una entrada en la crontab.
Existe una tercera forma dentro del sistema de escaneo bajo demanda, aunque realmente no es así, sino que es escaneado cuando el evento OnContentUpdate o OnContentRead es disparado. Esta forma es mediante el envío de la información al antivirus en forma de “data stream” o flujo de datos hacia un puerto determinado.
En este caso ClamAV puede ejecutarse en modo “demonio” con el comando clamd y podemos configurarlo para que escuche solicitudes de datos desde un puerto determinado de forma que enviaremos a este el documento para que sea escaneado y se nos devuelva un código de verificación.
Para configurar clamd se realiza en el fichero clamd.conf (generalmente en /usr/local/etc aunque dependerá de la distribución Linux que tengamos). 
De esta forma podemos cambiar algunos valores que por defecto están algo bajos:
/usr/local/etc/clamd.conf:
[…]
TCPSocket 3310
MaxConnectionQueueLength 30
StreamMaxLength 50M
MaxThreads 50
[…]
Ya solo queda modificar la clase para que envíe el flujo de datos hacia el puerto indicado y según nos devuelva el resultado así actuar. En mi caso he dejado la llamada directa al comando clamscan así como esta otra forma para que pueda seleccionarse la más adecuada según cada caso. 
También he realizado algunas modificaciones como que se pueda seleccionar qué evento se quiere que sea disparado (update, read, o ambos)  así como los nuevos valores de configuración necesarios.
La única acción que se realiza sigue siendo la asignación del aspecto “Infected”. Así se ha dejado para no realizar más acciones dentro de la propia lógica de la clase de detección y dejar este trabajo al propio Alfresco. Para ello solo habrá que crear una regla donde se necesite que contenga una acción sobre todo el contenido que se encuentre con este aspecto. Las acciones pueden ir desde enviar un mensaje de correo electrónico al propietario del documento, al administrador, etc. como mover el documento a un espacio de cuarentena o de infectados,… en fin, cada uno que elija las acciones según la política de seguridad y aplicación que se esté dando a Alfresco.
Para no dejar más código aquí, he creado un proyecto en Google Code para que pueda bajarse directamente el AMP o los fuentes de forma más fácil.
El proyecto está en: http://code.google.com/p/alfviral
El SVN es: “http://alfviral.googlecode.com/svn/trunk/ alfviral-read-only”
Si alguno quiere colaborar, como siempre, estaré encantado.  😉


Swithun Crowe ha realizado una “custom action” para poder escanear un documento a petición. Es otra idea más y esta es totalmente “bajo demanda” ya que es mediante una acción. Como es otra posibilidad se podría adaptar perfectamente a “Alfresco Virus Alert” y tenerla como otra posibilidad más dentro del marco de seguridad y protección de antivirus para Alfresco.

La entrada en la wiki es: http://wiki.alfresco.com/wiki/Antivirus

Protección antivirus en Alfresco ECM (segunda parte)

En la primera parte se vio como implementar una solución de antivirus escaneando todo el repositorio y que podía programarse por ejemplo en la “crontab” del sistema operativo.
Otra forma de realizar la detección de un virus que suba a Alfresco es mediante un escaneo “on-demand”, es decir, bajo demanda, y en este sentido cuando sea actualizado el contenido del documento. Dentro de las posibilidades que tenemos, de realizarlo de esta forma, podemos enviar un stream de datos del documento hacia el antivirus o bien ejecutar el propio antivirus pasándole exactamente el fichero a escanear. Ambas soluciones son posibles en ClamAV y en cualquiera de los dos casos el antivirus devuelve un código de error que será 0 si no hay infección y distinto si ha sido detectado, el fichero no existe, etc.
Por otra parte, Alfresco ECM disponde de las llamadas “policies” para ejecutar clases de Java o scripts de JavaScript cuando se produce algún “evento”. De todos los posibles tanto a nivel de contenido y de nodos, vamos a usar los siguientes:
Interface
Method
org.alfresco.repo.content.ContentServicePolicies
onContentUpdate
onContentRead
Usando OnContentUpdate y OnContentRead se puede lanzar la detección cuando sean leídos los documentos y/o cuando son actualizados. En el ejemplo se va a utilizar el evento OnContentUpdate para que cuando se realice una actualización de este (en el momento en el que se hace un COMMIT) se lance el antivirus y si este devuelve un código de error se añada un aspecto “infected” con dos propiedades, la fecha de detección y si ha sido “limpiado”.

Primero definimos los beans que usamos en el “behavior”: alfviral-behavior-context.xml

    
       
            true
        property>
       
           
                classpath:alfresco/extension/alfviral-behavior.properties
           
       
   
   
    <bean id="AlfViralBehavior" class="com.fegor.alfresco.behavior.OnUpdateReadScan"
        init-method=”init”>
       
           
       
       
           
       
       
           
       
       
            ${alfviral.command}
       
       
            ${dir.contentstore}
                  
    bean>

Seguidamente del fichero de propiedades: alfviral-behavior.properties

alfviral.command=/usr/bin/clamscan

Montamos el modelo de datos: alfviral-model-context.xml

       
   
       
           
                alfresco/extension/alfviralModel.xml
           
       
     

El modelo: alfviralModel.xml

     
   Alfresco Virus Alarm Model
   Fernando González Ruano (twitter://fegorara)
   1.0
  
     
     
  
  
     
  
   
       
            Infected
           
               
                    d:date
                    false
               
               
                    d:boolean
                    false
               
           
       
   

La parte para el cliente web del browser: web-client-config-custom.xml


  
     
        
        
     
  
  
     
        
     
  

El fichero de propiedades para las etiquetas: webclient.properties

ava_date=Fecha de detecciu00F3n
ava_clean=u00BFDesinfectado?

Y por último la clase java: OnUpdateReadScan.java

package com.fegor.alfresco.behavior;

import java.io.IOException;
import java.io.Serializable;
import java.util.*;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.repo.content.ContentServicePolicies;
import org.alfresco.repo.policy.Behaviour;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.log4j.Logger;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.ContentReader;

/**
 * Integrates antivirus scanning documents for alfresco
 *
 * Implements the policies of “OnContentUpdate” and “OnContentRead”.
 *
 * @author Fernando González Ruano (fegor)
 */
public class OnUpdateReadScan
    implements ContentServicePolicies.OnContentUpdatePolicy,
                ContentServicePolicies.OnContentReadPolicy
{
    private Logger logger = Logger.getLogger(OnUpdateReadScan.class);

    // behaviours
    private Behaviour onContentUpdate;
    private Behaviour onContentRead;
   
    // dependencias
    private PolicyComponent policyComponent;
    private ContentService contentService;
    private NodeService nodeService;
   
    // configuration
    private List command;
    private String store;
    private final String NAMESPACE_ALFVIRAL_CONTENT_MODEL = “alfviral.model”;
    private final QName ASPECT_INFECTED = QName.createQName(NAMESPACE_ALFVIRAL_CONTENT_MODEL, “infected”);
    private final QName PROP_INFECTED_DATE = QName.createQName(NAMESPACE_ALFVIRAL_CONTENT_MODEL, “date”);
    private final QName PROP_INFECTED_CLEAN = QName.createQName(NAMESPACE_ALFVIRAL_CONTENT_MODEL, “clean”);  
   
    Map aspectValues = new HashMap();

    // método de inicio
    public void init ()
    {
        if (logger.isDebugEnabled()) logger.debug(“Start OnUpdateReadScan.”);
       
        // crear behaviours
        this.onContentUpdate = new JavaBehaviour(this,
                “onContentUpdate”,
                NotificationFrequency.TRANSACTION_COMMIT);
       
        this.onContentRead = new JavaBehaviour(this,
                “onContentRead”,
                NotificationFrequency.TRANSACTION_COMMIT);
       
        // binding “policies”
        this.policyComponent.bindClassBehaviour(QName.createQName
                (NamespaceService.ALFRESCO_URI,
                “onContentUpdate”),
                “cm:content”, this.onContentUpdate);
       
        this.policyComponent.bindClassBehaviour(QName.createQName
                (NamespaceService.ALFRESCO_URI,
                “onContentRead”),
                “cm:content”, this.onContentRead);
    }
   
    @Override
    public void onContentUpdate (NodeRef nodeRef, boolean flag)
    {
        ContentReader contentReader = this.contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
       
        // full path of file
        String contentUrl = contentReader.getContentUrl();
        String contentPath = contentUrl.replaceFirst(“store:/”, this.store);
       
        if (logger.isDebugEnabled())
        {
            logger.debug(“(Update) “+this.command+” “+contentPath);
        }
        else if (logger.isInfoEnabled())
        {
            logger.info(“(Update) Llamando al script de escaneo de virus para “+nodeRef.getId());
        }
       
        try
        {
            // execute command “antivir contentPath”
            this.command.add(contentPath);
            ProcessBuilder pb = new ProcessBuilder(this.command);
            Process process = pb.start();
           
            int intResult = process.waitFor();

            logger.debug(“(Update) Resultado del escaneo: “+intResult);
           
            // if result is not 0, file is infected
            if (intResult != 0)
            {
                logger.info(“ALERTA ** El fichero: “+contentReader.getContentUrl()+” está infectado. **”);
               
                // add aspect Infected is not assigned              
                if (!nodeService.hasAspect(nodeRef, this.ASPECT_INFECTED))
                {  
                    this.aspectValues.put(this.PROP_INFECTED_DATE, new Date());
                    this.aspectValues.put(this.PROP_INFECTED_CLEAN, false);               
                    nodeService.addAspect(nodeRef, this.ASPECT_INFECTED, this.aspectValues);
                   
                    // TODO Other actions… quarantine, delete, send email, etc.
                }
                else
                {
                    logger.debug(“Este fichero se detectó como infectado anteriormente.”);                   
                }
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
   
    @Override
    public void onContentRead (NodeRef nodeRef)
    {
        // TODO Actions for read content
    }
   
    // Setters…
    public void setPolicyComponent(PolicyComponent policyComponent)
    {
        this.policyComponent = policyComponent;
    }
   
    public void setContentService(ContentService contentService)
    {
        this.contentService = contentService;
    }
   
    public void setNodeService(NodeService nodeService)
    {
        this.nodeService = nodeService;
    }
   
    public void setStore(String store)
    {
        this.store = store;
    }
   
    public void setCommand(List command)
    {
        this.command = command;
    }   
}

A partir de aquí…

Esto es solo un ejemplo de la multitud de acciones y configuraciones que pueden realizarse en este sentido, por ejemplo, falta el código para el evento OnContentRead, falta alguna acción de mover los documentos infectados a algún espacio de cuarentena, avisar al administrador y al usuario de la detección, internacionalizar, etc. pero creo que es un buen punto de partida para que cada cual adapte esta solución a su manera. No os lo voy a dar todo hecho ¿verdad?… 😉

Finalmente dos capturas de pantalla:


Un extracto del log (poniendo en el log4j.properties el valor log4j.logger.com.fegor=debug):

 

10:44:16,141 User:admin DEBUG [alfresco.behavior.OnUpdateReadScan] (Update) [/usr/local/bin/clamscan, /home/alfresco/enterprise/alf_data/contentstore/2010/12/30/8/3/30a7961f-cdef-4410-9ba0-ca94a8542d03.bin, /home/alfresco/enterprise/alf_data/contentstore/2010/12/30/10/42/4a6a4884-c14e-4b24-ae22-c1e2a9839593.bin, /home/alfresco/enterprise/alf_data/contentstore/2010/12/30/10/42/4a6a4884-c14e-4b24-ae22-c1e2a9839593.bin] /home/alfresco/enterprise/alf_data/contentstore/2010/12/30/10/44/b9b5c69c-cff5-400e-9f63-4888ca745749.bin
10:44:20,125 User:admin DEBUG [alfresco.behavior.OnUpdateReadScan] (Update) Resultado del escaneo: 1
10:44:20,126 User:admin INFO  [alfresco.behavior.OnUpdateReadScan] ALERTA ** El fichero: store://2010/12/30/10/44/b9b5c69c-cff5-400e-9f63-4888ca745749.bin está infectado. **

Y algo de bibliografía que podéis consultar:

Libro: Alfresco Developer Guide
Autor: Jeff Potts
ISBN: 978-1-847193-11-7

Web de Jeff Potts: http://ecmarchitect.com/
Wiki de Alfresco: http://wiki.alfresco.com/wiki/Policy_Component
Web de ClamAV: http://www.clamav.net/lang/en/

Protección antivirus en Alfresco ECM (primera parte)

Alfresco ECM no viene con antivirus de serie, al igual que otras aplicaciones y programas como puede ser OpenOffice.org, etc. dejando esta tarea al sistema operativo y las soluciones antivirus elegidas.
La solución de usar un antivirus directamente sobre Alfresco plantea varias soluciones cada una con sus pros y sus contras así como el impacto en el sistema que puede ser mayor o menor.
.
Se pueden plantear por tanto varias soluciones de escaneo con los documentos subidos a Alfresco, que pueden ser:
1. Escaneo del repositorio localizando el fichero infectado directamente y dejando a posteriori la localización del NodeRef.
2. Escaneo bajo demanda al subir un documento, haciendo para ello que Alfresco ECM redirija el contenido hacia un antivirus que permita la lectura de un stream de datos.
Ambas soluciones son viables en Alfresco aunque su función es distinta, en la primera se escanea todo el repositorio, dejándose generalmente para tareas nocturnas o de fin de semana y aseguran tener un repositorio saneado con el tiempo. Esto causa impacto en los discos que cada cierto tiempo tienen que realizar la tarea de principio a fin y que puede ser muy larga dependiendo del tamaño del repositorio.
La segunda solución es limpia y permite que cualquier documento subido a Alfresco ECM haya sido previamente escaneado, bloqueando si es necesaria, su subida si se detecta un código maligno. Esta solución impacta directamente a la hora de la subida del documento pero nos asegura que los archivos subidos estén limpios sin tener que ser examinados posteriormente.
Una solución muy completa es la de unir ambas soluciones de forma que siempre sean escaneados los documentos al ser subidos y creados en Alfresco ECM y a su vez, que cada cierto tiempo se compruebe la “salud” de los documentos pasando un escaneo completo ya que además se escanearán con la última actualización del antivirus lo que puede detectar documentos con virus que inicialmente no fueron detectados.
— Una primera solución —
Para la primera solución, a su vez, se puede abordar desde distintas perspectivas, bien desde un plano más externo a Alfresco ECM y otro directamente desarrollando parte de la solución como una extensión de este.
En cualquier caso hay que elegir una solución antivirus que pueda servirnos para estos propósitos. En mi caso he elegido la solución ClamAV ya que permite muchas formas de interactuar, es OpenSource y podemos usarlo en varias plataformas (Linux, BSD, AIS, Solaris, etc.)
Para instalar ClamAV solo hay que instalarlo vía apt-get/aptitude, yum, etc. o bien bajando el fichero directamente y compilándolo, generalmente con la secuencia:
./configure
make
make install
Una vez instalado (en el servidor, se entiende) podemos hacer uso del comando “clamscan” para escanear el repositorio. (NOTA: No es objetivo de este post la configuración de clamav, freshclam, etc.)
El uso de clamscan es muy sencillo, básicamente hay que poner las opciones necesarias y el directorio/fichero a escanear. Aquí vamos a usar las opciones de visualizar solo los ficheros infectados y que además sea de forma recursiva.
Un ejemplo:
clamscan -i -r /alfresco/alf_data/
Bien, hasta aquí correcto, nos listará los virus encontrados, pero ahora necesitamos saber a qué documento de alfresco corresponde en su entorno. Como sabemos, en Alfresco, ni siquiera el nombre del documento es correcto para poder localizarlo, en todo caso podría ser si conocemos la ruta completa, aunque no sería lo mejor; pero si podemos usar el UUID o también llamado nodeRef.
Aquí podemos usar también varias formas, bien haciendo llamadas RESTful/SOAP a WebServices o WebScripts de Alfresco ECM en el que hay que extenderlo para poder realizar las llamadas necesarias y que este nos devuelva los valores o bien realizar las acciones que necesitamos; o usar directamente consultas SQL a la base de datos utilizada. En este primer caso vamos a usar una consulta SQL hacia nuestra instalación en MySQL.
La consulta podría ser algo asi:

SELECT alf_node.uuid 
    FROM alf_node_properties
        INNER JOIN alf_node
            ON alf_node.id = alf_node_properties.node_id
        INNER JOIN alf_qname
            ON alf_qname.id = alf_node_properties.qname_id
WHERE alf_node_properties.node_id =
(SELECT alf_node_properties.node_id 
    FROM alf_content_url
                INNER JOIN alf_content_data
                        ON alf_content_url.id = alf_content_data.content_url_id
        INNER JOIN alf_node_properties
            ON alf_content_data.id = alf_node_properties.long_value
        INNER JOIN alf_qname
            ON alf_qname.id = alf_node_properties.qname_id
    WHERE alf_content_url.content_url = ‘${FUID}’
    AND alf_qname.local_name = ‘content’)
AND alf_qname.local_name = ‘name’;
 

Donde ${FUID} es la localización física del fichero en el contentstore o File Unique IDentifier, lo que Alfresco ECM llama normalmente contentUrl.
Bien, entonces, ¿Cómo podríamos hacer para que si un fichero es localizado con un virus, nuestro sistema Linux realice una llamada a Alfresco para que, por ejemplo lo renombre, ponga un aviso en el título, envíe correos electrónicos, etc.?
Para esto vamos a usar además un comando más, el comando wget (o curl si se prefiere) que puede realizar peticiones GET a un servidor para poder llamar a Alfresco ECM via RESTful y realizar una llamada al WebScript necesario.
Lo primero es el script que usaremos dentro del cron del sistema operativo (en este caso realizado para Linux CentOS con bash):
——————– alfviral.sh
#!/bin/bash

# Variables de configuración
#

ALFUSER=admin
ALFPASSWD=admin
ALFRESCO_URL=http://localhost:8080/alfresco
DIR_ROOT=/home/alfresco/alfresco-enterprise-3.3.4/alf_data
USERNAME=alfresco
PASSWD=alfresco
DATABASE=alfresco_enterprise_334
HOST=localhost
PORT=3306

PROG=$0
PROGDIR=`dirname “$PROG”`

SCANRES_FILE=scanres.txt
NODEREFS_FILE=noderefs.txt
DOCNAMES_FILE=docnames.txt

# Crea lista de ficheros infectados
#
echo “Creando lista de ficheros infectados…”
rm -f ${PROGDIR}/${SCANRES_FILE} 2>/dev/null
CONTENTSTORE=${DIR_ROOT}/contentstore
clamscan -i -r ${CONTENTSTORE} | awk -F: ‘$1~/.bin/{print “store:/”$1}’ | sed s:${CONTENTSTORE}::g >${PROGDIR}/${SCANRES_FILE}

if [ ! -s ${PROGDIR}/${SCANRES_FILE} ]
then
    echo “No hay ficheros infectados.”
    exit 0
fi

# Crea lista de NodeRefs de los ficheros
#
echo “Creando referencias NodeRefs de los FUID…”
rm -f ${PROGDIR}/${NODEREFS_FILE} 2>/dev/null
for FUID in $(cat ${PROGDIR}/${SCANRES_FILE})
do
    mysql -u${USERNAME} -p${PASSWD} -D${DATABASE} -h${HOST} -P${PORT} –skip-column-names –raw –silent >>${PROGDIR}/${NODEREFS_FILE} <
SELECT alf_node.uuid 
    FROM alf_node_properties
        INNER JOIN alf_node
            ON alf_node.id = alf_node_properties.node_id
        INNER JOIN alf_qname
            ON alf_qname.id = alf_node_properties.qname_id
WHERE alf_node_properties.node_id =
(SELECT alf_node_properties.node_id 
    FROM alf_content_url
                INNER JOIN alf_content_data
                        ON alf_content_url.id = alf_content_data.content_url_id
        INNER JOIN alf_node_properties
            ON alf_content_data.id = alf_node_properties.long_value
        INNER JOIN alf_qname
            ON alf_qname.id = alf_node_properties.qname_id
    WHERE alf_content_url.content_url = ‘${FUID}’
    AND alf_qname.local_name = ‘content’)
AND alf_qname.local_name = ‘name’;
q
STOP
done

if [ ! -s ${PROGDIR}/${NODEREFS_FILE} ]
then
    echo “¡No se han encontrado referencias a los ficheros!”
    exit 1
fi

# Lanza las llamadas a Alfresco hacia el webscript
#
echo “Llamando a Alfresco…”
rm -f ${PROGDIR}/${DOCNAMES_FILE} 2>/dev/null
ALF_TICKET=`curl “http://localhost:8080/alfresco/service/api/login?u=${ALFUSER}&pw=${ALFPASSWD}” | grep TICKET_ | sed ‘s:::g’ | sed ‘s:::g’`
for NODEREF in $(cat ${PROGDIR}/${NODEREFS_FILE})
do
    curl “${ALFRESCO_URL}/service/protect/alfviral?nref=${NODEREF}&alf_ticket=${ALF_TICKET}” >>${PROGDIR}/${DOCNAMES_FILE}
    echo “” >>${PROGDIR}/${DOCNAMES_FILE}
done——————– alfviral.sh

Y en segundo lugar crear un WebScript que realice las tareas necesarias:
——————– alfviral.get.desc.xml
<webscript>  <shortname>Alfresco Virus Alertshortname>
  <description>Alfresco Virus Alertdescription>
   <url>/protect/alfviral?nref={nref}url>
   <format default=”text”>extension</format>
   <authentication>user</authentication>
   <transaction>required</transaction>
webscript>
——————– alfviral.get.desc.xml

——————– alfviral.get.js
// chequeo de parámetros
if (args.nref == undefined || args.nref.length == 0)
{
    status.code = 400;
    status.message = “Es necesario indicar el nref.”;
    status.redirect = true;
}
else
{
    // buscar el documento por su nodeRef
    var nodes = search.luceneSearch(“ID:”workspace://SpacesStore/” + args.nref + “””);

    // renombrar el documento
    var name_infected = “”;
    name_infected = nodes[0].name;
    if (name_infected.indexOf(“_INFECTADO”) == -1)
    {
        nodes[0].name = name_infected + “_INFECTADO”;
        nodes[0].save();
        if (logger.isLoggingEnabled())
            logger.log(“El documento: ” + nodes[0].name + ” ha sido renombrado por estar infectado.”);
    }
    
    model.name_infected = nodes[0].name;
}

——————– alfviral.get.js

——————– alfviral.get.text.ftl
${name_infected}
——————– alfviral.get.text.ftl

——————– alfviral.get.html.400.ftl

${status.message}
body>
html>

——————– alfviral.get.html.400.ftl
Poniendo la llamada en nuestra crontab podemos escanear periódicamente nuestro repositorio y actuar en consecuecia.
En este caso, simplemente se han renombrado los documentos infectados, como puede verse en la imagen:

Para la segunda entrega seguiremos abordando nuevas posibilidades para mantener “sano” nuestro repositorio.

¡Ah!, y FELIZ NAVIDAD A TODOS

Alfresco Community 3.4.a y iBatis

Hoy he visto la noticia de la liberación de alfresco Community en su versión 3.4.a (lo de la letra “a” es debido a la nueva nomenclatura de que adopta Alfresco para su versión Community).

¿Donde he visto la noticia?, en los foros de Alfresco evidentemente y en el blog de Toni de la Fuente (blyx.com). Para saber más sobre esta nueva versión os remito a su blog: “Liberada Alfresco Community versión 3.4.a”

Una de las cosas que más me ha gustado ha sido la inclusión de iBatis y la eliminación de Hibernate, evidentemente solo para la parte del repositorio ya que jBPM usa también como capa de persistencia esta última y no puede eliminarse (todavía).

iBatis es un marco de trabajo para usar persistencia ayudado por SGBDs (Sistemas Gestores de Bases de Datos) al igual que Hibernate.

iBatis está dentro del proyecto Apache y lo que más destaca es su facilidad para “mapear” clases de Java y sus respectivos comandos en SQL, o sea, mapeo de campos y paso de parámetros principalmente. Personalmente creo que es un acierto que Alfresco incluya a iBatis en su sistema (al igual que otros productos que iré comentando en otras publicaciones).

Además cuenta con una herramienta llamada ibator y que no es más que un generador de código para ayudar al programador a obtener:

  • Ficheros SqlMap XML
  • Clases Java para mapear los campos y claves principales
  • Clases Java que usan los objetos DAO (opcionalmente)

Esta herramienta contiene también un plugin para Eclipse.

Si queréis saber más sobre el proyecto iBatis podéis ir a su web oficial: http://ibatis.apache.org/