How to configure Maven for a single codebase but multiple artifacts - java

I have a single very large codebase that compiles down to a JAR. I also use the shade plugin to compile it down and package it up with all dependencies. I also use the war plugin to get a WAR file.
With respect to the WAR file, once the goal is created I have a post-build event that simply copies the WAR file to its destination, so technically I'm good with that.
The problem I have is with the other two JARs. They both share the same artifactId since they're both built in a single pom.xml and this isn't acceptable for a number of reasons (including, but not limited to the fact that some caching of dependencies is pretty dumb about realizing that one JAR is the thin one and one is the full-dependency one).
What I need to do is create a pom.xml (or group thereof) suitable for builds (by Jenkins) that can use the same codebase (pulled from Github) but create two separate JAR files, each with their own artifactId.
Being a Maven novice, I've read through the beginning book and it seems to me that what I want is a parent pom.xml with two modules. But from what I can tell, each module means a separate directory with separate code. As I said, this is built from the same codebase. The only difference is one is built from the "regular" build, and the other is built using the "shade" plugin and goal.
The only other thing I can think of is build the "regular" JAR and then build the shaded JAR with a classifier of "full?" If this is the answer, may I humbly ask for some adult supervision on how to do this, as I'm not seeing how.
If that's not the answer, I suspect this must be a common problem, so again, some guidance would be very helpful!

The solution I came up with was to use a classifier for the "shaded" jar. Thus, the artifacts don't collide.
I then had an issue accessing it, but found the solution to that issue as well - How do I access a jar with a classifier?

Related

Could not create a maven script with such a dependency (interdependent projects)

I have a case where there are multiple projects and each are interdependent and I could not create a maven script with such a dependency because jars wont be ready to use as both are interdependent. Do we have a solution for such cases in maven? The solution what i see here are below:
Remove the dependency between the projects means change the code to remove such interdependent.
Merge two projects into one in the build - means create single jar by picking up source from 2 projects.
I dont want to do either of do as it needs additional efforts to do so.
Is there a way source of one project can be used in another project and not being part of JAR?
In Maven you cannot build interdependent jars. This problem also arose in our company when we moved legacy projects to Maven. In my opinion one should really put effort into solving this situation, so use your proposals (1) or (2) or generate a third project which contained the shared classes on which both jars depend. Any "trick" to avoid this would be as complicated and lead to a bad project structure.

Well structured organization of jars - is it possible for a set of projects?

Here and on the net I see much advice about how to put jars into a project. There are many ways for it. But a real life Project consists of many eclipse projects. And many of them have same jars.
I have one real project consisting of a few (a dozen) java projects. And the Ant is used for building.
I want to keep my and standard jars so:
It should be some understandable structure,
Jars should be easy to reach for referencing (excellent if I can include them by groups)
Jars should be easy and safe to update. I would like to minimize the possibility of having two jars of different versions in different projects
If I reference the projects from each other, I will quickly come to loop references. If I put every jar physically into each project, it is bad because of duplicity - later I will surely forget to update the jar in some of the projects. If I put a jar into some project physically and reference it from others, later I will have to search, where the appropriate jar is, in order to update is. The only structure that I had managed to think of, is to divide jar into some subsystems, make extra projects (library projects), that will contain these jars and to reference these projects from the real ones.
But maybe Eclipse or Ant have some automated solution?
(If it is impossible, can IntelliJIdea or Maven or Gradle or something else do the thing?)
Again, please, notice, I am NOT talking about how to put jars into a project, but about how to organize them in a set of projects.

Maven: is there simple way to generate a pom.xml file only?

I wanted to take a legacy project, and generate an initial pom.xml file for it, without converting the whole project. (The thinking is to take advantage of some Maven tasks without creating a big ripple effect in the automated builds that already include this project, which are not Maven projects.)
Is there some way to do this other than archetype:generate (in a phony directory), and then
copying the pom.xml to where I really want it?
AFAIK, no.
But if you know enough about Maven, you can typically write a POM file for a legacy project.
The complication is that the files / directories of a legacy project will typically not be organized in the Maven recommended way, so the normal generation tools won't work. However, "it is said" that Maven can cope with non-standard organisations ... if you write the POM file appropriately.
Another approach is to build the legacy project in the legacy way, and then manually add the resulting JAR files to your Maven repo with appropriate "coordinates" (i.e. group-id, artifact-id and version) so that your Maven projects can use them.
You seem to want to do an unorthodox thing with Maven, and while I like Maven, I find Maven is absolutely terrible at doing unorthodox things.
I would suggest using Gradle instead--especially since you just need to perform one task and don't need to build with it (and therefore learn it).
Because Gradle is a Groovy DSL, you can simply write some Groovy code to access a repository and copy it into a directory on the local file system.
(I will leave aside my personal abhorrence of putting compiled artifacts into source control.)
Gradle also has outstanding integration with Ant.

