Tuxitecte

mercredi 28 avril 2010

Maven + Alfresco : Delivery and Assembly


Hello everyone!

It is time to talk about the delivery format of your project. You tell me, he use for many post AMP format and now he wants to give us something else? Yeap it's true...!

Depending on your projects, your distribution of roles, your teams and your business practices, the delivery format can be simple or very very complex!

Take the classic case, on the one side we have a development team and in the other side we have an operation (production) team. The first one is responsible for creating the product, the other is in charge of installing the product and verify that it works throughout its lifetime.

The question: What is the exchange format of product between these two teams?
Ps: we will not consider here all the documentation from the production team ...

Let's make a list of possible cases:

  • AMP(s): For the development team is the simple format. Indeed it is the output format of our project! So you have just to get AMP file and send it! Of course if our project consists of several AMPs, we will send all AMPs. In this case, the production team is responsible for generating the WAR application using the script provided by Alfresco.
  • A WAR: Here the development team is responsible for creating the war. They have to integrate different modules in the default Alfresco war. The production team have to simply install the application.
  • AMP + WAR: In this scenario, the production team want to have the application and AMPs.
  • Other: Here you can invent anything that goes through your head. If you work in big projects, or in a large size companies, I think you have very beautiful examples!
As you can see, examples are not lacking! It is rather the opposite. The question :
How can we create these new delivery formats in our AMP project ?

The idea as usual is to run a Maven command and wait for the generated file(s). Never forget that the developer is supposed to be lazy ^ ^.

In lines below, I will present two approaches to generate anything other than your AMP. These approaches are based on a new project called war-alf-osecm.

Note: This approach is not very consistent with the philosophy but Maven does the work desired.

Creating a new project AMP

Defining Structure
Create a directory with the name: alf-war-osecm and add a pom.xml with value below

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4.0.0
fr.opensourceecm
alf-war-osecm
pom
0.1
Alfresco AMP Open Source ECM
Open Source ECM - Extension




Integrating the tool into a repository

In this project, we will use AlfrescoModule Management Tool . This module is available in the bin directory of Alfresco standard installation . It is also available here: http://wiki.alfresco.com/wiki/Community_Edition_file_list_3.3.
So initially, we will install this file in our maven repository with the command:

call mvn install:install-file -Dfile=alfresco-mmt.jar -DgroupId=org.alfresco.sdk -DartifactId=mmt -Dversion=3.3 -Dpackaging=jar -DgeneratePom=true -DcreateChecksum=true


Note : We perform this command in the directory where Alfresco module management tool file is.
Due to this integreation, It will be possible to find it through the classic mechanism of dependency .

Create a new Alfresco WAR
Now, as the title suggests, I want to generate a WAR with all AMPs. It will integrate all AMPs in a WAR Alfresco. This process will be divided into 4 parts.
  • Setting Properties: In this part, we will initialize the properties that we used during other parts.
  • Import pre-requisite: We will install Alfresco war and tool into our working directory.
  • Import of AMP: In our working directory, we will create a subdirectory that will serve to contain all AMPs to integrate.
  • War Generation : Here, we will launch a command line to integrate AMPs
  • (Optional) Install war in maven repository: After generation, you can optionally add the generated war in our maven repository.
Setting Properties
We will define four properties
  • Work.dir: Working directory
  • Amps.dir: AMP directory
  • Mmt.jar: Module Management Tool filename
  • Alfresco.war: Alfresco war filename



livraison
amps
mmt.jar
alfresco.war




Import pre-requisites
Via the plugin maven-dependency-plugin we will use the goal copy during the prepare-package phase. To simplify, we want to copy a set of dependencies (alfresco.war and tool) in our working directory.


org.apache.maven.plugins
maven-dependency-plugin


war-copy-requisites
prepare-package

copy




org.alfresco.sdk
alfresco-community-war
3.3
war
${project.build.directory}/${work.dir}
${alfresco.war}


org.alfresco.sdk
mmt
3.3
jar
${project.build.directory}/${work.dir}
${mmt.jar}









Import AMP
In this stage, we will add a dependencies block in our pom.xml. In our case, dependencies contains 2 dependencies :
  • fr.opensourceecm:alf-osecm-am:amp:0.0.5
  • org.alfresco.sample:SDK-CustomWizard:amp:1.0

Our pom.xml looks like



fr.opensourceecm
alf-amp-osecm
0.0.5
amp


org.alfresco.sample
SDK-CustomWizard
1.0
amp




Then, we continue using maven-dependency-plugin. We will realize the goal copy-dependencies in the phase of prepare-package. More simply, we want to copy all MPAs (defined in length) in a subdirectory of work amps.dir ().



org.apache.maven.plugins
maven-dependency-plugin


war-copy-amp
prepare-package

copy-dependencies


amp
${project.build.directory}/${work.dir}/${amps.dir}
false
true
true






War Generation
Via the plugin exec-maven-plugin, we will realize the goal exec during the package phase. In other words, we want to achieve the same behavior as apply-amps.bat file (or .sh) but via Maven.



org.codehaus.mojo
exec-maven-plugin
1.1


war-create
package

exec




java

-jar
${project.build.directory}/${work.dir}/${mmt.jar}
install
${project.build.directory}/${work.dir}/${amps.dir}
${project.build.directory}/${work.dir}/${alfresco.war}
-directory






Now it is possible to run the command


mvn clean package



You can now find in your work.dir a new alfresco.war containing all AMP dependencies defined in our pom.xml.

(Optional) Install the war locally
If you want to install this war in your local repository (and thus be able to create dependencies for testing or integration purposes), here is the configuration to add to your build phase:


org.apache.maven.plugins
maven-install-plugin
2.3


war-install
install

install-file


${project.build.directory}/${work.dir}/${alfresco.war}
${project.groupId}
${project.artifactId}
${project.version}
war
test
true
true






With the command

mvn clean install


you can install your new war. Of course, you can change various settings to suit your needs ...

Note: You can do the same deploy phase if you change some parameters... ^ ^


Create a custom delivery format
Now we'll look at creating a custom delivery format. In my case, I wish
  • Create a zip file containing
  • A root folder : groupId.artifactId
  • A sub-folder amps containing my amps
  • A sub-folder application containing alfresco.war (with the amp already include)

