Last year, I’ve wrote a post on Launching Acceleo generation from Maven. This was actually a second post on this topic – second post introducing multi Acceleo generators project build.
Things run well during this post redaction but after some weeks and colleagues tests, I realized that Acceleo had some limitations that made this build setup hard to be portable. To summarize : when it comes to referencing modules coming from other projects, Acceleo uses multiple forms to reference paths : relative paths when built dynamically by the IDE,
platform:/ paths when exported as a plugin and absolute jar paths when built via Maven (our case).
To illustrate, here is an excerpt of the
entityFile.emtl module you may find on my sample project, The reference to my own Maven local repository location made it hard to be portable !
<extends href="jar:file:/home/laurent/dev/repository/com/github/lbroudoux/acceleo/com.github.lbroudoux.acceleo.uml.java/0.0.1.qualifier-SNAPSHOT/com.github.lbroudoux.acceleo.uml.java-0.0.1.qualifier-SNAPSHOT.jar!/com/github/lbroudoux/acceleo/uml/java/files/classFile.emtl#/0"/> <imports href="jar:file:/home/laurent/dev/repository/com/github/lbroudoux/acceleo/com.github.lbroudoux.acceleo.uml.java/0.0.1.qualifier-SNAPSHOT/com.github.lbroudoux.acceleo.uml.java-0.0.1.qualifier-SNAPSHOT.jar!/com/github/lbroudoux/acceleo/uml/java/files/commonFile.emtl#/0"/>
When it comes to deployment onto our projects (in my company for my day-time job), these limitations do not really bother us because development and CI machines setup were standardized and we were sure that every local Maven repos were having the same location. I finally put up this problem over my shoulder and forgot it …
… until Dave comments !
Last week, Dave commented out (see its comments) this blog post, remembering me that this issue was left unsolved but still deserves some interest … While Dave is following a pure Java solution, I’m showing in this new post a pure Maven workaround, so let’s go.
A Maven workaround
The principle of this workaround is the following : as referencing other jar archives into the EMTL files make the build not portable, stop using multiple jar archives and use only one uber jar with referenced paths being relatives !
I know that this sounds weird as Maven promotes fine grained and atomic artifacts with transitive dependency resolution and so on … but it also open ways for different forms of artifacts when running/deploying into a constrained environment through the notion of assembly. That is exactly our situation : we’ve got a constrained running environment so we’re going to use an assembly.
The explanation takes place in 3 steps.
Replacing references into EMTL files
The first step is to deal with the referenced jar paths placed into EMTL files by the Acceleo compiler. The goal is to replace them by relatives paths. For this, we can use the Replacer plugin into the build of the Acceleo module referencing other modules.
In my sample project, this means modyfing the
pom.xml file of the com.github.lbroudoux.acceleo.uml.java.jpa module as follow :
<plugin> <groupId>com.google.code.maven-replacer-plugin</groupId> <artifactId>maven-replacer-plugin</artifactId> <version>1.3.1</version> <executions> <execution> <phase>prepare-package</phase> <goals> <goal>replace</goal> </goals> <configuration> <includes> <include>target/**/*.emtl</include> </includes> <regex>true</regex> <token>jar:file:.*.jar!</token> <value>../../../../../../../..</value> </configuration> </execution> </executions> </plugin>
This configuration basically tells to activate plugin on the
prepare-package phase and to process any
emtl file to replace the given regular expression denoting an absolute jar path by this relative path.
On the excerpt we took above, this lead to the following result :
<extends href="../../../../../../../../com/github/lbroudoux/acceleo/uml/java/files/classFile.emtl#/0"/> <imports href="../../../../../../../../com/github/lbroudoux/acceleo/uml/java/files/commonFile.emtl#/0"/>
Please, be sure to note 2 important things :
- the value given for replacement is dependent of the package you for this current Acceleo module files (
com.github.lbroudoux.acceleo.uml.java.jpain this case),
- the value is the same for any EMTL file because sample project follows Acceleo best practice in term of package naming : each package containing generator is at the same deepness from root (not following that best practice make this workaround non applicable in this state – configuration of replacer might be trickier !)
Creating a flattened uber assembly
Next step is now to create an archive that will contains :
- the EMTL and class files of our current Acceleo module (the one reworked during step 1),
- the EMTL and class files of the generators we depend on (their coming from Maven dependencies)
The whole resources should be flattened : all put together into a single package hierarchy, into a single jar file for still being usable as a library.
In order to do that, we start declaring a configuration for the Maven assembly plugin into the
pom.xml of the Acceleo module referencing other modules (check sample project) :
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>2.4</version> <executions> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <descriptors> <descriptor>assembly.xml</descriptor> </descriptors> </configuration> </execution> </executions> </plugin>
This configuration tells to activate assembly during the
package phase (so after the
pre-package) and to refer to descriptor present into
assembly.xml file. This is a new file and you just have to create it into project root folder. Its content is the following :
<assembly xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> <id>uber</id> <formats> <format>jar</format> </formats> <includeBaseDirectory>false</includeBaseDirectory> <dependencySets> <dependencySet> <includes> <include>com.github.lbroudoux.acceleo:com.github.lbroudoux.acceleo.uml.java</include> <include>com.github.lbroudoux.acceleo:com.github.lbroudoux.acceleo.uml.java.jpa</include> </includes> <unpack>true</unpack> <useTransitiveDependencies>false</useTransitiveDependencies> </dependencySet> </dependencySets> </assembly>
The important part here is to specify that our assembly with use a
uber qualifier/classifier for its result archive and that self artifact and dependency artifact should be specified into inclusions.
From now, when doing a
mvn install into this Acceleo module, Maven should now produce 2 artifacts : the main one that we already got and a the new
uber one holding every EMTL reources with relatives paths flattened. That new artifact is attached as secondary artifact to your build process.
Using this new archive for generation
Last step is now to modify our application that integrates Acceleo generators during its own build process : we should now tell it to use the
uber jar we produced at previous step. This modification is simply done editing the
pom.xml of your application and adding a classifier information.
When looking at my sample application file, it represents a single new line highlighted below :
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>1.2.1</version> <!-- Configuration details here --> <dependencies> <dependency> <groupId>com.github.lbroudoux.acceleo</groupId> <artifactId>com.github.lbroudoux.acceleo.uml.java.jpa</artifactId> <version>0.0.1.qualifier-SNAPSHOT</version> <classifier>uber</classifier> </dependency> </dependencies> </plugin>
Uber jar with relatives references is now used and should make your build portable ! You can check the application of this workaround onto my sample project looking at the Github commit.
As always, feedback and comments are greatly appreciated !