java: check compatibily between jar versions - java

Assume we have a set of projects "p1.jar", "p2.jar", etc, all them using library "foo-1.0.0.jar".
Assume a new version "foo-1.0.1.jar" is created. As an additional check, all projects p1, p2, ... are recompiled using new foo-1.0.1.jar. No compilation errors appears.
Now, in the production environment, foo-1.0.0.jar is replaced by foo-1.0.1.jar. No update of p1.jar, p2.jar, ... is done. Unfortunately, everything crashes.
It is possible to do some other checks to be 100% sure new library can replace old one?
Obviously, we are not talking about check of code errors introduced in new library or changes in the functionality (by example, if in new version function "sum" does a subtraction instead of an addition). But at least check changes in arguments, results, available classes, etc.
One example is, foo-1.0.0.jar contains method:
int m1() {
// some code
return 0; // always return 0, and ignored by the users
}
that in foo-1.0.1 it is changed to:
void m1() {
// some code
}
Unfortunately, the result of the method is part of the name mangling in Java (not in C) and p1.jar will fail with "NoSuchMethod" exception when looking for "int m1()".

You might use japicmp to compare the new and old library archive for API changes.
But there is still the risk that a dependency of the library you check would not be detected.
Find below an example to demonstrate changes which might be undetected.
ApiClass1.java
class ApiClass {
public static final int FOO = 23;
public static void version() {
System.out.println("version 1 FOO: " + FOO);
}
}
ApiClass2.java
class ApiClass {
public static final int FOO = 42;
public static void version() {
System.out.println("version 2 FOO: " + FOO);
}
}
ApiDemo.java
class ApiDemo {
public static void main(String...args) {
ApiClass.version();
System.out.println("ApiClass.FOO: " + ApiClass.FOO);
}
}
compile and build library for API version 1
javac ApiClass1.java
jar cf api_v1.jar ApiClass.class
compile and build library for API version 2
javac ApiClass2.java
jar cf api_v2.jar ApiClass.class
compile, build and run your code with API v1
javac -cp api_v1.jar ApiDemo.java
java -cp api_v1.jar:. ApiDemo
output
version 1 FOO: 23
ApiClass.FOO: 23
run your code with API v2, without recompilation
java -cp api_v2.jar:. ApiDemo
version 2 FOO: 42
ApiClass.FOO: 23
The only reliable way is to compile your code against the new version, run all your unit-/integration test and check if they pass.

Unpack both jars, old and new, with jar x
Get class contents with javap
Compare contents from two jars
For example, if you have foo-1.0.0.jar and foo-1.0.1.jar in current directory, you may run this bash script:
# unpack old jar
mkdir foo-1.0.0
cd foo-1.0.0
jar xf ../foo-1.0.0.jar
# unpack new jar
mkdir ../foo-1.0.1
cd ../foo-1.0.1
jar xf ../foo-1.0.1.jar
# get unpacked classes contents
cd ..
for file in `find foo-1.0.0 foo-1.0.1 -name '*.class'`; do javap $file > $file.txt; done
# remove redundant .class files
find foo-1.0.0 foo-1.0.1 -name '*.class' | xargs rm
# compare jar contents
diff -r foo-1.0.0 foo-1.0.1
Sure, that's not fully automated method, you still should to check diff output manually. Still, that's better, than nothing.

Related

Java javac compile with classpath

I was wondering if it's possible to include a jar in the classpath when compiling instead of executing. At the moment I am just checking to see if my PostgreSQL driver can be found
everything is in the same location for testing purposes so
program/
DriverCheck.java
DriverCheck.class
postgresql-longassname.jar
DriverCheck.java contains
public class DriverCheck {
public static void main(String[] args) {
try {
Class.forName("org.postgresql.Driver");
System.out.println(Driver Found);
} catch(Exception log) {
System.out.println(Driver Not Found);
}
}
}
If I compile and execute this as normal
# javac DriverCheck.java
# java -cp ".:./postgresql-longassname.jar" DriverCheck
It works as I get the output
Driver Found
However if I try to compile and execute in this manner
# javac -cp ".:./postgresql-longassname.jar" DriverCheck.java
# java DriverCheck
It does not work as I get the output
Driver Not Found
Why is this and is there a way for me to use the second method for including jars?
Why is this and is there a way for me to use the second method for including jars?
It's because specifying the classpath for compilation just tells the compiler where to find types. The compiler doesn't copy those types into its output - so if you want the same resources available when you execute, you need to specify the classpath at execution time.
In this case the compiler doesn't need the extra jar file at all - nothing in your source code refers to it... but you do need it at execution time... which is why your original approach works and your second approach doesn't.

Class not found error on Jpype

