Maven Multi Module Project - maven

I havent used Maven extensively
Presently have 5 different maven projects , Each has a different pom.xml. As of now there is dependency relationship between them , each one points to other in < dependency > if needed.
Presently the thing we dont like are
When we release a child projectA , then we need to manually modify all projects having projectA as a dependency to use new version. Saw Maven has a version plugin , not sure how that will help.
As a solution I want to have a cleaner organization between poms , and possible avoid above issue.
What i thought is ( may be incorrect )
Where fat arrow represents Parent Child relationship and a thin arrow represents sub-module. But this does not seem to be working, see code and errors below
Child Project 2 pom
<groupId>ChildProject2</groupId>
<artifactId>ChildProject2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>Parent</groupId>
<artifactId>Parent</artifactId>
<version>${parent.version}</version>
<relativePath>../Parent/pom.xml</relativePath>
</parent>
<dependencies> ... </dependencies>
ChildProject2 - Error
Project build error: Non-resolvable parent POM: Failure to transfer Parent:Parent:pom:${parent.version} from http://repo1.maven.org/maven2 was
cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced. Original error:
Could not transfer artifact Parent:Parent:pom:${parent.version} from/to central (http://repo1.maven.org/maven2): Illegal character in path at index 45:
http://repo1.maven.org/maven2/Parent/Parent/${parent.version}/Parent-${parent.version}.pom and 'parent.relativePath' points at wrong local POM
Parent pom
<groupId>Parent</groupId>
<artifactId>Parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<parent.version>0.0.1-SNAPSHOT</parent.version>
</properties>
<modules>
<module>../ChildProject2</module>
<module>../ChildProject1</module>
</modules>
<dependencies> ... </dependencies>
GrandParent2 pom
<groupId>GrandParent2</groupId>
<artifactId>GrandParent2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<grandparent2.version>0.0.1-SNAPSHOT</grandparent2.version>
</properties>
<modules>
<module>../Parent</module>
</modules>
<dependencies>... </dependencies>
ParentMain.java
public class ParentMain {
public static void main(String[] args) {
DocumentFactory df = new DocumentFactory();
ChildProject1Main cp1 = new ChildProject1Main();
ChildProject2Main cp2 = new ChildProject2Main();
}
}
ParentMain - Errors
ChildProject1Main cannot be resolved to a type
ChildProject1Main cannot be resolved to a type
I am currently using Maven Version 2.2.1 (open to upgrading if this can be solved using upgraded maven version)
One of the comments below says can solve this using " CI tools out there such as Jenkins and TeamCity" .. any pointer (examples) how to solve this using Maven ( and or Hudson )??
What am i doing wrong , how to get best design for such project dependencies

The first question I thought of when I saw this diagram was, "Why would the business logic have any kind of dependencies on submodules?" Two thoughts sprung to mind, and I'll go through each of them and what you shouldn't be doing to repeat these.
Tightly coupled code. This manifests itself in code duplication or the large class/method smell.
You want your code to be modular, such that a project only depends on what it needs to in order to compile and run without error.
Illogical code hierarchy. This (eventually) manifests itself as circular dependencies, or dependencies that suddenly go missing when <exclude> blocks show up.
You want the lines of what depends on what to be explicit, such that your code hierarchy is well laid out.
I'm going to take your arrows in one cardinality to mean that a project depends on another, so it makes sense that the UI and CLI depend on the Business Logic to function. It also makes sense that Business Logic can depend on the children modules to do some other function not quite related to the core.
What doesn't make sense is that these children models also depend on Business Logic. The children modules should be unique enough that they do not need to depend on anything from Business Logic; if they do, then perhaps they should live there instead.
As for the versioning - there are CI tools out there such as Jenkins and TeamCity that can aid you with that problem. The thrust would be to have that set up in such a way that it occurs independent of human intervention/error.

How do I tell Maven to use the latest version of a dependency?
Take a look at the above thread. I like the answer by Adam Gent to use the versions plugin to update versions in your poms in jenkins. I agree with him that maven and continuous deployment are a particularly poor match.
So use that and a simple parent pom for any shared configuration (plugins, dependencies, etc) but don't make them multi module projects and version and release the parent pom as an independent thing.

Related

Maven: how to not depend on Parent POM

I have a Maven multi-module project. Something like this:
- ParentProject
- ChildA
- ChildB
- ChildC
The child projects inherit from a Parent POM (ParentProject) solely for the reason of sharing stuff like <build>, <scm> and <properties>, so as to not repeat it in all the child modules. Thus, the objective of the parent-child relationship is not related to dependencies in any way. It plays a role at build-time, not at runtime, so to speak.
The child projects's artifacts are for consumption for a wider audience, hence they'll be published into a centralized repo.
How do I "break" the relationship between from the child up to the parent seen from a perspective of a consumer of a child?
Let's say another project, ProjectX, adds a dependency on ChildA. When doing this the Maven client will attempt to not only download the POM and artifact of ChildA itself but will even try to download the POM for ParentProject. However, there's absolutely no need for that POM seen from a consumer point of view. It doesn't contain information that the consumer needs to know.
How can I break this relationship from consumer's perspective? Forcing the POM for ParentProject to be published into a repo seems pointless as nobody has any need for it there.
Perhaps there's another way that Maven will let me share things like build instructions and properties between projects without mandating that a Parent POM exists in a centralized repo ?
Or perhaps there's some way I can manipulate the POM for the Child projects which gets put into the centralized repo (removing the <parent> element as it is irrelevant).
Perhaps only me but I feel that Maven is conflating two unrelated concepts here (build-time vs consume-time) and forcing unnecessary roundtrips and unnecessary artifacts in repo. I haven't dabbled with Gradle yet but I wonder if it does it any better?
Usually, the Maven POM is both build POM and consumer POM. This is not ideal, and will probably change in future versions of Maven.
At the moment, your best option seems to be the flatten Maven plugin, which allows you to remove "unnecessary" parts of the POM before uploading it.

Maven: "dependency vs module "

I would like to know the difference between the two. From what I understand the dependency needs a version etc but modules can be defined without the version and it takes the parent's version. Is there more to it?
A dependency and a module are different concepts.
From the Maven book about dependencies:
Maven can manage both internal and external dependencies. An external dependency for a Java project might be a library such as Plexus, the Spring Framework, or Log4J. An internal dependency is illustrated by a web application project depending on another project that contains service classes, model objects, or persistence logic.
Put simply, a dependency is saying: "To build my project, I need to use this library".
A dependency in Maven is defined by its coordinates, that are made of 4 elements:
groupId: The groupId groups a set of related artifacts. It generally resemble a Java package name. For example, the groupId org.apache.maven is the base groupId for all artifacts produced by the Apache Maven project.
artifactId: The artifactId is the project main identifier. When you generate an artifact, this artifact is going to be named with the artifactId. The artifactId, groupId combination must be unique. In other words, you can't have two separate projects with the same artifactId and groupId. Furthermore, you can't have the same artifactId for a given groupId.
version: When an artifact is released, it is released with a version number, such as "1.0", "1.1.1", or "1.1.2-alpha-01". You can also use what is known as a snapshot version for a component which is under development, like "1.0-SNAPSHOT".
classifier: A classifier is not needed and is generally not used. It is only relevant when you need to produce several artifacts from a single project: it then serves as a distinction in the file names, that otherwise would be made simply with the artifactId and version and thus conflicts.
In the POM, it looks like this:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
...
<dependencies>
<dependency>
<groupId>org.some.project</groupId>
<artifactId>my-project</artifactId>
<version>4.0</version>
</dependency>
...
</dependencies>
...
</project>
This declares a dependency to the my-project library identified by the given coordinates.
A module is used in the context of multi-module Maven project. From the Maven book:
A multimodule project simply manages a group of other subprojects or modules. [...] When setting up a multimodule project, you are simply telling a project that its build should include the specified modules. Multimodule builds are to be used to group modules together in a single build.
Put simply, having a module is saying "To build my project, I need to build this project".
In the POM, this is done like this:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.codehaus.mojo</groupId>
<artifactId>my-parent</artifactId>
<version>2.0</version>
<packaging>pom</packaging>
<modules>
<module>my-project</module>
<module>another-project</module>
</modules>
</project>
You are saying that this project, identified by org.codehaus.mojo:my-parent:2.0:pom, declared 2 modules which are my-project and another-project. Thus, when you build my-parent, those two modules will also be built.
Conceptually they're different, but they go hand-in-hand as well.
All of them parents, modules and libraries are referenced, by convention, using a unique name composed of a group-id, artifact-id and version. Unless you need otherwise (seldom happened to me) a module can indeed inherit its version from the parent but you do have the option of changing it.
A maven module is like a sub-project of your project. It allows you to separate a big projects into smaller parts which can be managed individually, and in the end aggregated to build whatever you require. We could picture a car as the end product, being composed of several modules such as engine, wheels, chairs etc.
A dependency is a library your project or module requires to compile and/or function. Coming back to our car, it's true that it requires an engine module to function. However, the current version of the engine itself requires a certain type (version) of pistons to run correctly. Too big and they won't fit, too small and it won't work at all.
Applied to a java project, you can imagine having several modules which access a DB, call some web services, provide an UI, all of them linked together by their parent. Also the DB module will probably require a certain DB driver (MYSql, Oracle, etc), matching the version of the DB you're using, and the web service module an HTTP library, and so on.

How to provide artifact upload settings in maven

I am using artifactory for artifacts management .To upload my artifacts to the server i have below lines added to my pom.xml
<distributionManagement>
<repository>
<id>e512209f3d01</id>
<name>e512209f3d01-releases</name>
<url>http://server-cicd-01-ubt:8081/artifactory/ext-release-local</url>
</repository>
<snapshotRepository>
<id>e512209f3d01</id>
<name>e512209f3d01-snapshots</name>
<url>http://server-cicd-01-ubt:8081/artifactory/ext-snapshot-local</url>
</snapshotRepository>
</distributionManagement>
but i do not want these settings to be in pom.xml.Is there any way i can define in global setting.xml. What will be the syntax for that.
In a similar need, we implemented a super pom, which is a single file component (pom.xml only) that declares all common properties and settings to be used by all components developed in our R&D group.
We mostly define common properties like dependencies versions strings and also the section is there.
Every component needing this needs to declare the super pom as its parent:
<parent>
<groupId>com.mycompany</groupId>
<artifactId>super-pom</artifactId>
<version>1.0.1</version>
</parent>
This works very well for us for managing 100s of components in the system, which all share common properties.
You can add may common elements like plugins, dependency management etc.
I hope this helps.

Avoid referencing parent pom in maven multi module project

My multi module project is made of public (open source) and private (undisclosed) modules. I need to create a master-all pom file referencing master-public and master-private, so that some plugins & commands are aware of all projects (e.g. cobertura). master-all has thus to be private as it references master-private.
The problem is that master-public should reference its parent master-all which is private, so users of public modules only won't be able to build them:
<groupId>group</groupId>
<artifactId>master-public</artifactId>
<parent>
<groupId>group</groupId>
<artifactId>master-all</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
Nesting master-public in master-private could be a solution for maven, but will be a mess for git.
Is there a clean way to do this?
You can have your master-private reference your master-public. But in my experience, it is best to keep them completely separate. Eventually, the master-public will be tuned more to open source, possibly deploying to Maven Central, while the master-private will deploy to your internal repository and possible have some special settings that only make sense in your enterprise environment. Copy whatever you absolutely need from your master-private to your master-public and disconnect the two.

Do I need to have a Parent declaration in my POM.xml?

I've declared my project as a pom.xml to define the parent, and it triggers a reactor build of all the included modules. Everything builds fine and works as expected. It builds in the right order, all the tests run correctly, and I get my expected output.
One of the projects is a shared library. I don't want to add a <parent> declaration here, so I didn't. It all still works.
My Question: do I need to bother adding a parent project declaration in any of my sub-projects? What are the pros and cons of having a two-way relationship between the projects? If I don't add the declarations, am I going to make it harder later when something stops working?
Rephrased into a single question: why bother with <parent> configuration in module pom.xml files?
Your question is related to the difference between Project Inheritance vs. Project Aggregation. The <parent> reference defines the inheritance relationship. The <modules> section in the parent pom.xml defines the aggregation. They are different.
If you do not have the <parent> configuration in the module pom.xml, it will not inherit the parent pom.xml configuration. So let's say you define the version of a dependency in the parent pom in the <dependencyManagement> section, the module pom.xml without parent reference will not inherit that. Or if all your child modules need to use a common library, you can define the dependency in the parent pom.xml. However, the module pom.xml without the parent reference will not inherit that dependency either.
For details, please check out Project Inheritance vs Project Aggregation
It depends on how you configure your build. If you have a correctly configured parent pom, basic information like version and groupid can be shared between your projects.
You can define the project wide configuration information that should be shared in all the modules in the project. For example:
<modelVersion>4.0.0</modelVersion>
<groupId>groupd.id</groupId>
<artifactId>artifact.id</artifactId>
<packaging>pom</packaging>
<version>version</version>
With that in place, you do not have duplicate that information in the module projects. The same information can be referenced like so:
<parent>
<groupId>group.id</groupId>
<artifactId>artifact.id</artifactId>
<version>version</version>
</parent>
Remember that the parent pom can be used to shared more information than specified above. You can do dependency management, plugin management and even define re-usable profiles.

Resources