Good POM

lafkblogs.wordpress.com 5 lat temu
Zdjęcie: wypis katalogu z lokalnym repo Mavena


WordPress is horrible and I suck at it. This is my fourth (4th, sic!) attempt at writing this post. I tried copying from PL version, saving, restoring revisions… Wasted hours. I’m officially looking for a good site where I could easily update the blog posts as ADOC files, from Vim, with Git and no pretentious and wannabe-fancy while restrictive and stifling UI. Loosing this content and the entire effort makes me feel gutted out. Seriously unpleasant experience.

POM for Project Object Model, XML file serving as a heart of any Maven project. This entry details what and why should be in such a POM and was created as a good reference point to sent people to when they ask me questions about it.

Titular POM is available as a Gist for your perusal and convenience, at your will, at:

https://gist.githubusercontent.com/LIttleAncientForestKami/c9b185c123fc97f6022861f645766aa5/raw/45db276f570fcca357fbcf36b6209517c69c6427/pom.xml

All tags are written with CAPS LOCK: MODELVERSION, PROJECT, ARTIFACTID.

Header

  1. XML file declaration with version information (have you seen version other than 1 here? I haven’t) and an information that the file is encoded with UTF-8 encoding. The only non-POM part.
  2. XML root tag: PROJECT. Specifies (through its attributes) the namespaces and grammar that whole file will follow. Helpful to IDEs and other automated tools that use the links here to check what is allowed where, when they offer you suggestions to modify your POMs. This is how you will not see a BUILD tag with cursor placed inside REPORTING or a DEPENDENCY outside the only two appropriate (once grammar’s concerned) sections for it. Here you also see links for namespaces – a malformed POM may use a link for a different model than the version you have in the next tag.
  3. MODEL VERSION tag (written in CamelCase and without space between words) is always set to 4.0.0 for Maven 2 and 3. AFAIK, there’s no other model than version 4.0.0 but Maven folks were at some point thinking to have more POMs than the one we talk about.
  4. coordinates

Maven coordinates are not bad interview topic, revealing if one read and/or delved deeper into topic or had just used Maven.

Coordinates

Maven alone is far from superb (some would already say it’s not alt all superb but that’s a topic for later – the tool has its merits), but Maven and Maven repositories are another story. Repositories allow Mavenowi to hold countless different projects, each with their own POM and are backbones for dependency management. For the tool to know what it’s supposed to download, it needs to identify projects across time and space. Hence – coordinates.

  1. Time is version (I know, I, too, had wanted to write money). Interestingly, many would here put a ticking time-bomb in their own project but that’s for another post. Tag: VERSION.
  2. Space is group + artifact… + packaging + qualifier. Qualifier almost always goes unheard of, it’s that rare. Packaging is forgettable for it’s set to JAR by default and can be hence omitted.
  3. Group has RDNS notation (reversed DNS), since Java packages are like that. Let me state outright, if you with to publish anything to Maven Central (main Maven repo) then you want the URL your group points to under your control. In my case that’s lafk.pl and on an image in Minimal POM section it would be app.mycompany.com (leading nowhere, so I’m not linking). Tag: GROUPID.
  4. Artifact is usually the last coordinate to fill-out, it specifies – within a group – usually finally (across space!) what exactly are we gonna download. Tag: ARTIFACTID.
  5. PackagingJAR, WAR, RAR, EAR, POM. I’ll explain in another post, default setting is JAR and if we omit PACKAGING tag we inherit that setting from a super-POM (coded per Maven version, so susceptible to change with Maven version upgrade or downgrade – though if we downgrande my experience tells me that should be safe, at least for now).

Coordinates affect two things:

  1. where we locally keep the package with a given dependency (or our own artifact / project)
  2. where we remotely look for it (second half of this information is which repositories we use but here we just assume only Maven Central)

Look at directory listing screen-shot from my .m2 directory on my machine:

