Find your application Missing Jars, Playing with apache BCEL
As saied by apache, The Byte Code Engineering Library (Apache Commons BCEL™) is intended to give users a convenient way to analyze, create, and manipulate (binary) Java class files (those ending with .class). Classes are represented by objects which contain all the symbolic information of the given class: methods, fields and byte code instructions, in particular. Here, we will explain a way to check if your application is missing any Jars that may happen when moving your application from testing/development environments to production ones.
Let's explain the required with example:
Once upon a time, I developed a web application that connects to some WebService, I've built the client using JBoss 6 wsconsume tool, everything were going perfectly on my local machine (weblogic 10.3.6 with development mode on), but once I moved the application to another production like environment (weblogic 10.3.3 with production mode on), the application started to complain about a missing class used by the WebService client. There is no problem with fixing "ClassNotFoundException" issues, as the exception already tells you what is the missing class, but this was not the case with me. The used library by the WebService client that produces the error "ClassNotFoundException" catch the exception and throws another "ClassNotFoundException", but in the second one, it set the following error message "...ClassNotFoundException: Engine failed to initialize" !!!, and then I got totally stuck with this exception. After digging in google about the issue, I found that one of the library classes has a static Logger field and that my application is missing the library "Apache Commons Logging" that has that class inside.
The required is to develop a tool that scans all application classes and all referenced classes by application classes and so on and check if any referenced classes are missing from the class path or all required classes are available and everything is OK.
The tool code:
-
package com.myapp;
-
-
import java.util.ArrayList;
-
import java.util.HashSet;
-
import java.util.LinkedList;
-
import java.util.Queue;
-
-
import org.apache.bcel.Repository;
-
import org.apache.bcel.classfile.Constant;
-
import org.apache.bcel.classfile.ConstantClass;
-
import org.apache.bcel.classfile.ConstantUtf8;
-
import org.apache.bcel.classfile.JavaClass;
-
-
public class MissingJarsScanner {
-
-
HashSet<String> foundClasses = new HashSet<String>();
-
Queue<String> classesQueue = new LinkedList<String>();
-
foundClasses.add(entryClass);
-
classesQueue.add(entryClass);
-
ArrayList<String> notFound = new ArrayList<String>();
-
-
while (!classesQueue.isEmpty()) {
-
try {
-
for (Constant constant : mainClass.getConstantPool()
-
.getConstantPool()) {
-
if (constant instanceof ConstantClass) {
-
.getConstantPool().getConstant(
-
((ConstantClass) constant)
-
.getNameIndex())).getBytes();
-
if (className.startsWith("[")) {
-
className = className.substring(className
-
.lastIndexOf('[') + 1);
-
if (className.startsWith("L"))
-
className = className.substring(1,
-
className.length() - 1);
-
else
-
continue;
-
}
-
className = className.replaceAll("/", "\\.");
-
if (!foundClasses.contains(className)) {
-
foundClasses.add(className);
-
classesQueue.offer(className);
-
}
-
}
-
}
-
notFound.add(classToScan);
-
}
-
}
-
-
System.out
-
.println("Your application is missing the following classes : \n");
-
}
-
}
-
}
Now Let's explain the code,
- As stated by the JVM specs http://docs.oracle.com/javase/specs/jvms/se5.0/html/ClassFile.doc.html, any referenced class will be saved in a place called the "Constant Pool", the JVM specs says: "Java virtual machine instructions do not rely on the runtime layout of classes, interfaces, class instances, or arrays. Instead, instructions refer to symbolic information in the constant_pool table",
-
Our tool loads and parse the entry class using BCEL and then
Extract the Constant Pool from inside the class, after that
The tool looks for all constant pool items of type "CONSTANT_Class_info" which represent a class type, each item will contains an attribute called "Name Index" which refere to a "CONSTANT_Utf8_info" constant pool item, the CONSTANT_Utf8_info structure is used to represent constant string values, which in our case will be the class full name (i.e. java/lang/Object, org/richfaces/component/html/HtmlMessage, [Ljava/lang/StackTraceElement;). The CONSTANT_Class_info has 3 cases, to represent a single class type (i.e java/lang/Object), an array of class type (i.e. [Ljava/lang/StackTraceElement;) or array/single primitive (int, long...etc) type (i.e. [I -array of integers-). - In case the CONSTANT_Class_info represents a single/array of class type, we fetch the CONSTANT_Utf8_info entry that holds the class name and remove unwanted characters (i.e. array symbol '[')
- In case the CONSTANT_Class_info represents an array of primitive types (int, long) the item will be ignored
- The fetched class will be saved to be scanned too in the classesQueue
- All found classes are saved in foundClasses and all missing classes are saved in notFound list
Now Let's test our code, suppose we have the following application:
add richfaces 4.1 libraries to the classpath, execute the code, you will find that everything went OK and no exceptions were thrown and the application did not complain about any missing classes, but Let's check that with our tool, add the following class to the Test application:
with the same classpath entries (which holds the richfaces libraries) execute the FindMissingJarsTest class main method, you will find the following output:
-
Scaning class : com.myapp.Test
-
Scaning class : org.richfaces.component.html.HtmlMessage
-
Scaning class : java.lang.StringBuilder
-
Scaning class : java.lang.Class
-
Scaning class : org.richfaces.component.UIRichMessage
-
Scaning class : javax.faces.component.behavior.ClientBehaviorHolder
-
Scaning class : org.richfaces.component.html.HtmlMessage$Properties
-
javax.faces.component.behavior.ClientBehaviorHolder
-
Scaning class : javax.faces.component.StateHelper
-
javax.faces.component.StateHelper
-
Scaning class : java.lang.StackTraceElement
-
-
...
-
-
Scaning class : java.util.prefs.NodeChangeEvent
-
Scaning class : java.security.cert.CRLSelector
-
Scaning class : sun.security.provider.certpath.OCSPRequest
-
-----------------------------------------------
-
-
Total Classes Referenced : 2126
-
Your application is missing the following classes :
-
-
javax.faces.component.behavior.ClientBehaviorHolder
-
javax.faces.component.StateHelper
-
javax.faces.component.UIMessage
Our tool scanning started from class "com.myapp.Test" and scanned all referenced classes, and found 2126 classes referenced and here is the required, our Test application is missing 3 classes, javax.faces.component.behavior.ClientBehaviorHolder, javax.faces.component.StateHelper and javax.faces.component.UIMessage, if you digged google for these classes you will find that the 3 are located in jsf2 Jars, so our Test application is missing the JSF api and JSF implementation libraries :)
This is just a simple use for apache BCEL, many other ideas - crazy ideas - can be implemented using this library
- Add new comment
- 3926 reads
Comments
That really ctparues the
Add new comment