Maven2 to Gradle Convertor
The code has been refactored from script to class, and it is now hosted on github.
Thanks to @lilithapp I have discovered a limitation – your project will be considered multi-module only if your reactor is also a parent of at least one of your modules. I probably won’t fix it, since that’s the case in most projects and since gradle-m2metadata-plugin doesn’t have that limitation.
Last JavaEdge I delivered a session about Java build tools landscape. My impression from this overview is solid – Gradle rocks. It is a best of breed and takes the best from Ant and Maven2, leaving the downsides of both behind. Take at look, it is worth it (Prezi rocks too, but it’s another blog post).
The only fly in the ointment I found is lack of good maven2 to Gradle convention. Gradle has good maven support. First of all, it can use dependenices’ POMs to determine their transitive dependencies. Second, it has Maven plugin, but it works in the opposite direction – it can generate POM for your project, built with Gradle. I need the other side – something similar Gradle has for Ant – ant.importBuild() imports an Ant build into the Gradle project, and each Ant target is treated as a Gradle task. This is cool! Franky, I need much less with Maven.
Here’s the shopping list: I need to generate the following settings from POM.xml
- Dependencies (inc. scopes and exclusions)
- Correct plugins selection (e.g. war for web application module)
- GroupId
- Version
- Repositories
- Compiler level settings
- All those with full multi-module support
- All those with reuse support from inheritance and settings.xml
After a short search I discovered JIRA issue GRADLE-154, in which Antony Stubbs asks for a subset of such functionality, and finally attaches a small Groovy script that parses given POM.xml and dumps to the console dependencies in Gradle format. That was a great start for me, but the drawbacks were obvious – no support for multi-module projects (I can’t recall when I saw single-module project last time), no support for parts, coming from settings.xml, etc. One specific pom.xml file in view has very little to do with the effective pom in runtime. You already got it, right? The parsing should be done on the effective pom, which is easily obtained using maven-help-plugin. So, having effective pom in hand, I can rip it apart and build nice set of build.gradle files, and the settings.gradle for the multi module support, and they include all the items from the above list!
I can assure you there are some bugs here and there in this script, but generally it works, and I managed to migrate fairly complicated project with war assembly, transitive dependencies, poms inheritance, artifacts exclusions etc. in a single click. “Is this cool or is this cool?”
So, grab the script, and give it a shot. It has two flags: -verbose prints out Maven output during effective pom resolution and -keepFile keeps the effective pom file for you.
Note the new task in the generated gradle.build – replacePoms. The idea is to solve the lack of IntelliJ Gradle integration when it comes to dependency management (IDEA knows how to run the build). Gradle generates poms for your modules. The gradle.build knows to copy them to the place where IntelliJ needs them. Just run “build replacePoms”, and IDEA will recognize dependencies from Gradle! Yup!
Enjoy.
P.S. You should check the new gradle-m2metadata-plugin, it’s the real thing – Maven3 embedded into Gradle’s plugin. It gets all the metadata in runtime!
P.P.S. Sorry for my Groovy, it’s not my mother tongue.
Artifactory as Training Labs Provisioning Platform
As AlphaCSP‘s training guy I deliver a lot of trainings. They come in different flavors, on different topics, and in different companies. The common in all of them is the problem I encounter on labs setups.
First, problem definition:
Let’s take, for example, some serious global financial company, which needs training in Spring, Hibernate and JAX-RS. My contact point is a nice Training Activities Administrator. She just organized “Micro-Expressions Training for HR” and her next task is to organize my Java course. What do you say, will she be able to install IntelliJ IDEA (or Eclipse?), Spring 3 bundles, Hibernate dependencies and Jersey? And yes, the classroom network is detached from both Intranet and Internet (BIG financial company, remember?). Oh, I almost forgot a bonus – the training workstations rollback all changes after every restart.
Now, here are two possible provisioning solutions:
- Come over day before for installs. Well, probably the classroom is occupied with another course. If not, the guy that should let me in the classroom, the network, and the computers is busy, sick or in Thailand, but probably all three in the same time. Ah, and I have another work to do on this day! And the customer won’t pay for it anyway. You got the point – bad idea.
- Prepare the labs on CDs. Well, it generally works, most of the software courses delivered that way, one successful example is SpringSource trainings. You get nicely branded CD full of all you need – The IDE, the dependencies, and the labs source code. Good stuff, really. It works for SpringSource because of the high volume of catalog courses they deliver. They have a stock of identical CDs they use during every single training, worldwide. When it comes to tailor-made courses, things are different. No course is similar to any other course, the topics, installs, dependencies and exercises are unique set each time. That rather complicates the CDs craft – composing, burning, labeling. I don’t say it’s impossible – I did it for each and every course, but it’s a real PITA. And thanks to the reverting workstations, students will have to copy, extract, setup, define variables every day from scratch over and over again. Did I mention PITA?
And there is a third solution. The best one. You can use Enterprise Repository Manager to recreate students environment in a couple of minutes in any given time. Now, it rocks. It really is. Watch the steps:
- Customer Requirements
- Two simple installs, every Training Administrator and/or Sysadmin can manage:
- Create .m2 directory under user home for Maven user settings.
- Permission to connect your notebook to the class’ Intranet. It is isolated, the machines revert themselves, shouldn’t be a problem.
- Exercises development
- Develop the exercises on your notebook with all the Maven goodies – pom.xml, dependencies, superb IntelliJ-Maven integration (or Eclipse?).
- Install Artifactory locally (you’ll see why Artifactory and not Nexus in the following steps). I mean – download and unzip, heh. Run it (not even as a service)
- Import your local repository to Artifactory (can’t do it in Nexus #1) – zip it and make half-dozen of clicks in Artifactory UI.
- Deploy the exercises to the local repository. They probably won’t compile – they are exercises, right? Then just zip them and deploy from UI. Students will download them through Artifactory UI.
- Take the Artifactory down. You are ready to go to class.
- Exercises delivery
- “Good morning, students!” – deliver the hell of the course, get to the hands-on part.
- Connect your notebook to the class’ Intranet, get dynamic IP (yap, dynamic is good enough).
- Get Artifactory up and running.
- Let the students browse to Artifactory’s homepage. There they will found Maven Settings Generator (can’t do it in Nexus #2), which will generate settings.xml to work with your instance of Artifactory from their machines. All they need to do is check “Mirror-Any”, select “repo” and save the generated file under .m2 directory. That’s all, their machines are fully configured to get all the dependencies needed for Spring, Hibernate, Jersey, and whatever you need for your training.
- Let the students browse the repository to download the exercises zip, unzip it, export Maven project into IntelliJ IDEA (or Eclipse?) and just start working!
As you saw, using Artifactory as labs delivery platform dramatically simplifies both lecturer’s and student’s life, enabling rapid exercises development and rollout without any preparation from student’s part and minimal preparation from training organizer’s part, all those thanks to Maven2 dependency management capabilities, good IDE Maven integration, and, of course, Artifactory’s ease of use. And frankly, there is nothing I love more than ease of use. Maybe only chocolate ice-cream.
Coding to a Bug
Well, the story goes like this.
Once you have a closed source application to extend (well, you decomiple it of course, but it doesn’t make it open source, do it?). And it is damn old (like 2001 or something, did people actually code then?). And it uses some weird database like MS SQL Server, and, of course a JDBC driver for it, jTDS. Back then, in 2001 version 1.0 of the jTDS was available. It was a little very buggy, for example, putting the driver’s jar in WEB-INF/libs generates NPE, so it have to live in Tomcat’s share lib.
And of course, as any respectable company the vendor of the application developed it’s own wrapper arround JDBC with DatabaseUtilities.getConnection, releaseConnection, and so on.
Now, to the story.
On the surface it looked easy. We obtain connection, bla-bla, executeQuery, ResultSet.hasNext, etc. Works like a charm on clearly installed application. Once they populate the database with their home-made utility for migration from excel… boom. Thier code works, our throws IOEXception – “system cannot find the path specified”. The root of the exception somewhere inside the driver. Google suggests to update the driver to the last version. No prob! Done. Of course I move it from Tomcat’s share to the application lib on that occasion.
Guess what? Our code works like charm, but all other updates in application start failing on it can’t manually commit when auto-commit is on! As I understood it, they just didn’t turn auto-commit off, and the old buggy driver forgave them!
What can you do? The old driver stops working on durty database, the new driver won’t work with their application code. You can’t change the source of their driver wrapper to turn the auto-commit off, because it is closed-source, and you can’t be sure decompiler did a perfect work (and usually it don’t).
Anyway, we rolled-back the driver version, returned it to Tomcat’s shared lib, and went to their JDBC wrapper to look which shaman ceremonies they perform to make it work. We’d better not. They cast the driver to the actual jTDS class and call all kind of implementation methods. I am thankful to them they wrapped all those inside their JDBC wrapper, so all we needed to do is to replace JDBC code with some really strange calls like getQueryResults, which gets the SQL string (not parametrized, of course), and returns array of strings (contatining all the data, including dates and numbers).
So, what do you say? Any better way to do it except coding to the bug?