To do the job, I'll use maven-assembly-plugin. This plugin is used to create custom distribution formats. Configuration of this plugin is done in 2 steps:
  • Create a description file
  • Adding configuration in pom.xml

Create a description file

This description file defines the expected result ie what are the directories to create, file formats, rights, encoding ... We can actually define a lot of parameters!

In our case, we will create osecm-assembly.xml in alf-war-osecm\src\assembly directory. This file contains the following configuration:


xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
production

zip

${project.groupId}.${project.artifactId}.${project.version}


target/${work.dir}
unix
/application

${alfresco.war}





/${amps.dir}

org.alfresco.sample:SDK-CustomWizard
fr.opensourceecm:alf-amp-osecm







As you can see, it takes the configuration expected in my requirements.

Adding to the pom.xml
Now we must declare in the pom.xml this new file. So we add maven-assembly-plugin with the configuration below


maven-assembly-plugin


src/assembly/osecm-assembly.xml






Start assembly
Finally, I can run the command :

mvn clean assembly:assembly



This command assemble my custom distribution format. In the target directory, you can find your custom file called : alf-war-osecm-0.1-production.zip ready to be sent!

Note: This plugin may be greedy in memory. To resolve this problem increase values in MAVEN_OPTS environment variable.
Read More...

Maven + Alfresco : Multi-AMP relationship



Hello World!

This time I'll try to explain multi-AMP relationships and how they can be managed with Maven and Alfresco...



Introduction

The problem is as follows:
In a multi-AMP project, how we organize those relationships in our Maven project(s) ?

Suppose we want to create a project with 3 AMPS: A, B, C

Above all, what are the relationships between these three AMPS? Are they independent, interdependent ? What are relations induced?

Then, what is the delivery format of our application? Is it a WAR, a single AMP, a set of AMP, a particular format of delivery...

According to the answers of each question, we have to create different policy! and of course, we have to create different configuration of Maven projects!

In general, I identify several use cases.

  • Simple relationships: In other words it is the same dependency that we've seen since we began our projects. In our case, modules A, B and C are independent, but our production server needs these three AMP to match our user's needs. For example, we want to integrate Alfresco RM module, SharePoint module and our own module including our personalization.
  • Dependencies (weak and strong): In this case, one module needs another module to work. A simple example is the association that may exist between a model of data and actions associated with this data model. If the model doesn't exist and if is not installed then B can't work. If B is changed so it changes the behavior of A.

How to define (and test!) these relationships in our project?

Simple relationships

First, we will use our historical alf-amp-osecm module.

Currently, our module has only dependencies to libraries already present in the Alfresco war. There are no problem because when we start our jetty server with Alfresco war.

But now I want to test my module with the presence of another modules.

Today for example, I'll take the module SDK-TaggingSample (previously created - cf. Post here). The idea is to
  • add SDK-TaggingSample module into Alfresco.war,
  • package my alf-amp-osecm module
  • run jetty to test the intregation and interaction between these modules

To add SDK-TaggingSample module, we will modify the pom.xml of alf-amp-osecm and the declaration below between dependencies tags in the webapp Profile.


org.alfresco.sample
SDK-TaggingSample
amp
1.0



At this stage, we might say, it's good ! We can launch our application with the maven command:

mvn clean integration-test -P webapp,clean-jetty -DskipTests=true


and we will have an Alfresco started with our two modules. Well in fact no... That would be too easy!

The AMP format is a custom format which is not well recognized by Maven and related plugins. Maven recognized this format thanks to maven-amp-plugin. But here we add an AMP dependency! The question is : Does Maven know how to integrate this AMP into exploded war necessary to jetty server ? The answer is obviously NO!

The solution lies in the declaration of a new plugin: maven-dependency-plugin. This plugin as its name suggests can manipulate dependencies and artifacts and therefore AMP! We will configure this plugin to use our dependency and install AMP into our exploded war.

How ? We have to add the configuration below. It has to be incorporated in webapp profile (in my case, it is now in the project alf-sdk. re-factoring required! Nevertheless the declaration of dependency is in my pom.xml)


org.apache.maven.plugins
maven-dependency-plugin


unpack-amps
process-resources

unpack-dependencies


amp
${build.directory}/${webapp.name}
META*





org.alfresco.maven.plugin
maven-amp-plugin
3.0.2





Note that this plugin will take all the dependencies of AMP type and will execute the goal: unpack-dependencies in the directory $ (build.directory) / $ (webapp.name). In other words, it will install AMP as would (almost) Alfresco Module Management Tool.

Next, we must add a testRessource block in webapp Profile .


true
.

module.properties

alfresco/module/${project.groupId}.${project.artifactId}



This block is necessary when we want to integrate several AMP in a war (which is our case here).

Now we can run the command:

mvn clean integration-test -P webapp,clean-jetty -DskipTests=true


and check (after a few minutes) our working AMPS!

Technology is beautiful sometimes ;o)

Dependencies