Maven group library

I have a web app with thousands classes packed in hundred jar's placed in several folders.
I want create in local maven repository a library, containing all classes from those jars.
And I can use this dependensy in all my projects...
Like in a IntelliJ IDEA i create a global library. Select a folder with sets of jars with subfolders and set name "My web-app libs". And then i add this global lib on my project.
I have founded a way for construct group for several libraries, described here. But I have a hundred jar files and stupid make new project for each.
In advance thanks.
Look at maven shade plugin.
This plugin provides the capability to package the artifact in an uber-jar,
You may want to create another project which enlists all your artifacts as dependencies. So when you include it into your project all the needed 100 dependencies will be transitively resolved.
Maven Versions plugin could help you bulk updating the many versions inside this new artifact.
UPDATE
If all your 300+ jars are completely static, i.e. their versions are fixed, you might probably need to repack them with the shade plugin. Otherwise updating a version of a JAR from this huge set could be a trouble... I can't predict the performance, but my guess is that a normal Maven approach is more efficient.
A note on Maven shade plugin: you might need to move your shaded libraries to shaded package. That's what they usually do to avoid library versions conflicts. I.e. if your 300+ libs use spring-2.0 and your current project uses spring-3.1.0, both will be included anyway. So to avoid conflicts, it's recommended to configure this plugin to move spring-2.0 packages under a different package.
UPDATE 2
If your jars are not mavenized, Maven won't be a big help here. You should probably merge your jars manually and check if it works for you: Merging Multiple Jars in to a Single Jar.

What's the best way to share JARs across multiple projects?

