Launching Acceleo generation from Maven

I’ve spend some hours last week (and also this week-end ;-)) trying to mavenize a build process that is using Acceleo for generating some artifacts (Java and non java) from a UML model. Because it was a little tricky and still not well documented, I’ve thought it was interesting spending little time to blog that.

04-oct update

I’ve post a sequel to this blog post here : https://lbroudoux.wordpress.com/2012/10/02/launching-acceleo-generation-from-maven-take-2/ with enhanced use case and a demo project available on Github.

So to summarize, the use case is the following : I’ve got a first project that is an Acceleo module (created by Eclipse wizard) that contains my UML 2 Java custom generator ; and a second project that is a real-life application (whatever is the framework used) that contains a UML model that should be processed by my generator.

The generation launched from developer workspace works fine but what I want now is to automate the generation/regeneration of artifacts. That way I would be able to integrate generation into a continuous integration feedback loop (and thus control that developer is not committing code outside the reserved block !).

This schema summarize the artifacts locations, names and dependencies :

Mavenizing this build process involves 3 steps :

  • Distribute Acceleo module as Maven artifact,
  • Prepare Acceleo main class for being used outside of Eclipse,
  • Launch application generation from Maven

Each step is detailed below in terms of noticable lines added into the file modified for this to work.

Distributing Acceleo module as Maven artifact

First thing to do, is to made the Acceleo plugin buildable and distributable using Maven. A good starting point is given here http://wiki.eclipse.org/Acceleo/Maven but is not enough for a true stand alone Maven build (it does not consider distributing java classes and resources).