Local Maven repository from my machine, demonstrates coordinates

First straight line goes from repository main directory: .m2. Second comes from a group: pl. Next group subdirectories (like with Java packages) come from dot-separated names part, until we reach the last one, which of course is an artifact (no, I haven’t tried artifact.name.with.dots but do try yourselves! :D).

Inside the artifact directory we have as many subdirectories as many versions we mvn installed or downloaded through dependencies in our Maven-enabled projects we ever had on our machine.

Packaging determines which file we’re gonna download. Just the POM? Only a JAR? Etc.

In the end, installed or downloaded filename is: artifactId-versionNr.packaging .

Do note how a typo in group name (or its part) may well have unpleasant consequences: on the image we have two artefacts, ticketMachine and OX from TWO DIFFERENT groups, since ksundaysky was written with a typo and withoutand this resulted in two different groups where one should be. Later, when one will try looking for it in the dependencies… one may not find it. 🙂

Minimal POM

Header and coordinates together give a so-called minimal POM – meaning a POM that will pass validation and will be recognized as a POM (by Maven itself). You can – for minimalization – even cut out links from PROJECT, if you so want.

Minimal POM has an XML root, the PROJECT tag and coordinates (with default packaging unseen but set to JAR) and model version 4.0.0

First-level tags and their order

POMs grammar (linked in PROJECT tag attributes) determines which tags may appear on a so-called first level. That is as direct children of a root tag (PROJECT). All so-far discussed tags and few next ones – actually everything aside from PROJECT tag which I also call a root tag, are just such first-level tags.

This means I could’ve put them in a different order than I did. They can be put right before a closing tag for PROJECT. Usualy though they appear at the beginning. It’s a convention for humans. Similarly BUILD tag (more about it soon) is put after DEPENDENCIES tag and before REPORTING tag. Order of all tags I’ve taken from Maven documentation.

For fellow humans

In my POM I use NAME, DESCRIPTION and URL tags (the last one not that well). They aren’t needed for Maven or other tools, rather they’re three basic tags out of many that Maven team anticipated to describe any project.

Yes, Maven team would like you to think about Maven not as just a tool to manage dependencies. For them site lifecycle is important and they use it to create their Apache projects pages, building those projects, coordinating work on them etc. But ad rem, let’s get back to good POM. For people to realize what they are looking at and so Maven Central and other automatically-created pages would look nice we usually add:

  1. NAME of the project which boils down to ARTEFACTID for humans, so with some disallowed characters there allowed here (like spaces),
  2. DESCRIPTION of the project in one sentence.
  3. URL for the page where more project-information may be found. Which in my case points always to my page, where usually I say nothing about a given project. Hence my earlier remark about not using it that well. If you publish to Maven Central, one of the checks is “do you have the right to that domain”. I know many projects which put here their GitLab URL, or GitHub or… Which ain’t without reason but this is not a page under THEIR contol, since it’s in GitLab or GitHub etc. domain.

Since this is for fellow humans, machines won’t get angry if you skip it. If you tried to skip something for machines there’ll be a mal-formed POM, but those three tags can be omitted without care or consequences – other than “I don’t know what this project is about” and somewhat less clear project site that Maven could generate.

Properties

tag PROPERTIES. Very interesting section, where we can define our POM properties… aka its variables. To not digress too far into topics irrelevant for describing a good POM, I’ll just state the following:

  1. At a minimum I put here setting that makes my build platform independent and I configure Maven compiler plug-in with Java version it’s supposed to used to compile.
  2. Usually I put here all version numbers, to have them in one central place.
  3. Having delved deeper into character encoding in Maven one time I’ve put here all other character-encoding (UTF8) settings in Maven.

