Want this course?
Course: JVM Fundamentals for Clojure
Like a surgeon, we dissect JAR files to figure out how they tick. We also take a look at the
jar command and how to use it to create, extract, and update JAR files.
Hello. Today, we're going to be looking at a JAR file. We're going to dissect JARs and look at their anatomy, one little bit at a time. And the tool we're going use, it's like a scalpel, is the tool called JAR. So, it's just J-A-R. Now, if you'll remember, JAR stands for Java archive, just like a TAR file, which you might be familiar with, if you work on Unix. A TAR file is a tape archive. And so the JAR file, the JAR command uses a lot of the same kind of conventions as the TAR tool.
So, let's just take a look at it. Here are all of the options. Now a JAR file is just a zip file, but the JAR tool is specialized to make zip files that have the characteristics that you need, like the directory structure and everything that goes in there. So this is a JAR file. It has some commands. The ones you really need to know about are C, which is to create a new archive. And so you say c, and then, just like in the TAR file, in the TAR tool, you're going to use the f, to say the name of the file to create.
So if you want to create an archive, you do, jar, c, which is to create f, says, "Here's the file name." And you give it, give it my, jar.jar, and you'd say that I want all the class files, or I want all the closure files. Okay, so that's how you build a JAR file with all of the coder in the current directory. Okay, I'm not going to actually do that. You can imagine it happening. You can also put like a directory name, it doesn't have to be files.
Okay, now what if you have a JAR file and you want to add a file or modify a file? Well, that's the u, command. That's your update existing archive. And then you need the same f, to specify the JAR file, and then the files that you want to add or change. If you want to list the files in a JAR file, you don't want to extract it say, you just want to list it, see, I've got some JAR files in here. Let's do joda-time. I'll do jar, t, f, that's for listing, let's see, list table of contents, joda-time.jar. There we go, we just see a bunch of files, you know there's a ton of stuff in there. And we're going to go through the contents of this, so you see like what is in there.
I just wanted to show you the command, if you need to like grep through this and see what's in there. It's there. Okay, and now if you want to extract it, here's the one we're going to use in a minute, you do x, f. So, let's actually do that. Let's look at joda-time first. I'm going to go to this directory, because it's going to extract it like right where you're at. And so, it's just going to explode all these files right in the directory that you're in. So I want to be in, like, a new directory, see there's nothing in here. There, it's just totally empty.
So I'm going to do jar, x, f, and that joda-time, joda-time.jar. So x, extract, f, is for file and then you give it the file, and it's going to explode that out, unzip it, basically. And now you see, that's what I put in here. There's two things to know. One is META-INF. This is a directory that has some meta information about the JAR. The important one is the MANIFEST. This one is used by the Java tool, you know, the runner, the JAVA run time, to figure stuff out about it. That's META-INF, and then all the class files are in here. Right, so you have all these by package, all the class files are just there in a directory. And so you go in here and you see everything's in here. And what's interesting is you even have stuff that is like not class files. Oh, let's go here, to binary file. We'll it's not a class file. It's doesn't end with a .class, but it is, this file I wanted to see, is binary. And it's in there, too. All of that is loadable by the class loader.
Meaning that if a class loader can read a JAR file, then they can look for classes in the package in the JAR file and the class path in those package directories, it can also find these files that are in there, and that happens like when you're packaging up something, in the resources directory. All of that stuff gets put into the JAR file, too. And we'll see that in another lesson. Okay, so we're just taking this little tour of what's inside here. Let's get all the way back to the top.
Okay. Now let's look again at that META, that MANIFEST. I mean license, you know, that's good, you need to package that in there, too, but see this is like a keyword, colon, property. So, it's like key value stuff about this package that things can read. The things that are important are this, like jdk, the Build-jdk. And then another thing is, it has to end with a blank line, okay? We're going to see that again in a minute.
Also, I think there's only one MANIFEST version, but it has to be there. Okay, a lot of this stuff is like, for package managers and stuff to use, not for JAVA itself. Okay, let's get out of joda-time, and let's look at the closure JAR. Now the reason I want to look at closure is because it's executable. You can do java-jar, right, so this is going to put this one JAR in the class path. There's the closure 1,8, JAR, and then I can do, give it a main class. Right, so I can do closure.repl. Is that going to work? No. Well it's executable. I can do this and I get a repl, right? You can't do that with joda-time. So, let's look at how that's done.
So we're going to JAR it, we're going to extract it, and this is just for information purposes, I rarely extract out my JARs and stuff, but here we go. Let's extract that out. Now, oh, I want to show you what a closure, joda-time is built in JAVA, so let's before we look at the MANIFEST, let's look at what's in here. See, there's 2,192 files in there. I do not want to display those. I will list them. Okay, let's look at tree, maybe that will help. But, see what happens is, every single function gets it's own class. This is when you, ahead of time, compile it. It's all going to be written out into class files so that, you know, that's how, Java has to read in those functions.
You see, they are still done by package, by name space. See, test, junit, repl, proxy, io, PushbackReader, see these are all things, a lot of this was compiled from JAVA, a lot of it is closure that is compiled. But it's, you know, it's just a whole bunch of files in here that are built in. So, yep, I just want to show you all of these files in here. That dollar sign is a normal thing that JAVA uses for inner classes, and closure just takes advantage of that.
Okay. So let's get back out of here, and let's look at META-INF. Let's look at the MANIFEST. Now it's a much simpler MANIFEST, not as much information. We see that it was built with 1.6, but then here we go, this is what makes it executable. Main-class. Don't forget the empty line here. Main-class is closeure.main. So, if we were to open up the source code for closure, we would see that there is package, or a class, called main in the closure package that has a main method, that would, you know, either run the repl or parse the command line arguments and do what needs to be done there.
So this is the secret for making an executable JAR, right there in the MANIFEST file. And so, there's actually a way with, let me get out of here, there's a, with the JAR tool, there is a way to set the MANIFEST exactly with the tool. So, you do jar, and now if you're creating, you do a c, and then m, says the MANIFEST. And then, so now the files have to be in that same order. So that MANIFEST has to come first, and if you have some MANIFEST file that you want to add to the JAR, then you do like this. Okay, so if you want to add a main class to something, you can do that. Okay. And I've already shown you how you can use the -jar to do a main, to do an executable JAR, so I can do it like this, oops, there, and it'll run.
Okay, so that is the JAR anatomy, that's how things work. I want to show you one more think about an uber-jar. So, I've got this uber-jar that I've made. Now an uber-jar, is a JAR file with all of its dependencies included inside it, so it is self-contained. It's going to be a lot bigger, because it's got all those other classes in it, but it's something that you can give to someone, and say run this, you don't need anything on your class path. It's all in there. And, we're going to see how to make those with both linia and boot, but for right now, just realize that I've got that. So, I'm going to unzip it, or un-TAR it, or un-JAR it, x, f, I'm just going to, I'm just going to do it, it's going to be big.
There we go. Now you see all the stuff in here. We still have the familiar stuff, the META-INF. We also have the package that I created, but look, there's all this other stuff. There are all the dependencies that this nitty-grits package uses. So, it's going to have clj-json, it's going to have com., something, we don't know, we'd have to go in there to see. It's got the google closure compiler stuff, it's got ring. So all of the classes are in there, so let's go into ring. Let's see, middleware. So here's all the ring stuff, and notice, ring has put in it's source code, into the JAR. That's where that comes from, and this is an interesting thing about the closure compiler, it will create a params, init.class that is all the stuff that gets executed when you load up that clj file. And then, of course, I've defined functions inside of that name space, or ring has, has defined function inside of that name space, like assoc, form, params. That's probably a function, and so that gets compiled in as an inner class. And then, this is some anonymous function in there. And this is an anonymous function inside of another function.
Okay, so all these things are just splattered out into the directory as different class files. And they, you know, the compiler knows how to load it all. Okay. That was just a little aside there. But you see, all of the dependencies are built into that and so you can just give that JAR to something. You can just deploy the JAR to your server and it can run without having to go to MAVEN and fetch everything independently.
And so, in some way, it's a good thing because it's independent and doesn't rely on anything else. And it's how I typically will deploy some things. I'll build up an uber-JAR and that artifact gets shipped off to a server. Okay, so that's the anatomy of a JAR file. It's very simple. It's just a bunch of files in a zip file with a META-INF director and a MANIFEST.