The ultimate idiot's handbook for build obfuscation

Posted by {"name"=>"Palash Ray", "email"=>"paawak@gmail.com", "url"=>"https://www.linkedin.com/in/palash-ray/"} on July 27, 2014 · 6 mins read

Prologue

This piece is dedicated to those demented souls, who bungle around our industry. Their only aim is to to make life complex and difficult for their co-workers. They are the Rube Goldbergs of the software industry. They have taken obfuscation and unnecessary complexity to a kind of art form. I have encountered many such instances over the years. This particular case is particularly interesting, as I spent weeks understanding the process, deciphering the actual requirements, unraveling layers upon layers of redundant and complex logic to finally come up with a workable solution.

Problem

We were using Maven 2 for our builds. I wanted to upgrade that to Maven 3. The damn thing would fail saying:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.7:run (default) on project maven-ant-assembly-example: An Ant BuildException has occured: The following error occurred while executing this line:
[ERROR] ~/maven-ant-assembly-example/build-with-maven-ant-task.xml:29: java.lang.NoSuchMethodError: org.apache.maven.settings.RuntimeInfo.<init>(Lorg/apache/maven/settings/Settings;)V
[ERROR] around Ant part ...<ant antfile="~/maven-ant-assembly-example/build-with-maven-ant-task.xml"/>... @ 7:118 in ~/maven-ant-assembly-example/target/antrun/build-main.xml

It turns out that apart from the regular Maven pom.xml, we also use an Ant build.xml.

Analysis

I ran with -X, and predictably:

org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.7:run (default) on project maven-ant-assembly-example: An Ant BuildException has occured: The following error occurred while executing this line:
~/maven-ant-assembly-example/build-with-maven-ant-task.xml:29: java.lang.NoSuchMethodError: org.apache.maven.settings.RuntimeInfo.(Lorg/apache/maven/settings/Settings;)V
around Ant part ...... @ 7:118 in ~/maven-ant-assembly-example/target/antrun/build-main.xml
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:216)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:108)
	at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:76)
	at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
	at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:116)
	at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:361)
	at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
	at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
	at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:213)
	at org.apache.maven.cli.MavenCli.main(MavenCli.java:157)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
	at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
	at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoExecutionException: An Ant BuildException has occured: The following error occurred while executing this line:
~/maven-ant-assembly-example/build-with-maven-ant-task.xml:29: java.lang.NoSuchMethodError: org.apache.maven.settings.RuntimeInfo.(Lorg/apache/maven/settings/Settings;)V
around Ant part ...... @ 7:118 in ~/maven-ant-assembly-example/target/antrun/build-main.xml
	at org.apache.maven.plugin.antrun.AntRunMojo.execute(AntRunMojo.java:355)
	at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:133)
	at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
	... 19 more
Caused by: ~/maven-ant-assembly-example/target/antrun/build-main.xml:7: The following error occurred while executing this line:
~/maven-ant-assembly-example/build-with-maven-ant-task.xml:29: java.lang.NoSuchMethodError: org.apache.maven.settings.RuntimeInfo.(Lorg/apache/maven/settings/Settings;)V
	at org.apache.tools.ant.ProjectHelper.addLocationToBuildException(ProjectHelper.java:551)
	at org.apache.tools.ant.taskdefs.Ant.execute(Ant.java:444)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.Target.execute(Target.java:390)
	at org.apache.tools.ant.Target.performTasks(Target.java:411)
	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
	at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
	at org.apache.maven.plugin.antrun.AntRunMojo.execute(AntRunMojo.java:327)
	... 21 more
Caused by: ~/maven-ant-assembly-example/build-with-maven-ant-task.xml:29: java.lang.NoSuchMethodError: org.apache.maven.settings.RuntimeInfo.(Lorg/apache/maven/settings/Settings;)V
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:116)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.Target.execute(Target.java:390)
	at org.apache.tools.ant.Target.performTasks(Target.java:411)
	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
	at org.apache.tools.ant.helper.SingleCheckExecutor.executeTargets(SingleCheckExecutor.java:38)
	at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
	at org.apache.tools.ant.taskdefs.Ant.execute(Ant.java:442)
	... 33 more
Caused by: java.lang.NoSuchMethodError: org.apache.maven.settings.RuntimeInfo.(Lorg/apache/maven/settings/Settings;)V
	at org.apache.maven.artifact.ant.AbstractArtifactTask.loadSettings(AbstractArtifactTask.java:328)
	at org.apache.maven.artifact.ant.AbstractArtifactTask.initSettings(AbstractArtifactTask.java:278)
	at org.apache.maven.artifact.ant.AbstractArtifactTask.execute(AbstractArtifactTask.java:750)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	... 40 more

I upgraded the maven ant task version, maven ant run plugin, and all that. Still no luck. I googled a bit and gathered that Maven 3.x onwards, there has been a change in how Maven interacts with its artifacts. It is now done through Aether. So, thats the reason why the maven-ant-task is not working in Maven 3.

Working with Aether-Ant

I some how managed to modify my ant script to use the AetherAntTask instead of the MavenAntTask. Everything was hunky dory, the build went fine, generated the zip with all dependencies and life was good. Till I decided to actually deployed the build. And thank my lucky stars that I did it. Though the build went fine, the application wont start. Apparently Aether messed up with some Spring dependencies which we working fine with the earlier MavenAntTask. It turns out Aether is still bleeding edge.

Solution

I had actually given up. Then after a couple of weeks, I was thinking about this problem again, and I spotted something very odd. The overall interaction looked something like this:
[caption id="attachment_608" align="aligncenter" width="649"]Existing build flow Existing build flow[/caption]
I am outlining the steps below:

  1. The build is kicked off by Maven
  2. Maven does the compilation, and also builds the jar out of the project. After that, it invokes Ant's build.xml through the MavenAntPlugin
  3. Ant needs to access the Maven dependencies.  It does this trough the MavenAntTask or the AetherAntTask. Thereafter, it zips all these dependencies into a single archive.
  4. Maven picks up this zipped archive from Ant and all is good

What is odd here, is the fact that Maven already knows its own dependencies. So, why does it need to outsource that to Ant? That, I thought was the real obfuscation. I modified the flow as below:

  1. The build is kicked off by Maven
  2. Maven does the compilation, and also builds the jar out of the project.
  3. Before calling the Ant's build.xml, Maven copies all its dependencies to a predefined directory using the Application Assembler plugin
  4. After that, it invokes Ant's build.xml through the MavenAntPlugin
  5. Ant does not need to know Maven dependencies, as its already copied in a predefined directory. Ant zips all these dependencies from the predefined directory into a single archive.
  6. Maven picks up this zipped archive from Ant and all is good

I modified my pom.xml and build.xml according to the new strategy, and all worked fine! I was finally able to migrate to Maven 3.