Three important things:

  1. whatever you set here you can use as variables in GNU shell notation – ${name} – as I do with version numbers
  2. while Maven understands setting source file encoding via project.build.sourceEncoding and Maven compiler understands setting maven.compiler.source or target you can’t set the plug-in version in the same manner (using dot notation for XML tags), hence later I declare the compiler plug-in anyway to declare its version.
  3. properties are inheritable and inherited – your project inherits properties from its parent and passes them (both those inherited and its own) on.

Properties section order in the POM file depends on team habits. I usually see it at the beginning of POM file but there are folks who like putting it at the end. AFAIK, for parsing POM variables this makes no difference.

Minimal properties set


UTF-8
1.8
1.8

Dependencies

tag DEPENDENCIES, section full of dependencies. Each DEPENDENCY means one pulled Maven project – or more! More, if said project has its own dependencies. More about this in a separate blog post since… I have this section empty.

Purposefully! So anyone could insert their own tool combo to download. If I were to put anything, in the first place I’d put in TestNG – and it will be there someday, but for now IDE can add it easily enough so…

Every dependency is a set of coordinates – our project can be pulled somewhere as a dependency so it’s only fair this works both ways – we can pull somebody’s project to our local repo through its coordinates.

Only allowed – by POM model version 4 – tag in DEPENDENCIES section is a DEPENDENCY tag. You have no other option if you want to live by the POM grammar.

Dependency management?

Any WHATEVER MANAGEMENT (written without spaces and in CamelCase) should be put aside, unless you are crafting a POM to be inherited. So DEPENDENCYMANAGEMENT out, DEPENDENCIES stay. Similarly PLUGINMANAGEMENT (out) and PLUGINS (stay).

Build

tag BUILD. We have more options here, but I still use just one: PLUGINS. Inside that section I can only have PLUGIN. Same rules as with dependencies, since Maven is plug-in-based. Each plug-in is described and configured by one PLUGIN, and all plugins must be within PLUGINS. Plug-ins declared here are:

  1. enforcer – ensures having proper versions of Maven (and Java, though I resigned from this and I don’t even recall why)
  2. jar – easy app-packing to a jar and running it just like that, without guessing or looking for a main class or wadling through manifest file (one can play with configuration better than I did, more on that at later date)
  3. compiler – setting newer than default Java to compile with. Maven 3 has default Java set to JDK 5, and in one of the recent versions – to 6. Yes They raised the default to JDK 6, whereas 8 is EOL
  4. resources – set to version compatible with jar plug-in version so your resources will be nicely packed to a jar

If you don’t have anything in resource directories then last plug-in won’t be useful. If your project can be built with relatively modern Maven 3 and even if they’ll try to build it with Maven 2 nothing wrong will happen, first plug-in goes away also. IMO two other plug-ins are a must-have combo. JAR could be replaced if you build jars in another way (with another plug-in) but shouldn’t be CUT without a replacement. Compiler… well, only if you don’t like to write code with features from JDK 7-13. Yes 13th JDK is generally available now.

Why declaring a plug-in only to declare its version?

A good question. Well, otherwise you take your plug-in version from a super-POM. So, the one hardcoded and Maven-version-dependent. But what mainly hurts here is how that version is older than the current one. Which – if you have automates checking your version numbers for both your deps and your plug-ins – causes multiple alerts about outdated versions.

Entire section is about build lifecycle. For reporting plug-ins… stay tuned, for in my POM there’s none.

Summarizing

I’ve described POM header, tags that are usually not seen or used and those more interesting ones like project coordinates or POM properties, which you can use to not have a warning about build being platform-dependent (setting source file encoding) or determine JDK version for compilation. There’s an answer to a question about differences between x and xManagement. There’s minimal POM and build plug-in combom to make build output an executable JAR.

That’s all folks, I’m hoping this will be useful and that something here was interesting or made you learn. I’ll gladly see comments to know what was good and what can be improved.

Whole thing once more, as a Gist. I’ll try remembering once I’ll update the version, to update the entry. I’ll try remembering… 😉

Idź do oryginalnego materiału