I have read and searched all stack overflow .. I also found JPype class not found but it didn't help me although it is solved! I have the same problem ! I am using Mac , python 2.7.6
My both python code and A.java are on desktop. But I keep receiving this error :
Traceback (most recent call last): File
"/Users/jeren/Desktop/aa.py", line 13, in
A = jpype.JClass("A") File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/jpype/_jclass.py",
line 54, in JClass
raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name) java.lang.ExceptionPyRaisable: java.lang.Exception: Class A not found
aa.py :
import jpype
import os
jpype.startJVM(jpype.getDefaultJVMPath(), "-ea", "-Djava.class.path=/Users/jeren/Desktop/")
A = jpype.JClass("A")
a = A()
jpype.shutdownJVM()
A.java :
class A
{
public A()
{
super();
}
public String sayHi()
{
return("Hello");
}
public static void main(String[] argv)
{
System.out.println ("Hello ");
}
public static int add(int a, int b)
{
return(a+b);
}
}
My mac , java and python are all 64bit ! where the problem can be?
everything was ok just needed to add a 'public' to the beginning of class A:
public class A
{
public A()
{
super();
}
public String sayHi()
{
return("Hello");
}
Here are some further nodes on specifying the class path for jpype.
A. Check JDK path
I had several versions of Java JDK installed and getDefaultJVMPath did not yield the expected path. I needed to replace
jpype.getDefaultJVMPath()
with the path to the JDK, that actually has been used to compile the code, e.g
D:/jdk11/bin/server/jvm.dll
B. relative paths
It is possible to use relative paths. If my python file is for example in a package folder "pkg" and my java class file is in a sub folder "foo" of a "bin" folder:
parentFolder
pkg/main.py
bin/foo/Foo.class
jpype.startJVM(jvmPath, '-Djava.class.path=../bin")
link = jpype.JClass('foo.Foo')
For this example, the working directory of the java application will be the pkg folder. With other words, inside a main method of Foo class, you might want to use "../" to access the parentFolder.
C. -cp option does not work
I tried to use -cp option instead of -Djava.class.path, which I would found more induitive. However, the following code does not work:
jpype.startJVM(jvmPath, '-cp', classPath)
D. jars need to be included individually
I tried to include a folder with several jar files.
parentFolder
foo/main.py
lib/foo.jar
Following code does not work:
jpype.startJVM(jvmPath, '-Djava.class.path=../lib/*")
link = jpype.JClass('foo.Foo')
Each jar file needs to be included individually, e.g.:
libOath = '../lib'
libJarPaths = str.join(';', [libPath + '/' + name for name in os.listdir(libPath)])
jpype.startJVM(jvmPath, '-Djava.class.path=../lib/*")
link = jpype.JClass('foo.Foo')
(Solution from JPype (Python): importing folder of jar's )

Eclipse, change compiler command (for JOMP)

I want to use JOMP API (equivalent to OpenMP in C) but I met some problems:
This is the code I want to run:
import jomp.runtime.*;
public class Hello
{
public static void main (String argv[])
{
int myid;
//omp parallel private(myid)
{
myid = OMP.getThreadNum();
System.out.println("Hello from " + myid);
}
}
}
It is just an hello worl but I have a problem with the compiler. Please have a quick look at this page to understand:
http://www2.epcc.ed.ac.uk/computing/research_activities/jomp/download.html
But I can't, I do not understand how it works... I can only compile it with eclipse default compiler (I guess) and then I have only one thread!
I understand I have to compile this code (in a .jomp file) with
java jomp.compiler.Jomp MyFile
and then compile normally but I can't do this in ecplise neither in the terminal (I do not know how to install this compiler!)
ps: I use Ubuntu 12.04 on a Intel® Core™ i7-3610QM CPU # 2.30GHz × 8.
You just need to add the JOMP parameters to your launch configuration, this example can help you:
JOMP eclipse workaround

Using ANTLR for static analysis of Java source file

Does anyone have a complete implementation (possibly github or googlecode) for using an ANTLR grammar file and Java source code to analyze Java source. For example, I want to simply be able to count the number of variables, method, etc.
Also using a recent version of ANTLR.
I thought I'd take a crack at this over my lunch break. This may not completely solve your problem, but it might give you a place to start. The example assumes you're doing everything in the same directory.
Download the ANTLR source from GitHub. The pre-compiled "complete" JAR from the ANTLR site contains a known bug. The GitHub repo has the fix.
Extract the ANTLR tarball.
% tar xzf antlr-antlr3-release-3.4-150-g8312471.tar.gz
Build the ANTLR "complete" JAR.
% cd antlr-antlr3-8312471
% mvn -N install
% mvn -Dmaven.test.skip=true
% mvn -Dmaven.test.skip=true package assembly:assembly
% cd -
Download a Java grammar. There are others, but I know this one works.
Compile the grammar to Java source.
% mkdir com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated
% mv *.g com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated
% java -classpath antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar org.antlr.Tool -o com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated Java.g
Compile the Java source.
% javac -classpath antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated/*.java
Add the following source file, Main.java.
import java.io.IOException;
import java.util.List;
import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import com.habelitz.jsobjectizer.unmarshaller.antlrbridge.generated.*;
public class Main {
public static void main(String... args) throws NoSuchFieldException, IllegalAccessException, IOException, RecognitionException {
JavaLexer lexer = new JavaLexer(new ANTLRFileStream(args[1], "UTF-8"));
JavaParser parser = new JavaParser(new CommonTokenStream(lexer));
CommonTree tree = (CommonTree)(parser.javaSource().getTree());
int type = ((Integer)(JavaParser.class.getDeclaredField(args[0]).get(null))).intValue();
System.out.println(count(tree, type));
}
private static int count(CommonTree tree, int type) {
int count = 0;
List children = tree.getChildren();
if (children != null) {
for (Object child : children) {
count += count((CommonTree)(child), type);
}
}
return ((tree.getType() != type) ? count : count + 1);
}
}
Compile.
% javac -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main.java
Select a type of Java source that you want to count; for example, VAR_DECLARATOR, FUNCTION_METHOD_DECL, or VOID_METHOD_DECL.
% cat com/habelitz/jsobjectizer/unmarshaller/antlrbridge/generated/Java.tokens
Run on any file, including the recently created Main.java.
% java -classpath .:antlr-antlr3-8312471/target/antlr-master-3.4.1-SNAPSHOT-completejar.jar Main VAR_DECLARATOR Main.java
6
This is imperfect, of course. If you look closely, you may have noticed that the local variable of the enhanced for statement wasn't counted. For that, you'd need to use the type FOR_EACH, rather than VAR_DECLARATOR.
You'll need a good understanding of the elements of Java source, and be able to take reasonable guesses at how those match to the definitions of this particular grammar. You also won't be able to do counts of references. Declarations are easy, but counting uses of a field, for example, requires reference resolution. Does p.C.f refer to a static field f of a class C inside a package p, or does it refer to an instance field f of the object stored by a static field C of a class p? Basic parsers don't resolve references for languages as complex as Java, because the general case can be very difficult. If you want this level of control, you'll need to use a compiler (or something closer to it). The Eclipse compiler is a popular choice.
I should also mention that you have other options besides ANTLR. JavaCC is another parser generator. The static analysis tool PMD, which uses JavaCC as its parser generator, allows you to write custom rules that could be used for the kinds of counts you indicated.

Java Command Line Trouble with Reading a Class from a Jar Archive

I am trying to run a java based tool using a command line syntax as the following: java -cp archive.jar archiveFolder.theMainClassName.Although the class I am searching for, a main class, "theMainClassName" is in the archive.jar and in the archiveFolder given at input, I keep getting the error that my class is not seen. Does anybody have any ideas concerning this problem? Thank you in advance
Here's a concrete example of what does work, so you can compare your own situation.
Take this code and put it anywhere, in a file called MainClass.java. (I've assumed a directory called src later. Normally you'd arrange the source to match the package, of course.)
package archiveFolder;
public class MainClass
{
public static void main(String[] args)
{
System.out.println("I'm MainClass");
}
}
Then run each of these commands:
# Compile the source
javac -d . src/MainClass.java
# Build the jar file
jar cf archive.jar archiveFolder
# Remove the unpackaged binary, to prove it's not being used
rm -rf archiveFolder # Or rmdir /s /q archiveFolder on Windows
# Execute the class
java -cp archive.jar achiveFolder.MainClass
The result:
I'm MainClass
How are you building your jar file? Is the code in the appropriate package?
Does theMainClassName class have the following package line at the top:
package archiveFolder
You need the class file to be in the same directory structure as the declared package. So if you had something like:
org/jc/tests/TestClass.class
its source file would have to look like this:
package org.jc.tests;
public class TestClass {
public static void main(String[] args) {
System.out.printf("This is a test class!\n");
}
}
Then you could use the following to create the jar file and run it from the command line (assuming the current directory is at the top level, just above org):
$ jar -cf testJar.jar org/jc/tests/*.class
$ java -cp testJar.jar org.jc.tests.TestClass
Perhaps with java -jar archive.jar?
Of course, it supposes the manifest points to the right class...
You should give the exact message you got, it might shed more light.
EDIT: See Working with Manifest Files: The Basics for information on setting the application entry point (Main class) in your jar manifest file.
Usually this happens when a dependent class (static member) is not found - like this, using log4j:
public class MyClass {
private static Logger log = Logger.getLogger("com.example");
}
The reason is that the initialization of such a static member can be understood as part of the class loading - errors causing the class not to be available (loadable), resulting in the error you described.
Static constructors are another possible reason:
public class MyClass {
static {
// <b>any</b> error caused here will cause the class to
// not be loaded. Demonstrating with stupid typecast.
Object o = new String();
Integer i = (Integer) o;
}
}
I think others have covered some common stuff here. I'd jar tf the jar and make sure the class is listed. I'd also double-check that the class is public and the method is "public static void main(String[] arg)".

Categories

Resources