Use of the Manifest.Mf File in Java

Use of the MANIFEST.MF file in Java

The content of the Manifest file in a JAR file created with version 1.0 of the Java Development Kit is the following.

Manifest-Version: 1.0

All the entries are as name-value pairs. The name of a header is separated from its value by a colon. The default manifest shows that it conforms to version 1.0 of the manifest specification.
The manifest can also contain information about the other files that are packaged in the archive. Exactly what file information is recorded in the manifest will depend on the intended use for the JAR file. The default manifest file makes no assumptions about what information it should record about other files, so its single line contains data only about itself.
Special-Purpose Manifest Headers

Depending on the intended role of the JAR file, the default manifest may have to be modified. If the JAR file is created only for the purpose of archival, then the MANIFEST.MF file is of no purpose.
Most uses of JAR files go beyond simple archiving and compression and require special information to be in the manifest file. Summarized below are brief descriptions of the headers that are required for some special-purpose JAR-file functions

Applications Bundled as JAR Files: If an application is bundled in a JAR file, the Java Virtual Machine needs to be told what the entry point to the application is. An entry point is any class with a public static void main(String[] args) method. This information is provided in the Main-Class header, which has the general form:

Main-Class: classname

The value classname is to be replaced with the application's entry point.

Download Extensions: Download extensions are JAR files that are referenced by the manifest files of other JAR files. In a typical situation, an applet will be bundled in a JAR file whose manifest references a JAR file (or several JAR files) that will serve as an extension for the purposes of that applet. Extensions may reference each other in the same way.
Download extensions are specified in the Class-Path header field in the manifest file of an applet, application, or another extension. A Class-Path header might look like this, for example:

Class-Path: servlet.jar infobus.jar acme/beans.jar

With this header, the classes in the files servlet.jar, infobus.jar, and acme/beans.jar will serve as extensions for purposes of the applet or application. The URLs in the Class-Path header are given relative to the URL of the JAR file of the applet or application.

Package Sealing: A package within a JAR file can be optionally sealed, which means that all classes defined in that package must be archived in the same JAR file. A package might be sealed to ensure version consistency among the classes in your software or as a security measure.
To seal a package, a Name header needs to be added for the package, followed by a Sealed header, similar to this:

Name: myCompany/myPackage/
Sealed: true

The Name header's value is the package's relative pathname. Note that it ends with a '/' to distinguish it from a filename. Any headers following a Name header, without any intervening blank lines, apply to the file or package specified in the Name header. In the above example, because the Sealed header occurs after the Name: myCompany/myPackage header, with no blank lines between, the Sealed header will be interpreted as applying (only) to the package myCompany/myPackage.

Package Versioning: The Package Versioning specification defines several manifest headers to hold versioning information. One set of such headers can be assigned to each package. The versioning headers should appear directly beneath the Name header for the package. This example shows all the versioning headers:

Name: java/util/
Specification-Title: "Java Utility Classes"
Specification-Version: "1.2"
Specification-Vendor: "Sun Microsystems, Inc.".
Implementation-Title: "java.util"
Implementation-Version: "build57"
Implementation-Vendor: "Sun Microsystems, Inc."

Additional information: https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html

What is use of MANIFEST.MF in WAR/JAR/EAR?

manifest.mf carries attributes of the artifact. One of the most well known ones is for example the main class of the jar that is used to start the jar file when no other class is specified. Syntax:

Main-Class: classname

Other purposes are, for example, package sealing and package versioning. Check out the java tutorial about it:
http://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html

A manifest in a jar usually contains much less information than for example AndroidManifest.xml. It is quite lightweight and does not contain any build or packaging information.

