jdj foss java tools

4

Click here to load reader

Upload: ganesh-samarthyam

Post on 26-Jun-2015

368 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Jdj Foss Java Tools

JDJ.SYS-CON.com10 May 2008

ny large Java source base can have insidious and subtle bugs. Every experienced Java programmer knows that fi nding and fi xing these bugs can be diffi cult and costly. Fortunately, there are a large

number of free open source Java tools available that can be used to fi nd and fi x defects early in the development lifecycle. In this article, we’ll look at a few examples of specifi c uncommon or unusual defects that can happen in code and see how different Java static analysis tools detect them.

Testing As software gets more complex and ubiquitous, it becomes more difficult to ensure high-quality code. One common method of finding bugs is testing. But testing can’t cover all paths and possibilities or enforce good programming practices. Expert knowledge in the form of manual code review by peers is one of the best ways to ensure good code quality. Code review is often used as a mandatory process step for improving the code and for finding the problems early in the software lifecycle. Since testing and manual code review processes are resource-intensive, it would be helpful to use automated tools to review code. Static analysis tools help consid-erably in detecting the problems early in the software lifecycle and help enhance the quality of the code significantly. There are many high-quality Java tools available in the open source domain. While it’s true that Java programs don’t suffer from traditional C/C++ problems like memory issues and major portability issues, Java software does suf-fer quality problems like reliability, efficiency, maintain-ability, and security. A brief discussion on benefits of using FOSS Java tools is given in the sidebar. Before getting into the meat of the matter, let’s discuss why bugs happen. First, it’s important to recognize that everyone makes mistakes, even experts. Second, compil-ers only check for syntax and semantic violations. Errors in language or API use, which manifest themselves as bugs, aren’t detected by compilers: This is left to static analysis tools and it’s important to use them to detect cod-ing problems. Third, programmers and engineers are un-der constant pressure to “get-the-work-done” under tight schedules; working under “almost-impossible-to-meet”

work schedules results in code that is often substandard and filled with bugs. So, because of practical problems, most code developed in the real world has bugs and it’s worthwhile using static analysis tools to find them and fix them. In this article we’ll see an uncommon defect and intro-duce a tool that detects it. We do this for two reasons: to illustrate the kind of unusual problems that can happen in the code and to introduce a FOSS tool that’s suitable for detecting this kind of problem.

Jlint What does this program print?

class LongVal {

public static void main(String[] s) {

long l = 0x1l;

System.out.format(“%x”, l);

}

}

When you run it, it prints 1, not 11 – why? Let’s use a

S. G. Ganesh is a research

engineer at Siemens (Corpo-

rate Technology), Bangalore.

Prior to Siemens, he worked

at Hewlett-Packard for

around fi ve years. His areas

of interest are programming

languages and compilers. His

latest book is 60 Tips on Ob-ject Oriented Programming(ISBN-13 978-0-07-065670-3)

published by Tata McGraw-

Hill, New Delhi.

[email protected]

by S.G. Ganesh

A

Detecting them with FOSS tools

FEATURE

NEED HEAD SHOT

Uncommon Java Bugs

Using Java static analysis tools can significantly improve the quality

of code. Although static analysis tools can’t cover all the paths or pos-

sibilities, it provides significant help in providing coverage in detecting

problems early in code; such tools can also point out programming prob-

lems and warn of violations of important and well-accepted program-

ming rules and recommendations.

Using static analysis tools has many attractive benefits. A few of the

salient benefits of most of these tools are listed here. Most of the Java

FOSS tools:

• Can cover code that’s not covered by testing or dynamic analysis

• Find many unusual or uncommon bugs that are usually missed

during testing or manual code review

• Work even on partial code – fully compilable source isn’t always

needed

• Easily integrate with popular IDEs, so it’s comfortable to use them in

your favorite environment

• Are usually easy-to-run –with just a button click from your IDE

• Are absolutely free and high-quality

Benefits of Using FOSS Java Static Analysis Tools

Page 2: Jdj Foss Java Tools

11May 2008JDJ.SYS-CON.com

Detecting them with FOSS tools

tool to detect the problem. The antic tool (that’s part of JLint) finds it:

$antic –java LongVal.java

LongVal.java:3:26: May be ‘l’ is used instead of ‘1’ at the end of

integer constant

