Month: January 2016

How to generate a MANIFEST & JAR using Maven

Posted on Updated on

Hello everyone, hope you had a pretty good time writing your own javaagent. If you did, then you may have wondered about ways you can do the following stuff.

  • Generate agent.jar
  • Avoid ClassNotFoundException and NoClassDeffFoundError

If you did, this post will give you some useful tips to achieve above targets. Assuming you have got maven installed let’s look into our solution.

In our case we need to bundle our MANIFEST file and .class files of our agent into agent.jar. There are several ways you can use to generate a JAR which bundles set of files we need.

  • Using command line : Executing following command with list of files you need to include, can be used to generate a JAR. You can find more details from here.
    • jar cf jar-file input-file(s)
  • Using the IDE : Different IDEs provide different steps to generate a JAR. You can find steps to create a JAR in Eclipse from here.

I am pretty sure no one is going to love above methods, because you have to create a jar each time you make a change to your agent. Trying to include each new file you create, to the end of the above command and trying to figure out how to maintain folder structure is going to be really annoying. Use of the IDE will also be a real pain when you get to go through all the above steps after each modification.

But, what if we can easily generate our agent.jar with just a single build command? What if it includes all the .class files you need to include, plus the MANIFEST file under correct folder structure?

Well here is the good news. Including the maven-jar-plugin as follows will do it like magic. Only thing you have to do is including following section under <build><plugins></plugins></build> tags in your pom file.

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-jar-plugin</artifactId>
   <version>2.4</version>
   <configuration>
      <archive>
         <manifest>
            <addClasspath>true</addClasspath>
            <classpathPrefix>lib/</classpathPrefix>
            <addDefaultImplementationEntries>
                  true</addDefaultImplementationEntries>
            <addDefaultSpecificationEntries>
                  true</addDefaultSpecificationEntries>
         </manifest>
         <manifestEntries>
            <Premain-Class>com.javaagent.SimpleAgent</Premain-Class>
            <Can-Redefine-Classes>false</Can-Redefine-Classes>
            <Can-Retransform-Classes>true</Can-Retransform-Classes>
         </manifestEntries>
       </archive>
   </configuration>
</plugin>

The above plugin addresses two main problems faced by those who work with a javaagent. I have already listed them at the beginning of the post. Let’s have an overall idea about what this plugin does.

Generating Agent.jar

As the name itself says, this plugin is used to create a JAR during the build time of the project. (We are using Maven to build the project) It will create a jar including all the class files in your project. The package structure would be same as it is in your src folder.

Other than creating the JAR, it will include one main thing that is needed by any JAR file. Even for the javaagent we have mentioned it as one of the main files we need. That is none other, but the MANIFEST file.

We have to provide directions on which file to look for, to access the main  method of the application. In our case it is the class with the premain method. <Premain-Class> tag  provides fully qualified class name of the class with premain method. Likewise <Main-Class> can be used to set the main class as the initiating point of the jar.

<Can-Redefine-Classes> and <Can-Retransform-Classes> allows user to specify some parameters required by the agent. Setting these values are optional, because their default values won’t conflict with our basic javaagent.

Avoid ClassNotFoundException and NoClassDeffFoundError

If you have been trying to instrument methods of JDBC or any other class, where you don’t have the paths of their class files / jars included in the classpath, agent may have aborted after throwing following Exceptions.

  • ClassNotFoundException : When an application try to load a class through its String name and no definition for the class with the given name can be found. (Eg: using forname(‘foo.com.TestClass’)
  • NoClassDefFoundError : When the JVM or classloader instance try to load definition of a class and definition can no longer be found. It may have found the class definition during compilation time, but fail to find it during run time.

Being unable to find the required class during run time of the agent can be considered as the main reason for both of these situations. This can be solved by including all the required jars to the classpath of the agent. Following tags will do it for you.

  • <addClasspath> : add all the names of jars downloaded, as dependencies of the agent.
  • <classpathPrefix> : add the given prefix (Eg: lib/) before all jar names included in the MANIFEST. This can be used as the path of each jar during the runtime of the agent.

Once you have set all correct values, you are all set to make a MANIFEST and include it in the JAR during the build time of your agent. ‘mvn clean install’ will do the magic for you. I’m pretty sure you are to love this method if you have been trying above two methods.

See you again from ‘How to copy maven dependencies’. Till then have a nice time…!!