This is because java has no good module system. So, a jar is not a module which might need a lot of configuration information (like a list of modules to which it has dependencies). Instead, a jar is just a bunch of classes with some configuration information. Hopefully, this will be fixed by project jigsaw (http://openjdk.java.net/projects/jigsaw/).

What is the use of MANIFEST.MF file in Dynamic web project in Eclipse

The files under WebContent are ready to be packaged into a WAR file for deployment on a servlet container.

The Servlet spec mentions a role for META-INF/MANIFEST.MF in chapter "10.7.1 Dependencies On Extensions":

Application developers need to know what extensions are installed on a
Web container, and containers need to know what dependencies servlets
in a WAR have on such libraries in order to preserve portability. The
application developer depending on such an extension or extensions
must provide a META-INF/MANIFEST.MF entry in the WAR file listing all
extensions needed by the WAR.

The format of the manifest entry should
follow standard JAR manifest format. During deployment of the Web
application, the Web container must make the correct versions of the
extensions available to the application following the rules defined by
the Optional Package Versioning mechanism.

So the information of the MANIFEST.MF may be evaluated and needed by the container. But the spec is not very detailled about the nature of these extensions.

What defines the main class if MANIFEST.MF is not present

What defines the main class if MANIFEST.MF is not present

If a JAR file doesn't have a "META-INF/MANIFEST.MF" component, then it is not a JAR file. It is just a ZIP file and you cannot use java -jar ... with a ZIP file. (You can include a ZIP file on the classpath, but it is not normally done.)

If a JAR file has a MANIFEST.MF without a Main-Class attribute, then is not an executable JAR file, and java -jar ... will fail.

But (as noted) many JAR files are libraries rather than applications. For them an entry-point class makes no sense.



How is a non-runnable JAR file able to execute if the main class is not defined?

(The preferred Java terminology is "executable" rather than "runnable".)

In that case, java -jar ... will fail. Instead, you can run the application like this:

  java -cp <classpath> <other-options> com.example.MyApp.Main <args>

where com.example.MyApp.Main is a main / entry-point class, and <classpath> includes the JAR (or ZIP) file and any other runtime dependencies.

Note that an application JAR file could contain more than one entry-point class, and the user can decide which one to use.



What are the pros/cons of having a MANIFEST.MF file that defines the main class?

First of all, if you create a JAR file using the jar command, then it will have a MANIFEST.MF. The command won't create a JAR without one.

Also, there are other useful things that you can include in a MANIFEST.MF. These include digital hashes (for signed JARs) and a Class-Path attribute for use when the JAR is launched with -jar. For more details, refer to the JAR file specification.

The pros of having a Main-Class attribute in a JAR file are:

  1. It is necessary if you want to use -jar.
  2. It means that the user doesn't need to know (or type) the full name for the entry-point class.

There are no significant cons of having a Main-Class attribute. If the user doesn't use the java -jar ... launch method, then any such attribute will be ignored. But I guess you could say that putting a nonsensical Main-Class attribute on a library JAR could lead to a naive user getting a misleading error. This is hair-splitting ...



Is one more stable than the other?

Not directly.

You could argue that using -jar is more stable because an executable JAR ignores the CLASSPATH environment variable and -cp arguments on the command line. But the flipside is that you can't force the user to use -jar (or double-click) to launch a command. And you can get similar stability by providing a shell script or BAT file to launch the application with an appropriate entry-point class name and an appropriate classpath.



I executed a non-runnable jar in Eclipse without the classpath or project files and then I ran it and it worked. It was able to identify the main class and run it from there. my question is: how was it able to identify it?

OK ... that is a different question.

What actually happens here is that the Eclipse project has a bunch of configuration information that includes the build dependencies. These provide a default classpath for the Eclipse launcher. Then, when you use Eclipse's run command without an existing run configuration, Eclipse will search all of the classes in the current project looking for any classes with a public static void main(String[]) method. If it finds only one such class, it assumes that it is the entry-point class, and creates a run configuration for the project / class. When that config is launched, Eclipse does the equivalent of java -cp <classpath> <class-name> <args>.

Notes:

  1. This is Eclipse specific behavior. The standard Java tool chain doesn't do anything like this.

  2. Eclipse is not using java -jar here, and hence the manifest won't be consulted to find the entry-point class.

  3. This has been known to break. For example, I have heard that if you delete the main class and create a new one, the launch config doesn't update, and it gives a JVM launch error when you attempt to "run" it.

What's the purpose of META-INF?

Generally speaking, you should not put anything into META-INF yourself. Instead, you should rely upon whatever you use to package up your JAR. This is one of the areas where I think Ant really excels: specifying JAR file manifest attributes. It's very easy to say something like:

<jar ...>
<manifest>
<attribute name="Main-Class" value="MyApplication"/>
</manifest>
</jar>

At least, I think that's easy... :-)

The point is that META-INF should be considered an internal Java meta directory. Don't mess with it! Any files you want to include with your JAR should be placed in some other sub-directory or at the root of the JAR itself.

Reading my own Jar's Manifest

You can do one of two things:

  1. Call getResources() and iterate through the returned collection of URLs, reading them as manifests until you find yours:

    Enumeration<URL> resources = getClass().getClassLoader()
    .getResources("META-INF/MANIFEST.MF");
    while (resources.hasMoreElements()) {
    try {
    Manifest manifest = new Manifest(resources.nextElement().openStream());
    // check that this is your manifest and do what you need or get the next one
    ...
    } catch (IOException E) {
    // handle
    }
    }
  2. You can try checking whether getClass().getClassLoader() is an instance of java.net.URLClassLoader. Majority of Sun classloaders are, including AppletClassLoader.
    You can then cast it and call findResource() which has been known - for applets, at least - to return the needed manifest directly:

    URLClassLoader cl = (URLClassLoader) getClass().getClassLoader();
    try {
    URL url = cl.findResource("META-INF/MANIFEST.MF");
    Manifest manifest = new Manifest(url.openStream());
    // do stuff with it
    ...
    } catch (IOException E) {
    // handle
    }

Replacing the MANIFEST.MF file in a JAR programmatically

You can use something like this:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.Manifest;

public class ManifestManipulator {

public static final void main(String... args) throws IOException {
if (args.length == 0) {
throw new IllegalArgumentException("at least the path to the JAR file expected!");
}
Path jarPath = Paths.get(args[0]);
Set<String> attributeNames = args.length > 1 ? new LinkedHashSet<>(List.of(args).subList(1, args.length)) : Set.of();

if (!attributeNames.isEmpty()) {
ManifestManipulator manifestManipulator = new ManifestManipulator();
manifestManipulator.removeManifestEntries(jarPath, attributeNames);
} else {
System.out.println("Warning: no entries specified to remove!");
}
}

private void removeManifestEntries(Path jarPath, Set<String> attributeNames) throws IOException {
System.out.println("Going to remove: " + attributeNames);
try (FileSystem jarFS = FileSystems.newFileSystem(URI.create("jar:" + jarPath.toUri()), Map.of())) {
Path manifestPath = jarFS.getPath("META-INF", "MANIFEST.MF");
Manifest manifest = readManifest(manifestPath);
Attributes mainAttributes = manifest.getMainAttributes();
System.out.println("Found main attribute names: " + mainAttributes.keySet());

boolean removed = mainAttributes.entrySet().removeIf(entry -> attributeNames.contains(((Name) entry.getKey()).toString()));
if (removed) {
writeManifest(manifestPath, manifest);
} else {
System.out.println("Warning: nothing removed");
}
}
}

private Manifest readManifest(Path manifestPath) throws IOException {
try (InputStream is = Files.newInputStream(manifestPath)) {
return new Manifest(is);
}
}

private void writeManifest(Path manifestPath, Manifest manifest) throws IOException {
try (OutputStream os = Files.newOutputStream(manifestPath)) {
manifest.write(os);
}
}

}

Please make sure you've added the jdk.zipfs module, which will provide a FileSystemProvider for ZIP/JAR files (see the technote).



Related Topics



Leave a reply



Submit