What I envy from Maven is the archetype and the dependency management. I really hate to collect all the dependent jars into the new project's lib directory. Fortunately there are some other ways to get along and that is the point of this post.
What is the adventage of using some kind of dependency management?
- no need to store jars in VCS - lib directory becomes a first class citizen in .gitignore
- no need to keep in mind transitive dependencies
- in-house libraries can be stored in the same shared repository as the other libraries
- easy to check upgrade possibilities
- test project that gets libs through declaration
- shared repository for enterprise
- publish in-house libs to shared repository
Setting up Ivy is easy: the ivy-[version].jar should be placed in the lib dir of ant. The dependency declaration goes to the ivy.xml file in the root directory. It could be something like this:
(A nice repository of dependency descriptions is available on the net.)<ivy-module version="2.0"> <info organisation="hu.progmatx" module="test-ivy"/> <dependencies> <dependency org="org.apache.velocity" name="velocity" rev="1.5"/> </dependencies> </ivy-module>
So we have the definition how to get the files? Let us put together a very simple build.xml:
The main points are<project xmlns:ivy="antlib:org.apache.ivy.ant" name="test-ivy" default="resolve"> <target name="resolve" description="--> retrieve dependencies with ivy"> <ivy:retrieve sync="true" symlink="true" refresh="true"/> </target> <target name="report" description="--> report dependencies with ivy" depends="resolve"> <ivy:report /> </target> </project>
- the namespace declaration that makes easy to call Ivy targets,
- resolve target which calls the retrieve task
- report task can display the dependencies in html or graphml
Repository
Our next step is to establish a shared place of libraries. The structure of the repository can be customized and the way Maven does it can also be suitable. Take a look at the following structure:
This can be accessed through the configuration of the following properties in the build.xml:shared-repository/ └── no-namespace └── ant └── ant ├── ivys │ ├── ivy-1.6.xml │ ├── ivy-1.6.xml.md5 │ └── ivy-1.6.xml.sha1 └── jars ├── ant-1.6.jar ├── ant-1.6.jar.md5 └── ant-1.6.jar.sha1
Of course with shared repository I have to say Good bye! to symlinks.... <property name="ivy.shared.default.root" value="/media/shared-repository"/> <property name="ivy.shared.default.ivy.pattern" value="no-namespace/[organisation]/[module]/ivys/ivy-[revision].xml"/> <property name="ivy.shared.default.artifact.pattern" value="no-namespace/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]"/> ...
Publish libraries
To maintain a repository like that by hand could be hard. Fortunately we have ivy:publish and ivy:install tasks to the rescue. One can be used to load up our own homebrew jars and the other is suitable to get a copy of the dependencies from the default Maven repo. Let us do it one by one!
To publish your jar just put the following in the build.xml:
OK, it was too easy, so let's look at the other problem! That seems to be more complex, because we have to edit two files. Append this to build:<target name="publish" depends="resolve" description="--> publish module to shared repository"> <ivy:publish resolver="shared" pubrevision="1.0"> <artifacts pattern="build/jars/[artifact].[ext]" /> </ivy:publish> </target>
And let's create the ivysettings.xml!<property name="ivy.cache.dir" value="${basedir}/cache"/> <property name="dest.repo.dir" value="/media/shared-repository"/> <target name="maven2" description="--> install module from maven 2 repository"> <ivy:settings id="copy.settings" file="${basedir}/ivysettings.xml"/> <ivy:install settingsRef="copy.settings" organisation="org.apache.velocity" module="velocity" revision="1.5" from="libraries" to="my-repository" overwrite="true" transitive="true"/> </target>
Here the point is that we create a so called resolver, that is connected to the standard Maven repository and to our shared repository. In the install task the dependent lib is named and with the attributes set up this way we gather the transitive dependecies as well.<ivysettings> <settings defaultCache="${ivy.cache.dir}/no-namespace" defaultResolver="libraries" defaultConflictManager="all" /> <!-- in order to get all revisions without any eviction --> <resolvers> <ibiblio name="libraries" m2compatible="true" /> <filesystem name="my-repository"> <ivy pattern="${dest.repo.dir}/no-namespace/[organisation]/[module]/ivys/ivy-[revision].xml"/> <artifact pattern="${dest.repo.dir}/no-namespace/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]"/> </filesystem> </resolvers> </ivysettings>
Conclusion
Wasn't it easy? I did not dare to think of it before - it seemed so many things to do and all these things seemed to be so complex. Fortunately Ivy is gentle, it just adopts to your pace and you do not have to customize more than what necessary is. I was only scratching its coat, but it was completly working already. It was worth to invest some time and gather some Experience by Doing!
hello,
ReplyDeleteok, the maven is more complex and hard to understood, but I think there are many option which are not exists in this solution. And if you have methodology for using Maven at your company or in your project, most of the developers do not have to take care of maven.
Best Regards
Gabor Blasko