Tuxitecte

mercredi 21 avril 2010

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


Digg Google Bookmarks reddit Mixx StumbleUpon Technorati Yahoo! Buzz DesignFloat Delicious BlinkList Furl

2 commentaires: on "Maven + Alfresco : SDK, Dependencies, Inheritance"

mau a dit…

Hi Jean Marie,
great stuff indeed! I've just noticed that in your POM you're using an obsolete URL for the Sourcesense Public Snapshot repo.

I recently merged http://repository.sourcesense.com/nexus/content/groups/public-snapshots onto http://repository.sourcesense.com/nexus/content/groups/public

HTH
Maurizio

bumbu pecel bali a dit…

this is good post.
and you can go here

http://bantalsilikon01.blogspot.com
http://kursusinternetmarketingmurah.blogspot.com
http://bumbupecelbali.blogspot.com


tanks very much.... :)