When you have multiple projects that all use the same set of JAR libraries, it's tedious to include the same JARs over and over again with each project. If I'm working on 20 different projects, I'd rather not have 20 of the same exact set of JAR files lying around. What's the best way to make all those projects (and new projects as well) reference the same set of JARs?
I have some ideas, but each of them has some disadvantages:
Place all the JARs in a folder and have each project look in that folder.
Using Eclipse, create a "User Library" and have each project reference that user library.
Create a "Library" project that references each JAR, and have each project reference that library project.
Believe it or not, your 'tedious' approach is probably the simplest, cleanest and least time-consuming approach there is.
Before jumping on the maven bandwagon you should consider what is really wrong with doing things the way you are currently doing them. You mentioned that it is tedious and that you have a lot of jar files lying around. I created the build process on a large multi-module project using Maven then spent the next 18 months battling with it constantly. Believe me it was tedious and there were a lot of jar files lying around.
Since going back to Ant and committing jars to source control alongside the projects that use them it has been a much smoother ride.
I store a bunch of jar files in a single directory on my machine and then when I create a new project or need to add a new jar to an existing project it only takes about 30 seconds:
Copy the jar from JAR_REPO to project lib dir.
Add jar to build.properties
Add jar to classpath in build.xml
Add jar to build path in Eclipse.
Over the course of a project, that 30 seconds is insignificant, but it means I have a project that can be checked out of source control and just works without requiring any custom Eclipse configuration or Maven installations or user-specific setup.
This approach has saved me and my project team a huge amount of time, mainly because it is simple, reliable and easy to understand.
Update: Clarification prompted by comments
#Robert Munteanu: Thanks for the feedback and updated comments. This might sound a bit argumentative but I'm afraid I can't agree with you that Maven is simpler and clearer, or that it will save you time in the long run.
From your posting:
"I strongly believe that it's simpler and clearer to declare dependencies rather then manually include them. There is a small one-time cost associated with this - smaller for Ivy than for Maven - but in the long run it does pay off."
It may be easier to have Maven download a jar file for you than having to download it yourself but that's the only advantage. Otherwise Maven is not simpler, not clearer and its complexities and limitations will cost you in the long run.
Clarity
The two dependency declarations below do the same thing. I find the Ant one much clearer than the Maven one.
Ant Style:
<path id="compile.classpath">
<pathelement location="${log4j.jar}" />
<pathelement location="${spring.jar}" />
</path>
Maven Style:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
Simplicity
With the Ant version you can hover over the ${log4j.jar} property and it will show you the absolute path to the jar file. You can search for usage of compile.classpath. There's not a whole lot more you need to know.
There is no question that Maven is more complex than the approach I'm suggesting. When you start out with Maven these are just some of the questions that need to be answered.
What does groupId mean?
What does artifactId mean?
Where does the jar come from?
Where is the jar now?
What is provided scope? Who's providing it?
How did that jar file end up in my WAR file?
Why does this dependency not have a version element?
I don't understand this error message. What on Earth does it mean?
Where on Earth did that jar file come from? I didn't declare it.
Why do I have 2 versions of the same jar file on my classpath?
Why does the project not build any more? Nothing has changed since the last time I built it.
How do I add a third-party jar that's not in the Maven repository?
Tell me again where I get that Eclipse plugin from.
Transitive Dependencies
"Another, smaller, benefit is the handling of transitive and conflicting dependencies."
In my experience, transitive dependencies are more trouble than they're worth. You end up with multiple versions of the same jar file and you end up with optional jar files that you don't want. I ended up declaring just about everything with provided scope to avoid the hassle.
The Long Term Payoff
"Focus on programming, not building."
I agree. Since going back to Ant and putting my jar files in source control I have been able to spend far less time dealing with build issues.
These are the things I spend less time doing:
Reading poor Maven documentation.
Reading even poorer Codehaus Mojo documentation.
Setting up shared internal repositories.
Educating team members.
Writing Maven plugins to fill the gaps.
Trying to workaround defective plugins (release, assembly).
Installing Eclipse plugins for Maven.
Waiting for the plugin to give me back control of Eclipse.
Anyways, sorry about the long posting. Maybe now that I've got that off my chest I can bring some closure to my long and painful Maven experience. :)
Use Maven or Ivy to handle these shared jars. If you're wary of changing your projects too much, you can simply use Ivy to manage the extra classpath for you.
Both have good Eclipse plugins:
m2eclipse
Maven classpath container http://img229.imageshack.us/img229/4848/mavendependencies.png
IvyDE
IvyDE classpath container http://img76.imageshack.us/img76/3180/cpnode.jpg
which I've used with good results.
You'll note that both of them reference jars outside the workspace, so the duplication is removed.
Update ( prompted by comments ):
My reason for recommending this approach is that I strongly believe that it's simpler and clearer to declare dependencies rather then manually include them. There is a small one-time cost associated with this - smaller for Ivy than for Maven - but in the long run it does pay off.
Another, smaller, benefit is the handling of transitive and conflicting dependencies. It's easy to forget why you need that commons-logging-1.1.jar in the classpath and whether you need to upgrade to 1.1.1. And also it's no fun to pull in all the depencies required for e.g. a Hibernate + Annotation + Spring combo. Focus on programming, not building.
It depends on your needs, but there are several viable options. My work uses an external folder and all projects reference that folder, which makes life easier running builds outside of eclipse. A user library is a slightly more plesant way of doing things, as long as you don't mind the slight eclipse dependancy. I don't see a whole lot of benefit to a library project on it's own, but if you have some sort of universal 'util' type project that all other projects already load, you could just put all the external jars in that project.
One approach is to put all your jar files in one location on your machine, in your eclipse ide, define an environment variable, say LIB_LOCATION that points to that directory and have your projects use the jars relative to that variable. This way, you get the ease of use, no multiple jars, portable across machines, as long as you have the variable defined correctly. I have been trying maven for a group of decent size projects and it seems I have to fight at least as much as I used to. Bugs and wired behaviors in the plug ins, m2eclipse and q4eclipse.
You might edit the "Installed JREs" to include your JAR file ("Add external JARs"), add the file to jdk\jre\lib\ext\ directory or specify a CLASSPATH environment variable containing the path to it.
I'd recommend the "library" project approach.
But even better -- a separate lib project per external jar -- this allows you to track deps between third-party jars and know what needs to change when you're upgrading a dependency.
Make sure you check in these projects so all users are using the same versions of third-party libs and so you can easily regenerate a version of software (use tags/labels in your version control to group which versions of which projects go together)
We have decided on a more tedious method but which allows us to have everything inhouse, but will probably only work well for a small set of developers.
Each set of jar files is set up as a Eclipse project named appropriately after the jar set, added to the build path, source jars and javadoc jars correctly set on each jar in the build path, and each project then includes those library projects needed for that project. The resulting multi-project workspace is then exported as a ProjectSet.psf file which can then be read in in a raw Eclipse bringing in the whole workspace again. We then have all the above projects in CVS including the jar files.
This has worked very wellf or us.
If you are in a larger organization the dependency handling in Maven may work well for you. You should definitively have a local cache of artifacts so the whole world doesn't stop if your internet connection is lost.
Also note that the new Eclipse 3.5 coming out this sommer, will have a "Create Runnable Jar" which can output the needed jars next to the generated runnable jar and set up the Class-PAth line in the Manifest correctly. I expect that to be a big time saver - check it out.

Categories