New release of Alfviral (Alfresco Virus Alert)

I have published a new revision of Alfviral (version 1.4.0), with a restructuring of the code, with the following characteristics:
  • Switching from module to subsystem: Now, Alfviral, behaves as a subsystem so that hot parameters can be modified by JMX, such as JConsole, and reset to read the new values.
  • Added the use of templates for email notifications.
  • General code refactoring.

 

The code is in: https://github.com/fegorama/alfviral/releases/tag/v1.4.0-SNAPSHOT

 

Alfresco and Scala

scala-logo-cropedWhat is Scala?

Scala is a multi-paradigm language that integrates features of functional languages ​​and object-oriented, unlike Java is an imperative language (except in version 8, Oracle has included some functional language features such as lambda functions).

It is therefore a good idea to use Scala and Java Alfresco since, in this way, we can have both –functional and imperative– paradigms to our service.

Version of Alfresco

The test was carried out on Alfresco 5.0.c and has been used maven with the archetype of creating AMP modules, version 2.2.0 of the SDK.

Installing components in Eclipse

The following components are recommended to use Scala in Eclipse IDE:

– Install from Marketplace: Scala IDE 4.2.x (recommended)
– Install from “Install new software” (optional):

 

Name: Maven for Scala
URL: http://alchim31.free.fr/m2e-scala/update-site/

Create project Alfresco AMP module (SDK 2.2.0)

mvn archetype:generate -Dfilter=org.alfresco:

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) > generate-sources
@ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) < generate-sources
@ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom --
-
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.
archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: remote -> org.alfresco.maven.archetype:alfresco-allinone-archetype (Sample mu
lti-module project for All-in-One development on the Alfresco plaftorm. Includes
modules for: Repository WAR overlay, Repository AMP, Share WAR overlay, Solr co
nfiguration, and embedded Tomcat runner)
2: remote -> org.alfresco.maven.archetype:alfresco-amp-archetype (Sample project
with full support for lifecycle and rapid development of Repository AMPs (Alfre
sco Module Packages))
3: remote -> org.alfresco.maven.archetype:share-amp-archetype (Share project wit
h full support for lifecycle and rapid development of AMPs (Alfresco Module
Packages))
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive co
ntains): : 2
Choose org.alfresco.maven.archetype:alfresco-amp-archetype version:
1: 2.0.0-beta-1
2: 2.0.0-beta-2
3: 2.0.0-beta-3
4: 2.0.0-beta-4
5: 2.0.0
6: 2.1.0
7: 2.1.1
8: 2.2.0
Choose a number: 8: 8
Downloading: https://repo.maven.apache.org/maven2/org/alfresco/maven/archetype/a
lfresco-amp-archetype/2.2.0/alfresco-amp-archetype-2.2.0.jar
Downloaded: https://repo.maven.apache.org/maven2/org/alfresco/maven/archetype/al
fresco-amp-archetype/2.2.0/alfresco-amp-archetype-2.2.0.jar (26 KB at 16.2 KB/se
c)
Downloading: https://repo.maven.apache.org/maven2/org/alfresco/maven/archetype/a
lfresco-amp-archetype/2.2.0/alfresco-amp-archetype-2.2.0.pom
Downloaded: https://repo.maven.apache.org/maven2/org/alfresco/maven/archetype/al
fresco-amp-archetype/2.2.0/alfresco-amp-archetype-2.2.0.pom (3 KB at 16.3 KB/sec
)
Define value for property 'groupId': : com.fegor.alfresco.scala
Define value for property 'artifactId': : AlfScala
[INFO] Using property: version = 1.0-SNAPSHOT
Define value for property 'package': com.fegor.alfresco.scala: :
Confirm properties configuration:
groupId: com.fegor.alfresco.scala
artifactId: AlfScala
version: 1.0-SNAPSHOT
package: com.fegor.alfresco.scala
Y: :

Create folders to work with Scala

Create directories to work with Scala within the project:

src/main/scala
src/test/scala

