Maven – reusing other plugins is .. Hard
I suppose each new technology has it’s own serious flow, right after it is invented. The problem is not with the flaw (we all make mistakes and nobody can predict how things will be used) but with the fact that it’s not always easy to fix it later. Some decisions once made – may stay for a long time and that’s the problem.
Ant, for example, made an assumption there should be no logic and no state in builds: no if’s, no loops, no try-catch, no mutable variables. “Big mistake!” – remember ? 🙂 Of course, everybody had if’s and loops and I guess lot’s of people went crazy seeing “immutable properties” in action. Fortunately, there was Ant-Contrib to rescue everybody and allow to set variables, for God’s sake.
Now Maven – it has it’s own issues like not thread-safe local repository and no built-in support for parallel builds or downloads but those may be fixed seamlessly for all users, we won’t even notice. I guess it’ll be announced someday that local repository is finally thread safe and parallel builds and downloads are supported out-of-the-box.
But it has another flaw that bothers me much more – plugins re-usability. It just doesn’t exist, same way as no loop existed for Ant. I happen to write some Maven plugins those days and there’s no built-in way one Maven plugin can use another! May be it’s just the way it should be – after all, I suppose no Firefox extension can use another as well. But you see, Maven plugins are really simple objects with getters, setters and some Javadoc annotations (real Java 5 annotations are only available with AnnoMojo but Maven guys have also started to think in the same direction).
Maven plugin (Mojo) is a simple Java object – still, you can’t easily re-use it!
- Why would you want to re-use it?
Because there are so many plugins dealing with lot’s of Maven details you really don’t want to know about.
Like copying an artifact to some folder, given it’s groupId, artifactId, and version (this artifact may be available locally [where?], remotely [where?], it needs to be downloaded, etc, etc). Yes, there’s a “maven-dependency-plugin” but I had to write a Mojo that is doing basically the same, still in a different way. CopyMojo is already doing what I need so how hard can it be to re-use this logic? And here we come to the second question ..
- Why do you think you can’t easily re-use another plugin ?
Because that’s Maven’s flaw I mentioned in the beginning – Maven has an assumption that only container will ever initialize a plugin. You can’t just new CopyMojo() – you’ll get a new object but no fields will be injected by the container. And if there are no setters for them – you can’t initialize them either! How about MyMojo extends CopyMojo ? (ignoring the fact that I had a convenient ParentMojo for all plugins that was abandoned in this case). Another catch here – Maven doesn’t inject super-class fields (AnnoMojo does that, but CopyMojo isn’t using it). All my Mojo annotated fields are properly initialized but all super-class fields are not, they’re null. I had to declare annotated duplicates of all super-class fields (carefully giving them another name to avoid confusion) and initialize them by myself:
this.project = getMavenProject(); this.factory = getArtifactFactory(); this.resolver = getArtifactResolver(); this.local = getArtifactRepository(); this.remoteRepos = new ArrayList<ArtifactRepository>( getRemoteArtifactRepositories());
All getters are mine, they retrieve values of duplicate fields and initialize those from a super-class.
Btw, even if sub-classing had worked without duplicate fields – it is still an ugly way to re-use a plugin!
- Why do you think it is a serious flaw ?
Because today Maven provides no support for plugins repository or plugins factory or whatever you call it. Something that you could ask for an instance of plugin (providing it’s groupId and artifactId) that is fully initialized (= all fields injected) and ready to be configured.
- Are there any solutions available ?
Seems exactly what I need so I’ll try it soon.
But I do hope there will be plugins factory one day!
Update: thinking about it again – the container itself knows how to load a plugin given it’s groupId and artifactId. So all I need is to find out how it’s implemented and re-use this API, if possible.