Here’s the resulting pom.xml file that should be added at the project root. Each higlighted line is explained below.

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.github.lbroudoux.acceleo</groupId>
  <artifactId>com.github.lbroudoux.acceleo.uml.java</artifactId>
  <version>1.0.0.qualifier-SNAPSHOT</version>
  
  <repositories>
    <repository>
      <id>Indigo Release</id>
      <url>https://raw.github.com/eclipse/acceleo/master/maven/repository/releases/indigo</url>
    </repository>
    <repository>
      <id>Snapshots</id>
      <url>https://raw.github.com/lbroudoux/acceleo/master/maven/repository/snapshots</url>
    </repository>
  </repositories>
  
  <pluginRepositories>
    <pluginRepository>
      <id>Indigo Release</id>
      <url>https://raw.github.com/eclipse/acceleo/master/maven/repository/releases/indigo</url>
    </pluginRepository>
    <pluginRepository>
      <id>Snapshots</id>
      <url>https://raw.github.com/lbroudoux/acceleo/master/maven/repository/snapshots</url>
    </pluginRepository>
  </pluginRepositories>
  
  <dependencies>
    <dependency>
      <groupId>org.eclipse.acceleo</groupId>
      <artifactId>maven</artifactId>
      <version>3.3.0</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.uml2</groupId>
      <artifactId>uml</artifactId>
      <version>3.2.1</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.uml2</groupId>
      <artifactId>common</artifactId>
      <version>1.6.0</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.6.4</version>
    </dependency>
    
    <dependency>
      <groupId>org.eclipse.core</groupId>
      <artifactId>org.eclipse.core.runtime</artifactId>
      <version>3.6.0.v20100505</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.uml2</groupId>
      <artifactId>org.eclipse.uml2.uml.resources</artifactId>
      <version>3.1.0.v201005031530</version>
      <exclusions>
      	<exclusion>
          <groupId>org.eclipse.uml2</groupId>
          <artifactId>org.eclipse.uml2.uml</artifactId>
      	</exclusion>
      </exclusions>
    </dependency>
  </dependencies>
  
  <build>
    <sourceDirectory>${basedir}/src</sourceDirectory>
    <plugins>
      <plugin>
        <groupId>org.eclipse.acceleo</groupId>
        <artifactId>maven</artifactId>
        <version>3.3.0</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>acceleo-compile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <useBinaryResources>false</useBinaryResources>
          <usePlatformResourcePath>true</usePlatformResourcePath>
          <acceleoProject>
            <root>${project.basedir}</root>
            <entries>
              <entry>
                <input>src</input>
                <output>target/classes</output>
              </entry>
            </entries>
          </acceleoProject>
          <packagesToRegister>
            <packageToRegister>org.eclipse.uml2.uml.UMLPackage</packageToRegister>
          </packagesToRegister>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
  • Lines 14, 25 and 33 : the 3.3.0 version is used here because it works on Linux 😉 and its dependency resolution is a bit simpler that 3.2.1 version. However, it is not yet available as official release and you should point to my fork of Acceleo (see this pull request : https://github.com/eclipse/acceleo/pull/1)
  • Line 53 : this dependency is required for compiling java resources (main and services) embedded into the Acceleo project
  • Line 58 : because my script uses UML librairies this dependency is necessary as transitive dependency for application that will use this generator,
  • Line 70 : setting the source directory is necessary since Maven will look for Java resources into /src/main/java folder that is not location used when creating module from Eclipse wizard.

Once this pom completed, you should be able to distribute your generator as a Maven artifact using a simple mvn install command (for local distribution).

Preparing main class for outside of Eclipse execution

The next step is to update the mail class of your launcher created by Acceleo wizrd in order to be executed outside of Eclipse. In my case, this class is called com.github.lbroudoux.acceleo.uml.java.main.GenerateAll.

The 2 following register methods are well documented by Acceleo first lines are fairly simple to elaborates. Things go wrong when it comes to lines relatives to UML resources (from 7 to 16) :

@Override
public void registerPackages(ResourceSet resourceSet) {
   super.registerPackages(resourceSet);
   if (!isInWorkspace(UMLPackage.class)) {
      resourceSet.getPackageRegistry().put(UMLPackage.eNS_URI, UMLPackage.eINSTANCE);
      
      if (umlJarPath != null){
         URI uri = URI.createURI("jar:file:" + umlJarPath + "!/");
         Map uriMap = resourceSet.getURIConverter().getURIMap();
         uriMap.put(URI.createURI(UMLResource.LIBRARIES_PATHMAP),
               uri.appendSegment("libraries").appendSegment(""));
         uriMap.put(URI.createURI(UMLResource.METAMODELS_PATHMAP),
               uri.appendSegment("metamodels").appendSegment(""));
	 uriMap.put(URI.createURI(UMLResource.PROFILES_PATHMAP),
	       uri.appendSegment("profiles").appendSegment(""));
      }
   }
}

@Override
public void registerResourceFactories(ResourceSet resourceSet) {
   super.registerResourceFactories(resourceSet);
   resourceSet.getResourceFactoryRegistry().getExtensionToFactoryMap().put(UMLResource.FILE_EXTENSION, UMLResource.Factory.INSTANCE);
}

Lines from 7 to 16 are extracted from this page : http://wiki.eclipse.org/MDT/UML2/FAQ#What.27s_required_to_load_a_UML_.28.uml.29_resource_from_a_standalone_application.3F . Because my model used Uml libraries, references to libraries, metamodels and profiles should be set to point to a true jar file sitting outside of Eclipse. A natural fit for that is the Maven local repository. However, because we’re in a single main file not related to Maven, we have no clue how to access local repository. So we just assumed that complete path for Uml resources jar will be passed as execution arguments of this main file.

The main() method should be enhanced that way :

// If there's a third argument, assume it's Uml ressources jar path.
if (args.length > 2){
   umlJarPath = args[2];
}

Launching generation from Maven

Last step is now to plug the launching of Acceleo generator into the Maven build of our application. This is simply relaized using the Maven Exec plugin that provides an exec:java goal for invoking main classes.

The modified pom.xml file comes as follow :

<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/xsd/maven-4.0.0.xsd">
  <!- ... ->
  <build>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>1.2.1</version>
        <configuration>
          <mainClass>com.github.lbroudoux.acceleo.uml.java.main.GenerateAll</mainClass>
          <arguments>
            <argument>${basedir}/mymodel.uml</argument>
            <argument>${basedir}/src</argument>
            <argument>${settings.localRepository}/org/eclipse/uml2/org.eclipse.uml2.uml.resources/3.1.0.v201005031530/org.eclipse.uml2.uml.resources-3.1.0.v201005031530.jar</argument>
          </arguments>
          <includePluginDependencies>true</includePluginDependencies>
        </configuration>
        <dependencies>
          <dependency>
            <groupId>com.github.lbroudoux.acceleo</groupId>
            <artifactId>com.github.lbroudoux.acceleo.uml.java</artifactId>
            <version>1.0.0.qualifier-SNAPSHOT</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>
  </build>
</project>
  • Line 10 : this is our main class generated by Acceleo and modified during step 2. Remember ? Basically, it has 2 arguments that follow : model location and generation target location,
  • Line 14 : this is the 3rd argument we add to the main class : the complete path of jar containing Uml resources. Note the ${settings.localRepository} syntax for making build portable by resuing the maven local repo,
  • Line 16 and 21 : this is the dependency for dynamically resolving and getting the artifact containing our generator. Note the includePluginDependencies flag that allows declaring this dependency at the plugin level and not project level which is cleaner because it does not make it transitive for future usages.

And that’s it ! By invoking the mvn exec:java command you now able to launch the Acceleo generation diretly from Maven ! You could now : bind this generation to a particular phase of Maven build process, activate this generation within a dedicated profile, etc … to really suit your needs.

Let me know if it help some of you …

Advertisements

11 thoughts on “Launching Acceleo generation from Maven

  1. have you had any luck doing this when you have two different acceleo projects. One which depends on the other?

    1. Hi David,

      actually I have this kind of configuration on one project : an acceleo project containing basic Uml to Java generators and another one containing advanced generation stuffs related to a specific UML profile.

      You just have to use the basic dependency resolution system from Maven : release your first acceleo project as Maven artifact, make the second acceleo project depend on the first one through Maven pom.xml, release your second acceleo project as Maven artifact and finally use it from another project as described into this post.

      The maven-exec-plugin is using Maven transitive dependencies and make the resources bundled into first acceleo project available during generation.

      Regards,

  2. Hi,
    Very interesting post. Did you try this using tycho ?
    I’m a newbee and I have some troubles with this.

  3. Hi, I am trying to follow same steps. The classes are compiled succesfully, and the emtl files are generated also in the outputfiles. The problem is that when i try to execute the java class with the command mvn java:exec it still shows me that .emtl file does not exists although the .emtl files are there
    Any idea?

    1. Hi MEJ,

      have no idea of what problem you may encounter … Have any stacktrace to push here in order to have a better idea ?

      I’m thinking of releasing some sample projects illustrating this post on Github. Let me know if you’ll have any interest in this.

      Regards,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s