Modify pom.xml to integrate compilation Scala

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.fegorsoft.alfresco.scala</groupId>
    <artifactId>AlfScala</artifactId>
    <version>1.0-SNAPSHOT</version>  
    <name>AlfScala Repository AMP project</name>
    <packaging>amp</packaging>
    <description>Manages the lifecycle of the AlfScala Repository AMP (Alfresco Module Package)</description>

    <parent>
        <groupId>org.alfresco.maven</groupId>
        <artifactId>alfresco-sdk-parent</artifactId>
        <version>2.2.0</version>
    </parent>

  <repositories>
      <repository>
          <id>artima</id>
          <name>Artima Maven Repository</name>
          <url>http://repo.artima.com/releases</url>
      </repository>
  </repositories>

    <!-- 
        SDK properties have sensible defaults in the SDK parent,
        but you can override the properties below to use another version.
        For more available properties see the alfresco-sdk-parent POM.
       -->
    <properties>
        <!-- The following are default values for data location and Alfresco Community version.
             Uncomment if you need to change (Note. current default version for Enterprise edition is 5.1)
          <alfresco.version>5.1.e</alfresco.version>
          <alfresco.data.location>/absolute/path/to/alf_data_dev</alfresco.data.location> -->

        <!-- This control the root logging level for all apps uncomment and change, defaults to WARN
            <app.log.root.level>WARN</app.log.root.level>
        -->

        <!-- Set the enviroment to use, this controls which properties will be picked in src/test/properties
             for embedded run, defaults to the 'local' environment. See SDK Parent POM for more info.
        <env>local</env>
        -->
        <recompileMode>incremental</recompileMode>
    <fsc>false</fsc>
    </properties>

    <!-- Here we realize the connection with the Alfresco selected platform 
        (e.g.version and edition) -->
    <dependencyManagement>
        <dependencies>
            <!-- Setup what versions of the different Alfresco artifacts that will be used (depends on alfresco.version),
                 so we don't have to specify version in any of the dependency definitions in our POM.
                 For more info see:
                    http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies
            -->
            <dependency>
                <groupId>${alfresco.groupId}</groupId>
                <artifactId>alfresco-platform-distribution</artifactId>
                <version>${alfresco.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Following dependencies are needed for compiling Java code in src/main/java;
             <scope>provided</scope> is inherited for each of the following;
             for more info, please refer to alfresco-platform-distribution POM -->
        <dependency>
            <groupId>${alfresco.groupId}</groupId>
            <artifactId>alfresco-repository</artifactId>
        </dependency>

        <!-- If we are running tests then make the H2 Scripts available
             Note. tests are skipped when you are running -Pamp-to-war -->
        <dependency>
            <groupId>${alfresco.groupId}</groupId>
            <artifactId>alfresco-repository</artifactId>
            <version>${alfresco.version}</version>
            <classifier>h2scripts</classifier>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>*</groupId>
                    <artifactId>*</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
  	    <dependency>
          <groupId>org.scala-lang</groupId>
          <artifactId>scala-compiler</artifactId>
          <version>2.11.8</version>
          <scope>compile</scope>
      </dependency>
      <dependency>
          <groupId>org.scala-lang</groupId>
          <artifactId>scala-library</artifactId>
          <version>2.11.8</version>
      </dependency>
      <dependency>
          <groupId>com.typesafe.scala-logging</groupId>
          <artifactId>scala-logging-slf4j_2.11</artifactId>
          <version>2.1.2</version>
      </dependency>
    <dependency>
      <groupId>org.scala-lang.modules</groupId>
      <artifactId>scala-xml_2.11</artifactId>
      <version>1.0.3</version>
    </dependency>
    <dependency>
      <groupId>org.scalactic</groupId>
      <artifactId>scalactic_2.11</artifactId>
      <version>3.0.0-M15</version>
    </dependency>
    <dependency>
      <groupId>org.scalatest</groupId>
      <artifactId>scalatest_2.11</artifactId>
      <version>3.0.0-M15</version>
      <scope>test</scope>
    </dependency>
    <!-- 
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
      </dependency>
       -->
    </dependencies>

    <profiles>
        <!--
            Brings in the extra Enterprise specific repository classes,
            if the 'enterprise' profile has been activated, needs to be activated manually.
            -->
        <profile>
            <id>enterprise</id>
            <dependencies>
                <dependency>
                    <groupId>${alfresco.groupId}</groupId>
                    <artifactId>alfresco-enterprise-repository</artifactId>
                    <version>${alfresco.version}</version>
                    <scope>provided</scope>
                </dependency>
            </dependencies>
        </profile>
  </profiles>
    
  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>net.alchim31.maven</groupId>
          <artifactId>scala-maven-plugin</artifactId>
          <version>3.2.1</version>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>2.0.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
            <plugin>
                <inherited>true</inherited>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <version>2.15.2</version>
                <executions>
                    <!-- Run scala compiler in the process-resources phase, so that dependencies on
                         scala classes can be resolved later in the (Java) compile phase -->
                    <execution>
                        <id>scala-compile-first</id>
                        <phase>process-resources</phase>                        
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
 
                    <!-- Run scala compiler in the process-test-resources phase, so that dependencies on
                         scala classes can be resolved later in the (Java) test-compile phase -->                    
                    <execution>
                      <id>scala-test-compile</id>
                      <phase>process-test-resources</phase>
                      <goals>
                          <goal>testCompile</goal>
                      </goals>
                	</execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <!-- Add src/main/scala to source path of Eclipse -->
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/main/scala</source>
                            </sources>
                        </configuration>
                    </execution>
                    <!-- Add src/test/scala to test source path of Eclipse -->
                    <execution>
                        <id>add-test-source</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>add-test-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/test/scala</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>          
            <!-- to generate Eclipse artifacts for projects mixing Scala and Java -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-eclipse-plugin</artifactId>
                <version>2.8</version>
                <configuration>
                    <downloadSources>true</downloadSources>
                    <downloadJavadocs>true</downloadJavadocs>
                    <projectnatures>
                        <projectnature>org.scala-ide.sdt.core.scalanature</projectnature>
                        <projectnature>org.eclipse.jdt.core.javanature</projectnature>
                    </projectnatures>
                    <buildcommands>
                        <buildcommand>org.scala-ide.sdt.core.scalabuilder</buildcommand>
                    </buildcommands>
                    <classpathContainers>
                        <classpathContainer>org.scala-ide.sdt.launching.SCALA_CONTAINER</classpathContainer>
                        <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
                    </classpathContainers>
                    <excludes>
                        <!-- in Eclipse, use scala-library, scala-compiler from the SCALA_CONTAINER rather than POM <dependency> -->
                        <exclude>org.scala-lang:scala-library</exclude>
                        <exclude>org.scala-lang:scala-compiler</exclude>
                    </excludes>
                    <sourceIncludes>
                        <sourceInclude>**/*.scala</sourceInclude>
                        <sourceInclude>**/*.java</sourceInclude>
                    </sourceIncludes>
                </configuration>
            </plugin>
            <!-- When run tests in the test phase, include .java and .scala source files -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <includes>
                        <include>**/*.java</include>
                        <include>**/*.scala</include>
                    </includes>
                </configuration>
            </plugin>
    </plugins>
  </build>
</project>

Create a unit test

In this case a unit test is created to retrieve the content is inside ‘Company Home’.

package com.fegor.alfresco.scala.test

/*
 *  @author Fernando González (fegor at fegor dot com)
 *  
 */
import org.scalatest.junit.AssertionsForJUnit
import org.slf4j.LoggerFactory
import com.typesafe.scalalogging.slf4j.Logger
import org.junit.Assert._
import org.junit.{ Test, Before }
import org.junit.runner.RunWith
import com.tradeshift.test.remote.{ Remote, RemoteTestRunner }
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import org.springframework.context.ApplicationContext
import org.springframework.beans.factory.annotation.{ Autowired, Qualifier }
import org.alfresco.util.ApplicationContextHelper
import org.alfresco.model.ContentModel
import org.alfresco.service.cmr.repository.{ StoreRef, NodeService }
import org.alfresco.service.cmr.search.{ SearchService, ResultSet }
import org.alfresco.repo.security.authentication.AuthenticationUtil

/** Test Alfresco in Scala
 *  
 * @constructor create tests for Alfresco with Scala
 */
@RunWith(classOf[RemoteTestRunner])
@Remote(runnerClass = classOf[SpringJUnit4ClassRunner])
@ContextConfiguration(Array("classpath:alfresco/application-context.xml"))
class RepositoryTest extends AssertionsForJUnit {
  val logger = Logger(LoggerFactory.getLogger(classOf[RepositoryTest]))
  val storeRef: StoreRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore")
  val ctx: ApplicationContext = ApplicationContextHelper.getApplicationContext
  val nodeService: NodeService = ctx.getBean("NodeService").asInstanceOf[NodeService]

  var searchService: SearchService = ctx.getBean("SearchService").asInstanceOf[SearchService]

  /* Initialize
   */
  @Before
  def initialize() {
    AuthenticationUtil.setFullyAuthenticatedUser("admin")
  }

  /* Alfresco repository test
   */
  @Test
  def testRepositoryList() {
    val rs: ResultSet = searchService.query(storeRef, SearchService.LANGUAGE_LUCENE, "PATH:\"/app:company_home/*\"")
    assert(rs.length() > 0)
    logger.info((for (i <- 0 until rs.length()) yield nodeService.getProperty(rs.getNodeRef(i), ContentModel.PROP_NAME)) mkString ",")
    rs.close()
  }
}

Enable debugging classes log4j.properties

log4j.logger.com.fegor.alfresco.scala.test.RepositoryTest=DEBUG

Results ejecutción test

2016-03-30 21:23:09,884  INFO  [extensions.webscripts.ScriptProcessorRegistry] [main] Registered script processor javascript for extension js
 2016-03-30 21:23:10,165  INFO  [scala.test.RepositoryTest] [main] Guest Home,User Homes,Shared,Imap Attachments,IMAP Home,Sites,Data Dictionary
 Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 41.923 sec - in com.fegor.alfresco.scala.test.RepositoryTest

Reference Websites

http://scala-ide.org/docs/tutorials/m2eclipse/index.html

http://www.scalatest.org/getting_started_with_junit_4_in_scala

http://ted-gao.blogspot.com.es/2011/09/mixing-scala-and-java-in-project.html

https://en.wikipedia.org/wiki/Scala_(programming_language)

https://es.wikipedia.org/wiki/Scala_(lenguaje_de_programaci%C3%B3n)

http://www.scala-lang.org/

LockService in JScript (Java Backend)

folder-blue-locked-icon

From Java you can access the Alfresco node lock service, but nodes cannot be locked (but only unlocked, usingdocument.unlock ()) with JScript (WebScripts). However, you often need to lock the document you are working with.

We can use several solutions: overload or add methods to ScriptNode object, expose the service or create an action and call it from JScript. In this case, we’ll implement the exposure of a locker object in JScript through the Java-Backend shaped object.

For this purpose, you have to create two files –the definition of bean so that Rhino can have it available, and the Java code that defines services for JScript.

Firstly, the bean definition file (javascript-context.xml) can look like this:

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
 <bean id="scriptLocker" class="es.omc.ae.javascript.ScriptLocker"
 parent="baseJavaScriptExtension">
 <property name="extensionName">
 <value>locker</value>
 </property>
 <property name="lockService">
 <ref bean="LockService" />
 </property>
 </bean>
</beans>

And the code in Java (ScriptLocker.java):


package es.omc.ae.javascript;

import org.alfresco.repo.jscript.ScriptNode;
import org.alfresco.repo.processor.BaseProcessorExtension;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockStatus;
import org.alfresco.service.cmr.lock.LockType;

/**
 * Locker and Unlocker (Backend JScript)
 * 
 * @author Fernando González (fegor [at] fegor [dot] com)
 * @version 1.0
 *
 */
public final class ScriptLocker extends BaseProcessorExtension {

 private LockService lockService;

 /**
 * @param nodeRef
 */
 public void unlock(ScriptNode scriptNode) {
 LockStatus lockStatus = lockService.getLockStatus(scriptNode.getNodeRef());
 if (LockStatus.LOCKED.equals(lockStatus)
 || LockStatus.LOCK_OWNER.equals(lockStatus)) {
 this.lockService.unlock(scriptNode.getNodeRef());
 }
 }

 /**
 * @param nodeRef
 */
 public void nodeLock(ScriptNode scriptNode) {
 this.lockService.lock(scriptNode.getNodeRef(), LockType.NODE_LOCK);
 }

 /**
 * @param scriptNode
 * @param timeToExpire (in seconds)
 */
 public void nodeLock(ScriptNode scriptNode, int timeToExpire) {
 this.lockService.lock(scriptNode.getNodeRef(), LockType.NODE_LOCK, timeToExpire);
 }
 
 /**
 * @param nodeRef
 */
 public void readOnlyLock(ScriptNode scriptNode) {
 this.lockService.lock(scriptNode.getNodeRef(), LockType.READ_ONLY_LOCK);
 }

 /**
 * @param scriptNode
 * @param timeToExpire (in seconds)
 */
 public void readOnlyLock(ScriptNode scriptNode, int timeToExpire) {
 this.lockService.lock(scriptNode.getNodeRef(), LockType.READ_ONLY_LOCK, timeToExpire);
 }
 
 /**
 * @param nodeRef
 */
 public void writeLock(ScriptNode scriptNode) {
 this.lockService.lock(scriptNode.getNodeRef(), LockType.WRITE_LOCK);
 }

 /**
 * @param scriptNode
 * @param timeToExpire (in seconds)
 */
 public void writeLock(ScriptNode scriptNode, int timeToExpire) {
 this.lockService.lock(scriptNode.getNodeRef(), LockType.WRITE_LOCK, timeToExpire);
 }
 
 /**
 * @param nodeRef
 * @return
 */
 public String getLockStatus(ScriptNode scriptNode) {
 return this.lockService.getLockStatus(scriptNode.getNodeRef()).name();
 }

 /**
 * @param nodeRef
 * @return
 */
 public String getLockType(ScriptNode scriptNode) {
 return this.lockService.getLockType(scriptNode.getNodeRef()).name();
 }

 /**
 * @param lockService
 */
 public void setLockService(LockService lockService) {
 this.lockService = lockService;
 }
}

Then, we have to use it. For example, in JScript, where we have the document object, we can write:


locker.nodeLock(document);

…to lock it, and:


locker.unlock(document);

…to unlock it later. We can also use the same methods, but with one more parameter, to lock nodes for a preset number of seconds.

In Alfresco 5, there are more methods for this service, but I have implemented the minimum so that you can operate from versions 3.4 and 4.x.

 

Links of interest:

http://dev.alfresco.com/resource/docs/java/org/alfresco/service/cmr/lock/LockService.html

 

Solucionar el error “Found 1 integrity violations” en Alfresco 5.0.2

Cuando se instala Alfresco 5.0.2 y 5.1.EA, si esta instalación recoge los parámetros para español, se produce un error como el siguiente:

 

Caused by: org.alfresco.repo.node.integrity.IntegrityException: 00270001 Found 1 integrity violations:

Invalid property value:

Node: workspace://SpacesStore/141296a2-92d5-4160-82ec-828421bd8a4a

Name: Rep. Dem.

Type: {http://www.alfresco.org/model/content/1.0}category

Property: {http://www.alfresco.org/model/content/1.0}name

 

Este error ya se resuelve en la incidencia https://issues.alfresco.com/jira/browse/ALF-21423 por Angel Borroy y lo que voy a explicar es, simplemente, como arreglarlo en el propio fichero WAR, de forma que, si instalamos de nuevo, no se produzca de nuevo el error.

 

Primero descomprimimos el fichero alfresco.war:

cd tomcat/webapps

mkdir fix

cp alfresco.war fix

unzip alfresco.war

 

Descomprimimos el fichero alfresco-repository-5.0.x.x.jar (cambia las x por el número que tenga):

cd WEB-INF/lib

mkdir fix

cd fix

jar -xvf ../alfresco-repository-5.0.2.5.jar

 

Cambiamos el mensaje que falla en bootstrap-messages_es.properties:

vi alfresco/messages/bootstrap-messages_es.properties

[Buscar dentro de vi, p.e. /Rep\.\ Dem\.]

[Cambiar “Rep. Dem.” por “Rep\u00fablica”]

Nota: Se podría hacer de una vez sustituyendo en vi, pero eso os lo dejo a vosotros 😉

[Guardar con ESC+:wq]

 

Volvemos a crear el JAR (cuidado con los números de revisión), sobreescribimos el original y borramos este directorio:

jar cvf alfresco-repository-5.0.2.5.jar

cp alfresco-repository-5.0.2.25.jar ../

cd ..

rm -rf fix

 

Volvemos a crear el WAR, creamos una copia del original, otra del nuevo, sustituimos el original y borramos el directorio de trabajo:

cd ../../

zip -r alfresco.war *

cd ..

cp alfresco.war alfresco.war-orig

cp fix/alfresco.war .

cp alfresco.war alfresco.war-fix

mv alfresco.war-orig ../../

mv alfresco.war-fix ../../

rm -rf fix

cd ../../

 

Reiniciamos el servicio de Alfresco y, si todo ha ido bien, arrancará sin problemas.

 

Nota: No olvidéis borrar previamente el despliegue (webapps/alfresco) así como el contenido del directorio work.

 

Eso es todo, solo me queda dar las gracias a Angel Borroy por su aportación en la resolución de este problema.

 

Más información:

https://forums.alfresco.com/es/forum/usu%C3%A1rio-alfresco/instalaci%C3%B3n/value-rep-dem-not-valid-file-name-10202015-1558

https://issues.alfresco.com/jira/browse/ALF-21423

 

Errores de red con Windows 10 y Alfresco

Con la actualización de Windows 10 pueden volver algunos errores que en otras versiones y sistemas operativos ya estaban solucionadas.

Estos errores vienen derivados principalmente de las nuevas configuraciones de red implementadas en Windows, a partir de la versión 7 en realidad, y que en su versión 10 han sufrido un gran cambio.

Por ejemplo, una vez instalado Alfresco puede presentarse el siguiente error:

— log —
ERROR [sf.ehcache.Cache] [main] Unable to set localhost. This prevents creation of a GUID. Cause was: Mordor: Mordor
 java.net.UnknownHostException: Mordor: Mordor
at java.net.InetAddress.getLocalHost(Unknown Source)
at net.sf.ehcache.Cache.(Cache.java:155)
[…]
— log —

Si se realiza una investigación sobre este error se encuentran muchas páginas donde la solución es introducir la dirección IP 127.0.0.1 y el nombre del host o “hostname” en el fichero hosts, tanto en Linux, Windows y Mac OSX, pero esta solución ya no funciona en Windows, debido principalmente a dos cuestiones:
1. El uso de DNSSEC 
2. El uso de IPv6 sobre IPv4
Por lo tanto, para solucionarlo, no basta con poner nada en el fichero %SystemRoot%system32Driversetchosts, de hecho, no hace falta.
La solución es más sencilla que esto, hay que usar IPv4. Se puede hacer (tocando el registro de Windows) que la versión 4 tenga prevalencia sobre la versión 6 del TCP/IP, pero también, y creo que esto es mejor, se puede arrancar la máquina virtual de Java para que use la versión de TCP/IP que se necesita. El parámetro es: 
-Djava.net.preferIPv4Stack=true
Con este parámetro se solucionan los posibles errores de detección del host que es necesario para el arranque de Alfresco.
Además, hay que incluirlo también en otros sitios donde se utilicen descargas o llamadas a direcciones de Internet, como el caso de maven, eclipse, etc.
Por ejemplo, mi variable MAVEN_OPTS es la siguiente:
-Xms256m -Xmx1024m -XX:PermSize=320m -Xss1024k -Djava.net.preferIPv4Stack=true
Y que es la misma que tengo en la configuración de eclipse (Servers) para cada servidor de Tomcat y para cada llamada de maven.
Evidentemente hay que usar este parámetro también en el fichero eclipse.ini si se necesitan instalar plugins.

Nueva revisión de Alfviral (Alfresco Virus Alert)

Hace unos días, recibí un correo electrónico donde se me avisaba de un error de Alfviral cuando se intentaba actualizar un documento desde Share en la versión 4.2.

Al parecer, en esta versión se usa un nuevo sistema de actualización asíncrona y cuando se actualiza un documento se produce un borrado de nodo para crear otro que es el actualizado. El sistema de eventos salta con cada paso por lo que hay que verificar antes de nada que el nodo sigue todavía “vivo” y no ha sido borrado por el propio sistema de actualización. Esto, curiosamente no pasa en el contexto del Explorer (Alfresco).

En resumen, he re-factorizado algo más el código, he arreglado el problema y he reorganizado el proyecto tipo all-in-one como dos submódulos de repositorio y otro para share, así creo que está más claro y es más sencillo de instalar.

La nueva revisión se puede descargar desde: https://github.com/fegorama/alfviral/releases/tag/v1.3.2-SNAPSHOT

Alfresco y aplicaciones de los DataList

— Entorno —

Sistema: GNU/Linux Mint 17.1 Rebbeca (x86_64)
Versión Alfresco: 4.2.2 Enterprise
Base de datos: MySQL 5
Cliente Web: Google Chrome 40.0.2214.115 (64-bit)
Indexador: Apache-Lucene

Las pruebas se han realizado con Apache-Lucene así pues en Solr podrían ser distintas.

— Introducción —

Los DataList en Alfresco son un sistema de mantener “tablas” de datos para ser usadas de forma básica. Estas “tablas” tienen en el repositorio una estructura donde una carpeta representa el DataList y como “hijos” contiene cada uno de los “items” u opciones que vamos añadiendo. A mi parecer no es un sistema muy eficiente en lo que respecta a la forma de gestionarlo posteriormente ya que seguramente usando como persistencia tablas de la base de datos que se use en la instancia de Alfresco seguramente serían más rápidas y estables, pensemos en alguna DataList que necesitemos con 5000 registros (items). Dicho esto, es al menos una nueva forma con la que podemos contar para gestionar datos tabulados en Alfresco.

— Aplicaciones —

Las aplicaciones van en principio por la creación de DataList que hay ya preconfigurados como la lista de contactos, eventos, agenda, etc. pero también podemos crearnos nuestra propia DataList que permita ayudarnos en la posterior gestión o en tareas como carga dinámica de nuevos controles. La creación de un nuevo tipo de DataList es muy sencillo, basta con crear un tipo que herede de “dl:dataListItem” y añadir las propiedades que necesitamos guardar.

Hasta aquí bien, de hecho es muy fácil crearnos tipos nuevos muy sencillos con uno o dos propiedades y que nos sirvan para guardar información de “datos maestros” como provincias, municipios, temperaturas, cantidades, etc.

Aquí es donde viene una posible aplicación, cuando necesitamos recoger datos de fuentes externas en muchas ocasiones estas fuentes van a ser sustituidas por Alfresco o bien solo se necesitan en determinadas ocasiones por lo que tenerlas como fuente de datos desde Alfresco es un posible problema por si se eliminan, paran o cambian. Aquí se puede recoger esa información y crear DataList con ella de forma que luego podamos usarla simplemente haciendo una serie de consultas y recorridos por la propia DataList.

— Problema —

Bien, ¿sencillo verdad?, en realidad si, pero hay algunos problemas que solventar, el primero es que al crear nuestro tipo de datos y al heredar de “dl:dataListItem”, cuando creamos un DataList de nuestro propio tipo, el nombre junto con el prefijo quedan en una propiedad llamada “dl:dataListItemType” de la siguiente forma:

Efectivamente su valor es “dh:listasClara”, pero ¿que pasa entonces?, ¿todo bien, verdad?, bueno, no del todo, ya que resulta que cuando queremos buscar por este término para poder acotar solamente la búsqueda a este tipo de DataList nos encontramos con un problema, no se encuentra…

¿Por que?

Esto es debido al tipo de indexación que tiene por defecto esta propiedad, podemos verlo mejor si usamos la herramienta LukeAll:

Como podemos ver, los valores tomados por la propiedad están divididos, de forma que realizando una búsqueda directamente como @dl:dataListItemType:”dh:listasClara” no obtendremos ningún resultado, también podemos verlo realizando la búsqueda en la propia utilidad:

Aquí se encuentran los registros porque LukeAll parte el valor como “dh” y “listasClara”, de forma que en Alfresco si usamos una consulta que solo busque “dh” la encontraremos:

Pero claro, esto ni es elegante ni fiable ya que no estamos buscando por todo el término completo. Esto nos interesa para, como digo, encontrar un tipo solamente de DataList, ya que buscando como tipo dataList encontraría todas las DataList de todos los sites.

¿Como se soluciona?

En concreto esto podemos solucionarlo pero hay que modificar el propio modelo de datos de Alfresco de la siguiente forma:

Donde se ha incluido la indexación desactivando la capacidad “tokenised”, de esta forma cuando volvemos a reindexar todo y comprobamos obtenemos:

Que es el resultado correcto y por tanto se puede buscar:

Pero esto implica, si, que tenemos que modificar el fichero original o sobrecargarlo para que funcione correctamente. Esto además es para todos los tipos, incluidos los que ya vienen predefinidos en Alfresco así que cuidado.

Bien, solucionado ¿no?, pues no, resulta que además para poder posicionarnos en el propio DataList que necesitamos (es un “folder”) debemos buscar por algo más, en principio podría ser por el nombre, pero Alfresco no nos deja que pongamos cualquier nombre, le pone uno de forma automática y poco descriptivo:

Lo que nos dificulta la búsqueda. Podemos cambiarlo, si, pero seguimos con el mismo problema que teníamos con el tipo “dl:dataListItemType”. Bueno, pues como si nos deja poner un título por ahí, igual, seguimos en las mismas…

— Solución —

La solución sencilla que he encontrado a esto es crear un aspecto nuevo y que lo asignemos a los DataList que necesitamos, por ejemplo:

De esta forma asignamos el aspecto y en “dh:nameOfDataList” podemos incluir cualquier nombre que buscará como tal, como cadena de texto.

Por ejemplo, si creando la siguiente DataList:

Y ahora buscando su “folder” en el repositorio:

Podemos asignarle directamente el aspecto:

Y el valor del campo que necesitamos:

Recomiendo un nombre bastante descriptivo como dl + site + nombre, por ejemplo para un site llamado Personal y una lista de Oficinas podría ser “dlPersonalOficinas”. Ahora ya solo nos queda comprobar que se indexa correctamente:

Y que se encuentra en Alfresco de forma correcta:

Con lo que ya tendríamos el “folder” del DataList y solo tendríamos que recorrer los “hijos” para cargar por ejemplo un control personalizado para rellenar alguna propiedad que nos interese.

Esta solución es viable para, como he comentado, usar tablas de datos maestros que se quieran tener directamente almacenadas en Alfresco (repositorio) y no tener que leerlas de fuentes externas como bases de datos o ficheros.

Además proporciona una ventaja adicional sobre la solución de leer datos de documentos que podemos tener también en Alfresco y es la facilidad que ya nos ofrece de crear nuevos “items” del DataList, modificarlos y borrarlos.

— Enlaces de interés —

https://code.google.com/p/luke/downloads/detail?name=lukeall-3.5.0.jar&

Alfviral 1.3.1-beta

Este puente de 3 días de fiesta los he dedicado, entre otras cosas, a crear mis proyectos y pasarlos de Google Code (¡gracias Google!) a GitHub y a refactorizar el código del módulo para la detección de virus en Alfresco, aunque las versiones anteriores, hasta la 1.3.0-420 seguirán estando en Google Code incluido el código fuente.

Entre las cosas que quedaban pendientes me ha dado tiempo a incluir 3 características principales aunque mi “hoja de ruta” va cambiando conforme tengo tiempo disponible así como las funcionalidades que los propios usuarios me vais pidiendo.
Las características principales añadidas a esta versión son:

  1. Incorporación del protocolo ICAP
  2. Notificaciones de infecciones a usuario y administrador
  3. Refactorización del código y creación del servicio AntivirusService

1. Incorporación del protocolo ICAP

El protocolo ICAP (Internet Content Adaptation Protocol) es un protocolo abierto para la redirección de contenidos con fines de filtrado y conversión. Este es muy usado para reenviar tráfico hacia antivirus, traducción, etc. En este caso, evidentemente, se utiliza para el envío de documentos de Alfresco hacia un servidor ICAP que se conecte a un antivirus, si bien, en realidad también podría utilizarse para más cosas, entre ellas traducción del documento, compresión, transformación, etc.

Se encuentra estandarizado en la RFC 3507 y para obtener más información se puede ir aquí.

Ahora, en Alfviral se puede configurar este modo como ICAP y con 3 parámetros de configuración que serán el servidor, puerto y servicio al que se necesita conectar. P.e. si usamos el servicor c-icap y lo configuramos para que utilice ClamAV podemos configurar el fichero alfviral.properties como:
alfviral.mode=ICAP
alfviral.icap.host=192.168.56.101
alfviral.icap.port=1344
alfviral.icap.service=srv_clamav
Aunque este sistema creo que es el mejor para casi todos los casos de uso he dejado los métodos anteriores para que puedan seguir siendo utilizados.

2. Notificaciones de infecciones a usuario y administrador

Aunque se podía realizar vía reglas de contenido, por ejemplo si hacíamos que los documentos infectados se movieran a una carpeta de cuarentena o infectados y ahí creábamos una acción de envío de correo, ahora se puede automatizar de forma general en la configuración de Alfviral. Por ahora se envían notificaciones al usuario que ha subido el documento y/o al administrador (admin) en forma de texto plano (text/plain) pero estoy trabajando para poder asignarle una plantilla personalizada según el caso. 
Por ahora para configurarlo basta con indicar a quién queremos enviarle las notificaciones.
alfviral.notify.user=true
alfviral.notify.admin=true

3. Refactorización del código y creación del servicio AntivirusService

Esto era algo que quería hacer hace tiempo. Hasta ahora todo el peso lo llevaba la clase VirusScan que era una acción, ahora ha pasado a llamarse VirusScanAction y he pasado la mayor parte del código a una nueva clase llamada AntivirusService y que funciona como servicio público, de hecho también he creado AntivirusServiceDescriptorRegistry y AntivirusServiceRegistry.
Esto hará más sencilla la actualización y extensión del módulo y la posibilidad de añadirle más métodos al servicio.
El módulo para descargar está disponible en el siguiente enlace: https://github.com/fegorama/alfviral/releases/download/v1.3.1-beta/alfviral-1.3.1-beta.zip
El código fuente se puede descargar de: https://github.com/fegorama/alfviral/archive/v1.3.1-beta.zip
El repositorio está en: https://github.com/fegorama/alfviral