View Javadoc

1   /***
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3   */
4   package net.sourceforge.pmd.rules;
5   
6   import net.sourceforge.pmd.AbstractRule;
7   import net.sourceforge.pmd.RuleContext;
8   import net.sourceforge.pmd.ast.ASTBlock;
9   import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
10  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
11  import net.sourceforge.pmd.ast.ASTName;
12  import net.sourceforge.pmd.ast.ASTTryStatement;
13  import net.sourceforge.pmd.ast.ASTType;
14  import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
15  import net.sourceforge.pmd.ast.Node;
16  
17  import java.util.ArrayList;
18  import java.util.Iterator;
19  import java.util.List;
20  import java.util.Vector;
21  
22  
23  /***
24   * Makes sure you close your database connections. It does this by
25   * looking for code patterned like this:
26   * <pre>
27   *  Connection c = X;
28   *  try {
29   *   // do stuff, and maybe catch something
30   *  } finally {
31   *   c.close();
32   *  }
33   * </pre>
34   */
35  public class CloseConnectionRule extends AbstractRule {
36    public Object visit(ASTMethodDeclaration node, Object data) {
37        List vars = node.findChildrenOfType(ASTLocalVariableDeclaration.class);
38        List ids = new Vector();
39  
40        // find all variable references to Connection objects
41        for (Iterator it = vars.iterator(); it.hasNext();) {
42          ASTLocalVariableDeclaration var = (ASTLocalVariableDeclaration) it.next();
43          ASTType type = (ASTType) var.jjtGetChild(0);
44  
45          if (type.jjtGetChild(0) instanceof ASTName && ((ASTName) type.jjtGetChild(0)).getImage().equals("Connection")) {
46              ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) var.jjtGetChild(1).jjtGetChild(0);
47              ids.add(id);
48          }
49        }
50  
51        // if there are connections, ensure each is closed.
52        for (int i = 0; i < ids.size(); i++) {
53          ASTVariableDeclaratorId x = (ASTVariableDeclaratorId) ids.get(i);
54          ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent()
55                                                      .jjtGetParent(), x, data);
56        }
57        return data;
58    }
59  
60    private void ensureClosed(ASTLocalVariableDeclaration var,
61      ASTVariableDeclaratorId id, Object data) {
62      // What are the chances of a Connection being instantiated in a
63      // for-loop init block? Anyway, I'm lazy!    
64        String target = id.getImage() + ".close";
65        Node n = var;
66  
67        while (!((n = n.jjtGetParent()) instanceof ASTBlock))
68          ;
69  
70        ASTBlock top = (ASTBlock) n;
71  
72        List tryblocks = new Vector();
73        top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
74  
75        boolean closed = false;
76  
77        // look for try blocks below the line the variable was
78        // introduced and make sure there is a .close call in a finally
79        // block.
80        for (Iterator it = tryblocks.iterator(); it.hasNext();) {
81          ASTTryStatement t = (ASTTryStatement) it.next();
82  
83          if ((t.getBeginLine() > id.getBeginLine()) && (t.hasFinally())) {
84            ASTBlock f = t.getFinallyBlock();
85            List names = new ArrayList();
86            f.findChildrenOfType(ASTName.class, names, true);
87            for (Iterator it2 = names.iterator(); it2.hasNext();) {
88                if (((ASTName) it2.next()).getImage().equals(target)) {
89                closed = true;
90              }
91            }
92          }
93        }
94  
95        // if all is not well, complain
96        if (!closed) {
97          RuleContext ctx = (RuleContext) data;
98          ctx.getReport().addRuleViolation(createRuleViolation(ctx, id.getBeginLine(), getMessage()));
99        }
100   }
101 }