Now we will focus on dependencies. In this case, I don't want my Alfresco app starts if my module doesn't have all necessary dependencies. If this case happens, we would have an unstable application that could damage data! (I obviously don't wish it...).

How to define these strong dependencies between my modules?

Due to significant changes in my code, I want to create a strong link between my alf-amp-osecm and SDK-TaggingSample. To do this, there is a mechanism defined in the AMP and available via module.properties (located at the root of our project).
At the end of this file, simply add the line below to create a dependency:

module.depends.org.alfresco.sample.SDK-TaggingSample=0.1-*


Note the simple formalism:
  • Module.depends: to define a dependency module
  • groupId.artifactId or module name : to identify the module
  • XX - YY: To determine version range of the module to be taken into account (between XX and YY). In our exemple, our module depends on SDK-TaggingSample from version 0.1. (Note the possibility to use wildcard character that means any)
For more information: wiki.alfresco.com

You can now start the application via this Maven command:

mvn clean integration-test -P webapp -DskipTests=true


As you can see, nothing change! It's good but what we would like here is to verify that our mechanism works well. For this you have two choices.
  • Change the version number in the file module.properties. For example: module.depends.org.alfresco.sample.SDK-TaggingSample = 99.99-*
  • Remove SDK-TaggingSample dependency in dependencies block in our pom.xml.

In both cases, restart the application and you will see in the console a nice stacktrace! It prohibits that you start the application if the dependency is not respected!


That's all for today!

Mavenize well!

PS :
alf-sdk sources are available here:
http://www.box.net/shared/exremqsr0m



alf-amp-osecm sources are available here:
http://www.box.net/shared/dzige0nchr

Read More...

mardi 27 avril 2010

Maven + Alfresco : Alfresco SDK Migration




Welcome Again (maybe...)!

To continue my series about Maven and Alfresco practice, I propose today a straightforward exercise. Your mission, if you accept it, is to change Alfresco SDK samples to our custom AMP format !

What for ? Clearly... It's only for practice. With this exercice, you can see the benefits and limitations of this approach. This approach allow you to understand how maven build, test and integrate Alfresco AMP.

For information about SDK, you can find this url very useful : http://wiki.alfresco.com/wiki/Alfresco_SDK_3.3

Currently I have successfully migrated following samples:
  • SDK Custom Dialog
  • JSP Custom SDK
  • Custom Log SDK
  • Custom SDK Wizard
  • Tagging SDK Sample
Each sample inherit the project alf-sdk. That's why you can execute commands below to:
  • Run unit tests: mvn clean test
  • Install the project in the local repository: mvn install
  • (Reset)Initiate the database /: mvn clean -P clean-jetty
  • Restart the application: mvn integration-test -P webapp

If you followed my previous posts, you should be able to do the same thing. Otherwise please see SOURCES of each project that I made.

See you soon for new adventures !
Read More...

dimanche 25 avril 2010

Maven + Alfresco : Jetty, Boostrap and Profil



Hello everyone!

This time I'll show you how you can bootstrap (initialize) data during the installation of our AMP and how we can test it easily.


1. Bootstrap

First of all, there are several ways to create a bootstrap
  • by XML files,
  • programmatically (via a java class).
The advantage of the first approach is its simplicity! Indeed, a bootstrap XML is nothing more than the equivalent of the import / export available in Alfresco Explorer. You know the green arrows on a folder in Details screen ... Anyway... Nodes are exported as XML and then (re) integrate into a (new) Alfresco instance. A typical example is the transition between a test server to a production server. On the DEV server, the development team has created a specific project tree, which must be installed on the PROD server automatically . This must be done during the first installation of our AMP on the PROD server.

The limitation of this approach is that only object creation is possible. You can not delete or change nodes...

The advantage of the second approach is its richness! Indeed, we have access to all Alfresco services plus (possibly) services of our project. We can do whatever you want .. You can reorganize a tree, perform a mass change of nodes or transform some contents ...

The disadvantage : you need to know programming :o/ and know how Alfresco service works.

In following I am going to explore the first approach. I may focus on the second approach in a future post ..

1.1 Boostrap via XML
So first, I'll explain the approach by an XML file.
In our example of bootstrapping, I want to create four spaces: a root space : BASE DOCUMENTAIRE (don't forget I'm a little french) + 3 sub-spaces PROJET 1 PROJET 2 PROJET 3. These spaces have been previously created in my Alfresco TEST instance. Now I want to boostrap my project with this data. In this manner, I can share data with any instance of Alfresco.

1.2 Export
First I export spaces in my Alfresco TEST (via the export action), then I download locally the ACP file generated in order to extract the XML file. My XML file is called spaces.xml (orginal isn't it ...).

For more information on the IMPORT / EXPORT: http://wiki.alfresco.com/wiki/Export_and_Import

1.3 Integration in the project
I will add this file to the new directory /alf-amp-osecm/src/main/config/bootstrap. Currently, this file is orphaned and will not be taken into account. We must declare it in Alfresco (and in Spring of course...).
For this, we will create a new file called bootstrap-context.xml in the directory /alf-amp-osecm/src/main/config/context















/${spaces.company_home.childname}
alfresco/module/fr.opensourceecm.alf-amp-osecm/bootstrap/spaces.xml






To summarize how to understand this file?
Literally, that's what he means:
We create a Spring bean identified by osecm.bootstrapSpaces . It inherits module.baseComponent. This bean is used to import spaces. It applies to the module fr.opensourceecm.alf-amp-osecm (our AMP), since version 0.0.4. Treatment of bean (hence the import) is required only once (so only during the first start). It takes as a parameter "bootstrapViews". This will allow us to create our tree structure as a subspace of {spaces.company_home.childname}

Simple isn't it ? :o)

1.4 Creating the project
Now we have our bootstrap. We just have to test it ! We can make a package of our AMP via the maven command

mvn clean install


In target folder, you have alf-amp-osecm-0.0.4.amp. It's possible to integrate it into your alfresco.war and test it.

Copy/paste the file alf-amp-osecm-0.0.4.amp to the amps directory in your Alfresco installation. Then run the installation script : apply_amps.bat in your root Alfresco installation folder. Follow instructions, wait few seconds and after that you have a new alfresco.war in tomcat/webapps folder.

Now you can run Alfresco and verify the integration of your custom module.

For more information : http://wiki.alfresco.com/wiki/AMP_Files

2. Starting and Testing Alfresco via Maven

If you've followed all of preceding posts on integrating Alfresco + Maven, you realized that you spend a lot of time to incorporate an AMP in Alfresco... Copy / paste is well but is a bit repetitive... The idea to improve the process would execute a maven command to start Alfresco !

How?
Well through the use of new plugins: maven-jetty and war!

Jetty is a lightweight HTTP container (just the same style as Tomcat ...) which will enable us to launch Alfresco. Maven-war allow us to create the war of our application.

Simply, we want to create an exploded alfresco.war in a subdirectory of target folder (with maven-war plugin) and launch it as a web application (with the Jetty server).

Here we go!

2.1 Profile
In fact the majority of the configuration work is done at our project's pom.xml. We will create what is called a PROFILE. A profile is a set of maven action made optional or not by the user during the execution of a command. It can be seen as a conditional statement controlled by the user. In this case, we try to start an Alfresco to verify that bootstrap has worked. We also try to verify if our AMP works.

However, before setting up our profile, we need to create some properties files.


2.2 Jetty-env.xml

Above all, we create jetty-env.xml in /alf-amp-osecm/src/test/properties/local. This file configure connection to the database via JNDI.




jdbc/dataSource


jdbc:mysql://localhost/${alfresco.db.name}
${alfresco.db.username}
${alfresco.db.password}





2.3 Application.properties
if you say new environment inevitably you say new repository configuration. We will create a new file for this called application.properties and we will add it to the directory: /alf-amp-osecm/src/test/properties/local
This file contains a set of key / value which can be used by the pom.xml and by dev.properties file.

webapp.name=alfresco-jetty
alfresco.data.jetty=alf_data_jetty
alfresco.data.location=./alf_data_jetty
alfresco.db.driver=org.gjt.mm.mysql.Driver
alfresco.hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect
alfresco.db.url=localhost
alfresco.db.name=alfjetty
alfresco.db.username=alfresco33
alfresco.db.password=alfresco
webapp.log.level=error
webapp.log.dir=
module.log.level=debug

2.4 Dev.properties
Here we will create a file called dev.properties and we'll add it to the directory: /alf-amp-osecm/src/test/properties/local. This file create the link between variables defined in application.properties and dev-context.xml. When creating our war, this file will overwrite our dev.properties previously defined.

dir.root=${alfresco.data.location}
index.recovery.mode=AUTO
integrity.failOnError=true
db.test.name=${alfresco.db.name}
db.username=${alfresco.db.username}
db.password=${alfresco.db.password}
db.host=${alfresco.db.host}
db.port=${alfresco.db.port}
db.driver=${alfresco.db.driver}
db.url=jdbc:mysql://${db.test.host}:${db.test.port}/${db.test.name}
hibernate.dialect=${alfresco.hibernate.dialect}


2.5 properties
Now we can attack the configuration of our pom.xml. First we will declare two property : env and webapp.name. Those properties are defined after project declaration block and before the dependencies block .


local
alfresco-jetty


2.6 Create Profile
As the title indicates, we will now create a block profile called webapp. Its main default goal is jetty: run-exploded (ie launch exploded war as a webapp application into jetty). This block is to add at the end of our pom.xml but before the project closing tag.



webapp


src/test/properties/${env}/application.properties

jetty:run-exploded


... Following points...




true
src/test/resources

alfresco/desktop/*.*



true
src/test/properties/${env}
alfresco/extension

dev.properties






org.alfresco.sdk
alfresco-community-war
3.3
war






Note several points:
First we added FILTER via the file application.properties. It will allow us to use all the variables defined in this file in the rest of the pom.xml and more specifically in our profile.

Then we addedalfresco-community-war (ie the application Alfresco Explorer) as a dependency. Of course, we have previously added to our Maven repository local via a maven command :


call mvn install:install-file -Dfile=alfresco.war -DgroupId=org.alfresco.sdk -DartifactId=alfresco-community-war -Dversion=3.3 -Dpackaging=war


After that we added a testResource. It allow us
  • to ignore desktops actions into our AMP (As reminder desktop actions are simply necessary for unit testing in Eclipse)
  • to declare repository configuration defined by dev.properties.


2.6 Configure Maven-War plugin
This plugin will allow us to deploy the alfresco war and to add our configuration files.


org.apache.maven.plugins
maven-war-plugin
2.0.2


it
package

exploded


true




false
${project.build.directory}/${webapp.name}

false

licenses/**


${project.build.testOutputDirectory}
WEB-INF/classes
true

**



src/test/properties/${env}
WEB-INF
true

jetty*







We note in resources block that we
  • Add all configuration files in src/test/resource/alfresco/* (equivalent to ${project.build.testOutputDirectory}

  • Add jetty-env.xml for JNDI database setting


2.8 Configure Jetty plugin

This plugin will enable us to launch the alfresco application and to declare sources of our project.


org.mortbay.jetty
maven-jetty-plugin
6.1.22


it
integration-test

run-exploded


/${webapp.name}
${project.build.directory}/${webapp.name}
10


8080
60000







mysql
mysql-connector-java
5.0.3
provided





Don't forget the dependence with mysql!

2.9 Launch application
Now you are ready to launch your application via a simple command

mvn clean integration-test -P webapp


For information, we use a profile by adding the parameter -P followed by the profile name. If you do not want to play the tests, add the parameter -DskipTests = true at the end of the command.

A few minutes later if all goes well, you can open a browser and type the address http://localhost:8080/alfresco-jetty/

You realize then the presence of a space called BASE DOCUMENTAIRE!

Well Done!


3. Extras

Generally, a bootstrap test does not work the first time! If you want to retry again, it would be useful to reset our application (and your repository).

Now you are familiar with profiles and configuration plugin, so I want to share this extra profile : clean-jetty. Its main goal is to erase your database, stores and lucenes indexes.
It's your turn to place it judiciously in the pom.xml and eventually to improve it!



clean-jetty



maven-clean-plugin
2.4

true


target

war/**


**



alf_data_jetty

oouser/**






org.codehaus.mojo
sql-maven-plugin
1.4



mysql
mysql-connector-java
5.0.3



org.gjt.mm.mysql.Driver
jdbc:mysql://localhost/
root
admin
true



drop-db-jetty
pre-clean

execute


true
drop database IF EXISTS alfjetty



create-db-jetty
clean

execute


true
create database alfjetty default character set utf8 collate utf8_bin










Hope you enjoy this post. Rendez vous to the next chapter!

A ++


PS
All sources of alf-amp-osecm can be downloaded at:
http://www.box.net/shared/bpcx8kl7dn

Read More...

mercredi 21 avril 2010

Maven + Alfresco : Unit Test



Hello world!

In this post, I'll try to share my approach to unit tests with Maven during AMP creation.
Of course if I say TEST, I say CODE! So prepare your eclipse !


Objectives
:
As a developer and integrator my goal is multiple :
  • First of all I want to run unit tests in Eclipse. I want to keep the debugger. It helps me a lot during coding!
  • Then I want to run all my unit tests via Maven. So when I want to create a version of my AMP, it has to pass all tests! I think it's a good practice (^^)

So how?
If you followed my 2 previous posts here and here, we have in our possession two AMP (well. .. 2 projects MAVEN to be exact ...).

  • First, alf-sdk. It is a kind of PARENT-AMP which contains all dependencies of Alfresco SDK and all plugins to create an AMP.
  • Second, alf-amp-osecm. This is our AMP. It inherit alf-sdk.

We will modify these 2 projects to implement unit tests.

1. Updating ALF-SDK

First we will put some news in our parent pom. The main changes occur at build tag.

  • Maven-compiler-plugin and maven-resources-plugin. They received parameters for encoding.
  • Maven-eclipse-plugin has been installed. We will see in the next paragraph why ...





4.0.0
fr.opensourceecm
alf-sdk
pom
3.3C
Maven - Alfresco SDK 3.3C
Super POM Maven Alfresco SDK 3.3C



....





true
src/main/resources

**README-*



true
src/main/config
alfresco/module/${groupId}.${artifactId}

**README-*







org.apache.maven.plugins
maven-eclipse-plugin
2.8


org.springframework.ide.eclipse.core.springnature


org.springframework.ide.eclipse.core.springbuilder





org.apache.maven.plugins
maven-resources-plugin
2.2

UTF-8




org.apache.maven.plugins
maven-compiler-plugin

1.6
1.6
UTF-8
true




org.alfresco.maven.plugin
maven-amp-plugin
3.0.2
true


false

alfresco/extension/**,alfresco/module/**







Once modified, you can install and deploy the artifact locally with a simple command maven :

mvn clean install


In our repository, we now have access to the module fr.opensourceecm:alf-sdk:pom:3.3C
Projects inheriting from this artifact will automatically receive the updates.


2. Updating our AMP

Now we'll create an Alfresco action and a test. As reminder, an action is a unit of work on Alfresco Repository. It can be run via a rule or directly by the user via Alfresco Explorer.
  • In our case, the action simply performs the creation of a sub-space.
  • The test class will start a background Alfresco (creation of a store), create a space, launch the action on this space and check the creation of one new sub-space.
2.1 Adding an action Alfresco
In the directory /alf-amp-osecm/src/main/java/fr/opensourceecm/alf/amp/osecm/actions / I add the action: sampleActionExecuter.java


package fr.opensourceecm.alf.amp.osecm.actions;

import java.util.List;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;

public class sampleActionExecuter extends ActionExecuterAbstractBase{

private FileFolderService fileFolderService;

private final String FOLDER_TEST_NAME = "FolderTest";

@Override
protected void executeImpl(Action arg0, NodeRef actionUponRef) {
List listNodeRef = fileFolderService.listFolders(actionUponRef);
fileFolderService.create(actionUponRef, FOLDER_TEST_NAME + "_" + listNodeRef.size(), ContentModel.TYPE_FOLDER);
}

@Override
protected void addParameterDefinitions(List arg0) {
// TODO Auto-generated method stub

}

public void setFileFolderService(FileFolderService fileFolderService) {
this.fileFolderService = fileFolderService;
}
}


Note : To create our subspace, we use the FileFolderService and the name of our space will be the concatenation between FolderTest and number of folder present.

2.2 Adding a unit test
In the directory /alf-amp-osecm/src/test/java/fr/opensourceecm/alf/amp/osecm/actions, I add the test: sampleActionExecuterTest.java


package fr.opensourceecm.alf.amp.osecm.actions;

import junit.framework.Assert;

import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.BaseAlfrescoTestCase;
import org.junit.Test;

public class sampleActionExecuterTest extends BaseAlfrescoTestCase {

protected NodeRef companyHomeRef;
protected NodeRef rootFolderTestRef;
protected final String rootFolderTestName = "ROOTFOLDERTEST";

@Test
public void testIntegrerFluxCreationParAction() throws Throwable {

// Création d'un répertoire de test dans le store de test.
rootFolderTestRef = serviceRegistry.getNodeService().createNode(
rootNodeRef, ContentModel.ASSOC_CHILDREN,
ContentModel.TYPE_FOLDER, ContentModel.TYPE_FOLDER).getChildRef();

//Lancement de l'action de création d'un sous-répertoire.
Action action = serviceRegistry.getActionService().createAction("createFolderTest-action");
serviceRegistry.getActionService().executeAction(action,rootFolderTestRef);

//Vérification de la présence du sous-répertoire.
Assert.assertEquals("ERREUR LORS DE LA RECHERCHE", 1, serviceRegistry.getNodeService().getChildAssocs(rootFolderTestRef).size());
}
}


Here I draw your attention to the inheritance. sampleActionExecuterTest extends the class BaseAlfrescoTestCase. It's a util class which allow to start a spring/alfresco context. In my class, I just create a test to start my work and verify the result.

2.3 Configuring the pom.xml
Our pom.xml will of course changed after this integration of Java code. In fact, Maven must know dependencies needed to compile classes. In this sample, we need
  • Alfresco-repository-3.3 for Alfresco services
  • Org.springframework.beans-3.0.0 to create Spring context
  • Junit for unit testing.
Our pom.xml becomes:




fr.opensourceecm
alf-sdk
3.3C

4.0.0
alf-amp-osecm
jar
0.0.3
Alfresco AMP Open Source ECM
Open Source ECM - Extension



org.alfresco.sdk
alfresco-repository-3.3
3.3
community
provided


org.alfresco.sdk
org.springframework.beans-3.0.0
3.3
community
provided


junit
junit
4.7
test





IMPORTANT NOTE: The packaging is JAR! Don't put it into AMP (it's a little "trick" to configure the project in Eclipse ...)

2.4 Configuring the Eclipse workspace
Generally (and if you've followed instructions above) you have never opened Eclipse for the previous manipulations! I agree ... is difficult. But you have to know that a Maven project generated is not directly integratable in Eclipse! In fact it is not planned for! There is no .classpath, .project file or .settings for configuring your project in Eclipse.
But Maven has a plugin to automatically create all of these configuration files. This is the maven-eclipse-plugin defined previously in the parent pom.

To create this configuration, type a command in the root directory of the maven project :

mvn eclipse:eclipse


Automatically, maven-eclipse.xml, .project and .classpath files have been created as well as .settings directories and ExternalToolBuilders (and eventually target).

Note: For users of Spring IDE, the project will have the nature of project Spring by default.

Note: If you have already joined the project in eclipse, or if you want to delete the configuration eclipse, type the following command:

mvn eclipse:clean


2.5 Importing the project in eclipse

You can now launch an eclipse (3.5R2 Galilleo for me) and import the project into your workspace. The project now looks like the image below:



Note: If you do not have this configuration, make sure you launch the command eclipse while the packaging is JAR. Indeed, if you execute this command with amp packagin the plugin will build another tree.

2.6 (Re) Definition of spring beans

We will now modify the declaration of spring beans for each part of our AMP to improve readability. For this, we will specialize a directory : context .

So in the \alf-amp-osecm\src\main\config, create a directory context and create 3 files:
  • action-context.xml
  • model-context.xml
  • webclient-context.xml

action-context.xml











model-context.xml





alfresco/module/fr.opensourceecm.alf-amp-osecm/model/ecmModele.xml






This statement, as its name suggests will allow us to define the data model. Note that in my previous post, I replaced fr.opensourceecm.alf-amp-osecm by (groupId) and (artifactId). But here, I want Eclipse to be able to understand after a compilation. Indeed Maven is the only one capable of replacing these values! So we must declare this value ...

Note: In general, groupId and artifactId must be defined at the beginning of your project.
Note: I take advantage of refactoring to simplify the name of my data model.

Now we will create the file
webclient-context.xml





alfresco/module/fr.opensourceecm.alf-amp-osecm/ui/ecm-web-client-config.xml







This statement, as its name suggests will allow us to define the configuration of the web client.
Note: I take advantage of refactoring to simplify the name of my web client config.


In the \alf-amp-osecm\src\main\config, edit the file:
module-context.xml









This file will serve to reference all statements necessary for our Spring module. In our case, to summarize, our module have : a data model, an action and a web client configuration.

2.7 Definition of our test environment
Now we will define and configure our Alfresco repository. Ie we will define the configuration settings of the database and where we put our folder (content-store and lucene indexes).

For this purpose in the directory /alf-amp-osecm/src/test/resources/alfresco/extension/ (create the missing directories if necessary) create the file

dev.properties

dir.root=./alf_data
index.recovery.mode=AUTO
integrity.failOnError=true
db.name=alf33Test
db.username=alfresco33
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}
hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect


This file defines all properties of our test environment. In my case, I already installed and configured a MySQL database.

For more information on how to configure an environment, please download the following pdf: Manual Configuration and Installation of Alfresco 3.3

Then in the same directory, you must create the file
dev-context.xml






classpath:alfresco/repository.properties
classpath:alfresco/domain/transaction.properties


classpath*:alfresco/module/*/alfresco-global.properties

classpath*:alfresco-global.properties
classpath:alfresco/extension/dev.properties



SYSTEM_PROPERTIES_MODE_OVERRIDE




hibernate.dialect
hibernate.query.substitutions
hibernate.jdbc.use_get_generated_keys
hibernate.default_schema







This bean will identify the properties file previously created. This is used only during maven test phase

NOTES
In this step, you can add a directory /alf-amp-osecm/src/test/resources/alfresco/desktop containing the default desktop ations Alfresco (available in the sdk). Apparently it can't have them in my environment ...

In this step, you can also add a log4j.properties file in the directory /alf-amp-osecm/src/test/resources to determine levels of logs.

2.8 Changing the pom.xml

We now have an environment ready. You just have to change the packaging tag in pom.xml to AMP.

2.9 Starting the Packaging

Now your project should look like the image below:


The project is complete, you can start packaging via the command

mvn clean install


If all goes well, you see the famous and coveted BUILD SUCCESSFULL and if you pay attention to the console, it display the results of tests like
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

If so you have your first AMP tested by Maven.

CONGRATULATIONS!

In the target directory of the project, you have packaged your AMP: alf-amp_osecm.0.0.3.amp. You can install it on your local Alfresco instance.


2.10 Starting the test via Eclipse
As I said at the beginning, I also want to use Eclipse to debug my application if necessary. I execute my JUnit test and verify the result.




Goals achieved!


That's all for today!
If you have any questions, remarks or bug / improvement to report for example, feel free to post a comment.

For this part, I wish to thank G. Tournier for his help and advice.
Without him Alfresco nodes would probably still a big mystery to me.

Stay Tuned !

PS

All sources of alf-amp-osecm can be downloaded at:
http://www.box.net/shared/374lms3ogv


All sources of alf-sdk can be downloaded at:
http://www.box.net/shared/oevx14hy8n
Read More...

Maven + Alfresco : SDK, Dependencies, Inheritance




Hello,

This time, I want to discuss about the integration of Alfresco SDK and dependencies in creation . The goal of this post is to explain how we can manage AMP dependency with Alfresco SDK.

If you are not familiar with dependency and maven, have a look at this adress

Actually, there's two mode to manage dependency.
  • Eclipse project mode or Build Path Dependency
  • Maven Mode or Maven Dependency Mechanism

Eclipse mode
In Eclipse mode, dependency are managed by links between Eclipse Projects. It's your responsability to define your builpath and classpath via Eclipse settings. In our case, we have a link between our eclipse AMP Project and Alfresco SDK. In details, this is the procedure to add SDK dependency with our project :
  • First, you have to download Alfresco SDK and unzip it in a directory. (let's say ALF_SDK_HOME)
  • Then in Eclipse, you have to perform the operation "Import Existing Project", usually AlfrescoEmbedded SDK project, in the directory ALF_SDK_HOME\lib
  • Always in Eclipse, you have to configure AMP Project Build Path. Most of the time, you have to open "Configure Build Path" form and add AlfrescoEmbedded SDK in "Project" tab.
  • Once all these steps accomplished, you can use the SDK in Eclipse. All links and dependencies are now available for auto-completion and compilation in Eclipse.

Maven Mode
In this mode, you have all AlfrescoEmbedded SDK dependencies integrated in a Maven 2 repository. It could be your local, your project, your company or a third party maven 2 repository. It doesn't really matter. You have just to know where to find it!

For information, Alfresco has recently open its own repository at Alfresco Nexus / Maven. Nevertheless, all AlfrescoEmbedded SDK dependencies are not available. But day after day it's going to be better ! Thanks at @Gabriele Columbro to his work http://mindthegab.com/

Now the job is to search, define and identify in our project pom.xml all AMP dependencies with the n-tuple : groupId, artifactId, version, classifier, packaging. According to this parameters, we can retrieve all dependency you want! When the pom.xml is complete and comprehensive, it becomes possible to compile and test the application with Maven.

But that's not enough... I'm a developper and I WANT(!!) my project into Eclipse ! How can I do if all is manage with Maven ??? Simple answer : Maven can generate Eclipse configuration thanks to a maven-eclipse-plugin. With just on Maven command : mvn eclipse:eclipse, you can (re)generate Eclipse configuration. But it's note the right time...

Anyway, it is this second method I will try to present.


How to set up Alfresco SDK in a Maven 2 repository
and use it to build an AMP


Note and warning: This procedure is far (very far even ...) to be the cleanest or most aesthetic method. It does not necessarily meet the very basic principles of Maven.
That's why it should be used with caution!


1. Integrating Alfresco SDK locally

As you know, Alfresco SDK includes all dependencies needed to run an Alfresco and to use it.

Problem : How can we add this dependency into our Maven repository ?

Via a small project written in Java and compiled using Maven (if you want to make Maven as well do it anywhere ^^ ).

Principle?
List all dependencies present in AlfrescoEmbedded SDK and integrate them into our repository via a script (. Bat or. Sh)

Code?


package fr.opensourceecm;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;

public class AlfrescoMavenSDKCreator {

public static String isWindows = "O";

public static String pathToSDK = "D:\\opt\\JM-Alfresco\\SDK\\3.3C\\lib\\server";
public static String sdkGroupId = "org.alfresco.sdk";
public static String sdkVersion = "3.3";
public static String sdkClassifier = "community";
public static String mavenLocation = "call mvn";

private static final String ln = System.getProperty("line.separator");

private static void execute() {

String nomFichier = null;

FileWriter fout1 = null;
FileWriter fout2 = null;
try {

showVariables();

System.out.println("Voulez-vous modifier ces paramètres (O/N) ? ");
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
String reponse = in.readLine();
if (reponse.toUpperCase().equals("O")) {
System.out.println("Windows (O/N) ? ");
isWindows = in.readLine();
System.out.println("PATH SDK ? ");
pathToSDK = in.readLine();
System.out.println("Version d'Alfresco ? ");
sdkVersion = in.readLine();
System.out.println("GroupID ? ");
sdkGroupId = in.readLine();
System.out.println("emplacement de Maven (binaire) ? ");
mavenLocation = in.readLine();
System.out.println("classifier (labs/enterprise) ? ");
sdkClassifier = in.readLine();
}

if (isWindows.toUpperCase().equals("O")) {
nomFichier = "maven-sdk-install-files.bat";
} else {
nomFichier = "maven-sdk-install-files.sh";
}

File file1 = new File("maven-dependencies.txt");
fout1 = new FileWriter(file1, false);
BufferedWriter out1 = new BufferedWriter(fout1);

File file2 = new File(nomFichier);
fout2 = new FileWriter(file2, false);
BufferedWriter out2 = new BufferedWriter(fout2);

if (isWindows.toUpperCase().equals("O")) {
out2.write("cd " + pathToSDK + ln);
out2.write(ln);
} else {
out2.write("#!/bin/bash" + ln);
out2.write(". /etc/profile" + ln);
out2.write(ln);
out2.write("cd " + pathToSDK + ln);
out2.write(ln);
}

ArrayList listingFiles = getFiles(pathToSDK);

for (File file : listingFiles) {

String librairieNameJar = file.getName();
String librairieName = librairieNameJar.substring(0,
librairieNameJar.length() - 4); // moins ".jar"

out1.write("" + ln);
out1.write(" " + sdkGroupId + "" + ln);
out1.write(" " + librairieName + "" + ln);
out1.write(" " + sdkVersion + "" + ln);
out1.write(" " + sdkClassifier + "" + ln);
out1.write(" test" + ln);
out1.write("
" + ln);

out2.write(mavenLocation + " install:install-file" +
" -Dfile=" + file.getPath().substring(pathToSDK.length() + 1) +
" -DgroupId=" + sdkGroupId +
" -DartifactId=" + librairieName +
" -Dversion=" + sdkVersion +
" -Dpackaging=jar" +
" -Dclassifier=" + sdkClassifier +
" -DgeneratePom=true" +
" -DcreateChecksum=true" + ln) ;

out1.flush();
out2.flush();
}
fout1.close();
fout2.close();

System.out.println("Les fichiers ont été générés : ");
System.out.println(file1.getAbsolutePath());
System.out.println(file2.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

private static void showVariables() {
System.out.println("This utils is going to generate two files");
System.out.println("");
System.out.println("Le programme va s'ex�cuter avec les paramtres suivants :");
System.out.println("- pathSDK : " + pathToSDK);
System.out.println("- Windows : " + System.getProperty("os.name"));
System.out.println("- GroupID Alfresco : " + sdkGroupId);
System.out.println("- version d'Alfresco : " + sdkVersion);
System.out.println("- classifier : " + sdkClassifier);
System.out.println("- emplacement de Maven (binaire) : " + mavenLocation);
}

private static ArrayList getFiles(String root) {
ArrayList listeFichiersJar = new ArrayList();
getFilesRec(listeFichiersJar, root);
return listeFichiersJar;
}

private static void getFilesRec(ArrayList listeFichiersJar, String root) {
File f = new File(root);
File[] listFiles = f.listFiles();
for (int i = 0; i < listFiles.length; i++) {
if (listFiles[i].isDirectory()) {
getFilesRec(listeFichiersJar, listFiles[i].toString());
} else if (listFiles[i].getName().endsWith(".jar")) {
listeFichiersJar.add(listFiles[i]);
}
}
}

public static void main(String[] args) {
(new AlfrescoMavenSDKCreator()).execute();
}
}


Warning : My code formatter (in this blog) sometimes modify or delete some part of the code. If you want to be sure to have the correct one, download the source of this project at the end of this post.

Running this small program from the command line interface or via Eclipse create two files.

The first file (a. Bat) can install dependencies in the repository. This file launch a serie of call maven as :
call mvn install:install-file -Dfile=alfresco-core-3.3.jar -DgroupId=org.alfresco.sdk -DartifactId=alfresco-core-3.3 -Dversion=3.3 -Dclassifier=community -Dpackaging=jar


We can notice from this sample, there's a "problem" in version number! Indeed, my script will generate an artifact name like : alfresco-core-3.3-3.3-community.jar.
It's a little bit non conform with Maven principle... That's why I consider this method as not aesthetic... but clearly the fastest!
If you want to resolve this problem, you have multiple choice.
  • First, identify one by one each dependency (and repository). Next, create files... if you have time to spend...
  • Second, you have to wait that Alfresco will add all dependency in his official repository or create a pom.xml with them.
  • Third, it's a mix between all of preceding solutions.
The second file creates all dependencies declarations. It can be easy to integrate them into a pom.xml This file contains a list of element below :


org.alfresco.sdk
alfresco-core-3.3
3.3
community
test



All dependencies have TEST scope. Indeed Alfresco already has all of its libraries. It is therefore not necessary to add them at the creation of our AMP. By cons it is needed to test the module.

Now how to use dependencies?
A strong point of Maven is its inheritance ability between artifact (plugin). It's the same as Java Classes inheritance. On object can inherit from its parent object.

In this context, one idea is to consider Alfresco SDK as the father of our draft AMP. Thus All SDK dependencies can be available for the project son.

2. Create a Super Pom Alfresco SDK


1. Creating a Maven 2 project
Via this command line in a directory :

mvn archetype:create -DgroupId=fr.opensourceecm -DartifactId=alf-sdk -Dpackagename=fr.opensourceecm.alf.sdk
you can create the whole tree of a jar Maven 2 project type.

2. Cleaning
I delete subdirectories from src/main/java and src/test/java to remove default generated class .

3. Changing pom
Now I take the content of maven-file dependencies.txt and I add it between &alt;depedencies> tags in my pom.xml. Next, I modify the defintion of my artifact to create : fr.opensourceecm: alf-sdk: pom: 3.3C.
Indeed, I know this project will become a parent project, so it must be automatically package as pom. Finally I add the plugin configuration for the build phase.
This produces the file below :


4.0.0
fr.opensourceecm
alf-sdk
pom
3.3C
Maven - Alfresco SDK 3.3C
Super POM Maven Alfresco SDK 3.3C




alfresco-public-snapshots
http://maven.alfresco.com/nexus/content/groups/public-snapshots

true
daily



alfresco-public
http://maven.alfresco.com/nexus/content/groups/public




alfresco-public
http://maven.alfresco.com/nexus/content/groups/public


alfresco-public-snapshots
http://repository.sourcesense.com/nexus/content/groups/public-snapshots

true
daily






org.alfresco.sdk
alfresco-core-3.3
3.3
community
test


org.alfresco.sdk
alfresco-deployment-3.3
3.3
community
test

...

org.alfresco.sdk
mysql-connector-java-5.1.7-bin
3.3
community
test


org.alfresco.sdk
dom4j-1.6.1
3.3
community
test


org.alfresco.sdk
ehcache-core-2.0.0
3.3
community
test

...

org.alfresco.sdk
org.springframework.beans-3.0.0
3.3
community
test


org.alfresco.sdk
org.springframework.context-3.0.0
3.3
community
test

...





true
src/main/resources


true
src/main/config
alfresco/module/${groupId}.${artifactId}






org.apache.maven.plugins
maven-compiler-plugin
2.2

1.6
1.6




org.alfresco.maven.plugin
maven-amp-plugin
3.0.1-SNAPSHOT
true


false








4. Installation and Deployment Project
A simple command maven is enough to create our artifact and deploy it in our local repository :

mvn clean install

In our repository, we now have access to the module fr.opensourceecm:alf-sdk:pom:3.3C

Important !
If you use version 3.3C (like me here), we don't have config.jar in Alfresco SDK. In SDK it's a folder and not a jar. For information, this folder contains all configuration files for Alfresco. So it's clearly important to have it! This means, you have to create a jar of this directory and put it as a dependency in our pom.xml.


org.alfresco.sdk
alfresco-config
3.3
community
test



3. Update alf-amp-osecm project

1. Changing the pom.xml
Now we can modify our AMP Project pom.xml to take into account the parent. Note that we added the parent block in first place to define inheritance.




fr.opensourceecm
alf-sdk
3.3C

4.0.0
alf-amp-osecm
amp
0.0.2
alf-amp-osecm
Open Source ECM - Extension


We realize now that the pom is shorter. The information about dependencies and plugins have been inherited from the project SDK. That's the magic of inheritance!

2. AMP Creation
Now we have everything to create our module. You have just to execute one command in your project folder :

mvn clean install
In target folder, you have alf-amp-osecm-0.0.2.amp. It's possible to integrate it into your alfresco.war and test it.

3. Integration
Copy/paste the file alf-amp-osecm-0.0.2.amp to the amps directory in your Alfresco installation. Then run the installation script : apply_amps.bat in your root Alfresco installation folder. Follow instructions, wait few seconds and after that you have a new alfresco.war in tomcat/webapps folder.

For more information : http://wiki.alfresco.com/wiki/AMP_Files

4. Starting Alfresco
Now you can run Alfresco and verify the integration of your custom module.

WELL DONE !


Here I think, how we can do more with Maven Alfresco.

In the next post will explore how to set up tests with Maven.


PS

All sources of alf-sdk can be downloaded at:
http://www.box.net/shared/2zpgm3fcag

All sources of alf-sdk-osecm (for creating dependency files) can be downloaded at:
http://www.box.net/shared/fe6ftxeb4y

All sources of alf-amp-osecm be downloaded at:http://www.box.net/shared/s966vj1xqz

Read More...