Saturday, November 14. 2009
Spring Actionscript is a great library - no question. It makes it much easier to mock Cairngorm service delegates and rewire them for testing with FlexUnit. Testing the full event/command/delegate workflow is pretty easy.
However today I had quite some "fun" with the Maven artifact and it's transitive dependencies in combination with Maven 2.2.1. Spring Actionscript has some dependencies to these artifacts:
We have a rather complicated setup. There are tons of individual Flex artifacts and libraries along with several Java modules for the backend. We also have certain legal requirements to the build like being able to reproduce a released build at any time in case a customer needs a patch. This requires a tight dependency management, which can only be accomplished with an in-house repository server that is able to cache external libraries in case external repository servers break and become unavailable or in case artifacts are removed. All builds are absolutely restricted to the in-house repository server. No build may access external repository servers directly.
Until I noticed that Flex builds suddenly start to contact the yoolabs.org repository server. I searched all POM files, but did not find any reference to this server. My local Maven settings also did not contain anything. So how was this repository server introduced to the local build?
Update 2009-11-14: The original article claimed that a distributionManagement section tag was the fault. This is not true. After further examination I discovered that the spring-actionscript-superpom declares remote repositories, which are actually used:
<repositories> <repository> <id>flex-mojos-repository</id> <url>http://repository.sonatype.org/content/groups/public</url> <releases> <enabled>true</enabled> </releases> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>yoolab.org-releases</id> <url>http://projects.yoolab.org/maven/content/repositories/releases</url> <releases> <enabled>true</enabled> </releases> </repository> <repository> <id>yoolab.org-snapshots</id> <url>http://projects.yoolab.org/maven/content/repositories/snapshots</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories>
I have tested this with various Maven versions and obviously repositories in dependent POMs are activated since Maven 2.1.0. With 2.0.9 I cannot reproduce this behavior, but any version above 2.1.0 downloads artifacts from such repositories defined in dependencies.
For completeness, here is the old and wrong text:
After spending some hours searching I finally examined the POMs of the artifacts hosted on the yoolabs.org repository servers and found something in the as3commons-project POM:<distributionManagement> <repository> <id>yoolab.org-releases</id> <name>Yoolab.org releases repository</name> <url>dav:https://www.yoolab.org/maven/content/repositories/releases</url> </repository> <snapshotRepository> <id>yoolab.org-snapshots</id> <name>Yoolab.org snapshots repository</name> <url>dav:https://www.yoolab.org/maven/content/repositories/snapshots</url> </snapshotRepository> <site> <id>yoolab.org-as3commons</id> <url>scp://www.as3commons.org/srv/www/www.as3commons.org/public_html</url> </site> </distributionManagement>
The distributionManagement section seems to be something new in Maven 2.2.1. At least I am unable to reproduce this behavior with Maven 2.0.9 and that's why I haven't noticed it until I had upgraded my notebook to Ubuntu 9.10, which comes with Maven 2.2.1. It allows an artifact to define remote repositories, where transitive dependencies can be downloaded.
Well, I must say I have an ambivalent opinion about defining other repositories in POMs on remote repositories. It may ease initial setup, but it is very dangerous if you want to enforce a tight artifact management. I don't know yet, how to disable such indirect repositories. The only solution I know so far is running Maven builds with no direct Internet access and making sure that no proxy has been configured for Maven.
This was the first problem. The second problem also took some time to figure out.
Since some projects started to use features from the Flash Player 10 that are not available in the Flash Player 9 API, we had to upgrade the playerglobal artifact dependency. The major version of the Flash Player is configured through a classifier, which is not a good idea and you'll see soon why.
So we had changed all occurrences of the artifact in the POM files, but still the build was being compiled against the old Flash Player 9 libraries. To make it more interesting this was the case only on some machines. E.g. on a colleagues Windows Vista machine the build failed, but not on my Linux Box. Running mvn dependency:tree finally revealed that the artifacts spring-actionscript-core, spring-actionscript-cairngorm and as3commons-logging had dependencies to this playerglobal artifact with classifier 9.
Artifacts with different classifiers are different artifacts by Maven convention. So in case you do no longer have an explicit dependency to playerglobal, classifer 9, you are not longer able to overwrite the version. Note that the classifier is NOT the version. So after upgrading our POMs to playerglobal, classifier 10, we had two playerglobal dependencies. And the actual dependency that was used by the compiler was obviously chosen randomly. So on one developer machine classifier 10 was in place, on another classifier 9.
So in case you use Spring Actionscript in your builds, you may have a working build, until you change something that affects the dependency order in your builds and suddenly the builds will fail with no obvious reason. To prevent this problem, you will have to exclude the playerglobal dependency from the transitive dependencies of the affected artifacts when you add the dependencies mentioned above to your POM:
<dependency> <groupId>org.as3commons</groupId> <artifactId>as3commons-logging</artifactId> <type>swc</type> <!-- Supress transitive playerglobal 9 dependency from as3commons-logging --> <exclusions> <exclusion> <groupId>com.adobe.flex.framework</groupId> <artifactId>playerglobal</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springextensions.actionscript</groupId> <artifactId>spring-actionscript-core</artifactId> <type>swc</type> <!-- Supress transitive playerglobal 9 dependency from spring-actionscript-core --> <exclusions> <exclusion> <groupId>com.adobe.flex.framework</groupId> <artifactId>playerglobal</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springextensions.actionscript</groupId> <artifactId>spring-actionscript-cairngorm</artifactId> <type>swc</type> <!-- Supress transitive playerglobal 9 dependency from spring-actionscript-cairngorm --> <exclusions> <exclusion> <groupId>com.adobe.flex.framework</groupId> <artifactId>playerglobal</artifactId> </exclusion> </exclusions> </dependency>
Trackback specific URI for this entry