The programmer, possibly by mistake, typed ‘l’ (English letter l) instead of ‘1’ (number one)!

long l = 0x1l;

To avoid this problem, it’s best to use ‘L’ (upper case letter L) as the suffix for long constants instead of ‘l’ (lower case letter l). Antic is part of the Jlint tool that’s meant to find prob-lems related to C syntax. There are quite a few coding problems that are common to languages that use C-like syntax. The problem we saw now is just one such problem. Jlint ferrets out Java inconsistencies and bugs. It’s not a very sophisticated tool and if you don’t have experience using static analysis tools, JLint is a good tool to start with. Antic works on Java source files and Jlint works on Java class file builds. It’s a command line tool and easy-to-use. It’s available at http://jlint.sourceforge.net.

FindBugs What does this program print?

class NaNTest {

public static void main(String[] s) {

double d = getVal();

if(d == Double.NaN)

System.out.println(“d is NaN”);

}

private static double getVal() {

return Double.NaN;

}

}

You might be surprised to find that it doesn’t print any-thing! What went wrong? The FindBugs tool detects the problem and warns us about it (see Figure 1). The bug is that the condition (NaN == NaN) evaluates to false! In the condition (d == Double.NaN), this code checks to see if a floating-point value is equal to the spe-cial “Not A Number” value. The IEEE 754 floating-point standard provides the special semantics of NaN: no value is equal to NaN, including NaN itself. So, the check (d == Double.NaN) always evaluates to false. The correct check to use is the condition check Double.isNaN(x). The FindBugs tool detects this problem and aptly names it “Doomed test for equality to NaN”. The FindBugs tool is excellent. It detects correctness problems, multithreading issues, performance problems, and bad practices. It has less false positives and warns of only critical or important problems that are likely to be actual defects in code. So, if you’re pressed for time and want to look at only important problems, this tool will suit you. It runs on Java class/jar files, so no Java source files are needed to use it. And it runs in a nice standalone GUI. You can download it at http://findbugs.sourceforge.net/.

PMD What’s wrong with the program in Listing 1? If you try to run it (as shown in Figure 2) you’ll get a NullPointer-Exception! What could have gone wrong? PMD detects it and warns of the problem:

$ pmd Test.java text design

Test.java:3 Overridable method ‘foo’ called during object con-

struction

The bug in this program is that the constructor of the Base class calls an overridden method. Construc-tors don’t support runtime polymorphism since derived objects aren’t constructed when the base class constructor executes. The virtual method foo is called from the base class constructor. Since foo is overridden, the overridden foo calls the toString method from I, which isn’t initial-ized yet (note that i gets initialized only after the Derivedconstructor has completed executing). Because of this, the program terminates with a NullPointerException. For this reason, it’s not a recommended programming practice to call overridable methods from constructors. The PMD tool checks for problems like possible bugs, design rule violations, duplicates, sub-optimal or dead code, suggestions for migration to newer JDK versions,

Figure 1 FindBugs detects the check-for-equality-to-NaN problem

Figure 2 Test.java program results in NullPointerException

Figure 3 The DeadLock.java program results in a “deadlock condition”

Page 3: Jdj Foss Java Tools

JDJ.SYS-CON.com12 May 2008

J2EE, JavaBeans, JSP, and JUnit rules. It works on Java source files and can be used from command lines. Plug-ins for popular IDEs like Eclipse, JBuilder, and JCreator are also available. You can download it from http://pmd.sourceforge.net/.

QJ-Pro What’s wrong with the program in Listing 2? It’s likely that the program will hang after running success-fully for few times as shown in Figure 3; in other words, this program can lead to a “deadlocked condition” (the program actually hint at this: the name of the class is Deadlock!). The QJ-Pro tool detects it as shown in Figure 4. The bug in this code is that the code acquires two locks in opposite order; and after that a sleep/wait method is called – this condition will usually result in a deadlock. Locks are the basic Java synchronization mechanism. Using locks ensures exclusive ownership for a thread while executing a critical section. Incorrect use of synchronization can lead to deadlocks. A big problem with deadlocks (as with most multithreading problems) is that deadlocks are “non-deterministic” – they need not reproduce consistently, and so it’s difficult to detect, reproduce, and fix problems related to deadlocks. Acquiring multiple locks is prone to deadlock, particularly if not done in the same order or if the sleep()/wait() in the Thread is called after acquiring locks. In this program, foo and bar acquire locks in opposite order and call sleep(). Hence deadlock occurs.

