I have a java project which is previously developed by another developer, in this application there are a lot of unnecessary libraries. I was able to delete some of those libraries. But, as there are a lot of them I want to know whether there is a technique to scan the project and detect unrelated libraries to the project.
I recommend Tattletale from JBoss.
Depending on the build method, it should be easy to remove unnecessary libraries without deleting/adding them by simply recompiling the application modifying the classpath at compile time, or rather modifying the build files by one-by-one deletion of lib dependencies. Either one of these methods will require no manual deletion of jars.
1) If you set the jars using the classpath manually, you can play with classpath wild cards to remove / add different jars when running : Setting multiple jars in java classpath classpaths . This does not require manual deletion / adding of jars.
2) Find the dependencies definition (pom.xml in maven, ivy.xml for some ant projects).
and One by one delete the (maven)
<dependency>..</dependency>
or (ivy)
<dependency .... />
entries in your build file.
If you have the correct build plugins installed - then your IDE will immediately rebuild the project using your new definitions (no error prone removal/deletion of jars or classpath entries).
Related
I have a spring project and it is running well. The problem is, whenever I perform a new deployment using the war file in tomcat, I need to manually copy the libraries in the lib folder. I am wondering if there is any way to add those libraries inside the war file so that every time I don't need to waste my time by copying the libraries in the server. It needs to be automatically included from the generated war file.
This answer depends on how you are building your WAR but I assume you are using one of the popular build tools for Java. Given that your war does not have a populated lib folder I'm assuming you have either not configured the right plugins for these build tools or you are compiling and managing your classpath from the command line like a madman. If the later is the case then I highly suggest you start using a build tool for your projects (All IDE's do this by default), if the former is the case I refer you to the individual pages for the plugins:
Maven:
https://maven.apache.org/plugins/maven-war-plugin/index.html
The war plugin does this by default.
Gradle: https://docs.gradle.org/current/userguide/war_plugin.html
The War plugin for Gradle also does this by default.
Ant: https://ant.apache.org/manual/Tasks/war.html
A glossary reading for the Ant tasks did not reveal if it did so by default but I'm assuming it does.
I am trying to add a dependency as jar file and that jar is shipped with all the classes which are needed for it to run.
compile files('lib/org.hl7.fhir.igpublisher.jar')
Along with that, I have few other dependencies which are added as maven dependencies.
compile ("ca.uhn.hapi.fhir:hapi-fhir-base:2.3")
Now I am facing a lot of issues related to class conflicts because same classes have been shipped with different versions.
In an ideal case, how should I solve this problem? I want to say that the local jar should always use its own files and other dependencies should ignore the local jar files.
Note:- I am using IntelliJ idea.
This is a tricky problem. There is only one classpath and multiple versions of the same class mean that only one of that versions is visible and the other ones are hidden.
One should generally avoid to declare dependencies on "fat jars" that contain their own dependencies. If possible, one should use the slim version without the dependencies (often both versions are published). If there is not alternative one can construct such a slim jar yourself by manually splitting up the jar file. It is also possible to control the structure by carefully ordering the dependencies on the classpath, but this is a little brittle.
I have read numerous posts regarding this, and I was still not able to find a clear-cut answer.
We have the need to use a proprietary SDK in our maven project and this SDK contains ~315 jar files that are needed for around 30 lines of code (SAP product). Every answer I read dealt with adding individual jars to your local maven repo. That is fine and I understand that, but is it possible to add an entire directory of libraries. These libraries are only needed for compiling the project since they are already on the classpath of the target server (They would all be scoped as provided in a pom).
I've tagged Netbeans 8 since that is the IDE I am using, so if anyone knows a hack to get a maven project in netbeans compiled using libraries on Netbeans classpath that would be a good solution as well...
JAR's are just java .class organized in folders and Zipped. Extract all those 315 JARs to somewhere, thus merging all of their content, and then Zip it again to one single fat JAR file. Add this fat JAR to your local repository as you have read elsewhere.
This other question can help you with the JAR merging thing: How to combine two Jar files
Although there are many messy workarounds for this, the ideal would be to let the compilation fail and search for the missing compile jars using a search utility like agent ransack you can search within the jars in that directory for the missing classes referenced in the compiler errors. As you find the jars you need, add them as dependencies with the scope of provided.
A less clean option would be to zip all of the jars, use the dependency plugin to unpack them to a folder and add that folder to the classpath of the build, then remove them or exclude them from the final package.
So I'm fairly new to Java and especially Eclipse, so please excuse my ignorance. I took a project from a server and copied it locally to my machine. When I opened the workspace, I had many errors due to it not being able to find the jars. This makes sense because I don't have the same dir structure as the server I copied from. So if I copy the same external jar's to my machine and get it to compile into a jar and copy it back to the server, will it work? Or will it fail because now the external jar's are in a different place than it is expecting?
Also, down the road should I put the external jars into regular jars to avoid this problem?
You should be OK. Java is using what is called classpath to locate dependencies. The classpath may be different on the development machines, but as long as all the dependencies are on the classpath in the production everything should work.
To avoid issues with the synchronisation of directory structures the most common way is to use Maven - it will manage all the dependencies for you (but you have to manage the pom.xml - the Maven's project descriptor). A little clumsier way is to have the dependencies in the project, however you may end up with many projects having to include same jars, and then there will be version conflicts and so on.
For small projects you can manage dependencies yourself, however larger projects will need a more thought through strategy (like Maven).
In regard to the executable jars, make sure the Class-Path entry in <jarfile>:\META-INF\MANIFEST.MF is correct, e.g. where it references other jars, those jars are going to be there in the production. For example, assume we have ourjar.jar and assume this is a snippet from its MANIFEST.MF:
Class-Path: lib/myteamjar.jar
It will then be expected that a following directory structure is in place:
lib/myteamjar.jar
ourjar.jar
No, the location of the external jars does not mater. What you want to do is put the external jars on your classpath. How you do it depends on how you are running your java code. If you are running it from the CLI using the java command, it takes the classpath as an argument. If you want your code to build/run in Eclipse, you need to right click on your project, select "Build Path" > "Configure Build Path..." Use the "Add JARs..." button to add jars that are part of a project you have open and "Add External JARs..." to add jars that reside outside of the project. See specific documentation for your tool for more details about classpaths.
I would not recommend Maven to somebody who is fairly new to Java and Eclipse. I would forget about Eclipse, too.
You have a packaging and CLASSPATH issue. Focus on that.
What kind of project are you talking about? The answer you get will depend on what type of app you're creating. Is it an executable JAR? Then the right way to do it is to package everything into a ZIP file that's laid out exactly as the CLASSPATH in the JAR manifest expects.
If it's a web app, the right thing is a WAR file, with all the JARs your app needs in the WEB-INF/lib directory.
If you package things properly, you should end up with a single package that has everything laid out the right way. You should be able to deploy it to the server and make it all work.
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.