Acquiring multiple locks is not a recommended program-ming practice. However, it’s often required in practice, so when we need to acquire multiple locks, we should ensure that we acquire them in the same order in the code. Alternatively, we can consider using non-blocking locks when we attempt to acquire multiple locks. The tryLock meth-od in the java.util.concurrent.locks.Lock interface provides this ability. It’s also recommended to release locks quickly and not hold the locks for a long time; so, it’s not recommended to use sleep/wait methods after acquiring a lock; consider using the wait/notify mechanism instead to avoid deadlocks be-cause of holding a lock for a long time waiting for a condition to occur. The QJ-Pro tool checks for problems like conformance to coding standards, coding best practices, misuse of features, and APIs. It gives lots of violations by default, so you’d have to spend some time selecting the list of rules you want to run for your project. It works on Java source files and is easy-to-use in its standalone GUI version (shown in Figure 5). You can use its plug-ins with popular IDEs like Eclipse, JBuilder, JDeveloper plug-ins or Ant. You can get QJ-Pro from http://qjpro.source-forge.net/.

Other Tools Other than the four tools covered here – Jlint, FindBugs, PMD, and QJ-Pro – there are many other FOSS tools avail-able. For example, CheckStyle checks for adherence to coding standards such as Sun’s. You can get it from http://checkstyle.sourceforge.net/. JCSC (Java Coding Style Checker) checks for coding style adherence and for common bugs. You can get it at http://jcsc.sourceforge.net/. There are many more useful tools like Classycle, Condenser, DoctorJ, and JarAnalyzer. More in-formation and links on Java tools is provided in the Resource section.

Conclusion We saw four specific static analysis tools that can be used to detect not-so-common defects in code. They are free, easy-to-integrate with IDEs, and easy-to-use. It’s highly recommended to use such tools to improve the quality of the software by detecting and fixing bugs early in the software lifecycle.

Resources • If you’re interested in a list of the Java FOSS static analysis

tools available, check http://java-source.net/open-source/code-analyzers.

• “A Comparison of Bug Finding Tools for Java” by Nick Rutar, Christian B. Almazan, and Jeffrey S. Foster from the University of Maryland provides a detailed technical comparison of Bandera, ESC/Java, FindBugs, JLint and PMD tools. See http://www.cs.umd.edu/~jfoster/papers/issre04.pdf.

• If you’re using Eclipse, it’s very convenient to use Java tools as plug-ins. The list of available plug-ins for Java is at http://www.eclipseplugincentral.com/Web_Links-index-req-viewcatlink-cid-14-orderby-rating.html.

• The book Java Puzzlers: Traps, Pitfalls, and Corner Cases by Joshua Bloch and Neal Gafter covers many interesting bugs that can happen in code. Check the link http://www.javapuzzlers.com/.

FEATURE

Figure 4 QJ-Pro detects deadlock because of acquiring multiple locks

Figure 5 QJ-Pro detects deadlock because of acquiring multiple locks

Page 4: Jdj Foss Java Tools

13May 2008JDJ.SYS-CON.com

Listing 1

class Base {

public Base() {

foo();

}

public void foo() {

System.out.println(“In Base’s foo “);

}

}

class Derived extends Base {

public Derived() {

i = new Integer(10);

}

public void foo() {

System.out.println(“In Derived’s foo “

+ i.toString());

}

private Integer i;

}

class Test {

public static void main(String [] s) {

new Derived().foo();

}

}

Listing 2

class This {}

class That {}

class DoSynchronize implements Runnable {

public static void foo() throws InterruptedException {

synchronized(This.class) {

synchronized(That.class) {

Thread.currentThread().sleep(10);

}

}

}

public static void bar() throws InterruptedException {

synchronized(That.class) {

synchronized(This.class) {

Thread.currentThread().sleep(10);

}

}

}

public void run() {

try {

foo();

bar();

} catch (InterruptedException e) {

System.out.println(“Caught interrupted

exception”);

}

}

}

class DeadLock {

public static void main(String []s) {

DoSynchronize pc = new DoSynchronize();

Thread t1 = new Thread(pc);

Thread t2 = new Thread(pc);

t1.start();

t2.start();

}

}