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.ASTClassOrInterfaceType;
10  import net.sourceforge.pmd.ast.ASTCompilationUnit;
11  import net.sourceforge.pmd.ast.ASTImportDeclaration;
12  import net.sourceforge.pmd.ast.ASTLocalVariableDeclaration;
13  import net.sourceforge.pmd.ast.ASTMethodDeclaration;
14  import net.sourceforge.pmd.ast.ASTName;
15  import net.sourceforge.pmd.ast.ASTReferenceType;
16  import net.sourceforge.pmd.ast.ASTTryStatement;
17  import net.sourceforge.pmd.ast.ASTType;
18  import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
19  import net.sourceforge.pmd.ast.Node;
20  
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Vector;
25  
26  
27  /***
28   * Makes sure you close your database connections. It does this by
29   * looking for code patterned like this:
30   * <pre>
31   *  Connection c = X;
32   *  try {
33   *   // do stuff, and maybe catch something
34   *  } finally {
35   *   c.close();
36   *  }
37   * </pre>
38   */
39  public class CloseConnection extends AbstractRule {
40  
41      public Object visit(ASTCompilationUnit node, Object data) {
42          if (!importsJavaSqlPackage(node)) {
43              return data;
44          }
45          return super.visit(node, data);
46      }
47  
48      public Object visit(ASTMethodDeclaration node, Object data) {
49          List vars = node.findChildrenOfType(ASTLocalVariableDeclaration.class);
50          List ids = new Vector();
51  
52          // find all variable references to Connection objects
53          for (Iterator it = vars.iterator(); it.hasNext();) {
54              ASTLocalVariableDeclaration var = (ASTLocalVariableDeclaration) it.next();
55              ASTType type = (ASTType) var.jjtGetChild(0);
56  
57              if (type.jjtGetChild(0) instanceof ASTReferenceType) {
58                  ASTReferenceType ref = (ASTReferenceType)type.jjtGetChild(0);
59                  if (ref.jjtGetChild(0) instanceof ASTClassOrInterfaceType) {
60                      ASTClassOrInterfaceType clazz = (ASTClassOrInterfaceType)ref.jjtGetChild(0);
61                      if (clazz.getImage().equals("Connection")) {
62                          ASTVariableDeclaratorId id = (ASTVariableDeclaratorId) var.jjtGetChild(1).jjtGetChild(0);
63                          ids.add(id);
64                      }
65                  }
66              }
67          }
68  
69          // if there are connections, ensure each is closed.
70          for (int i = 0; i < ids.size(); i++) {
71              ASTVariableDeclaratorId x = (ASTVariableDeclaratorId) ids.get(i);
72              ensureClosed((ASTLocalVariableDeclaration) x.jjtGetParent()
73                      .jjtGetParent(), x, data);
74          }
75          return data;
76      }
77  
78      private void ensureClosed(ASTLocalVariableDeclaration var,
79                                ASTVariableDeclaratorId id, Object data) {
80          // What are the chances of a Connection being instantiated in a
81          // for-loop init block? Anyway, I'm lazy!
82          String target = id.getImage() + ".close";
83          Node n = var;
84  
85          while (!((n = n.jjtGetParent()) instanceof ASTBlock)) {}
86  
87          ASTBlock top = (ASTBlock) n;
88  
89          List tryblocks = new Vector();
90          top.findChildrenOfType(ASTTryStatement.class, tryblocks, true);
91  
92          boolean closed = false;
93  
94          // look for try blocks below the line the variable was
95          // introduced and make sure there is a .close call in a finally
96          // block.
97          for (Iterator it = tryblocks.iterator(); it.hasNext();) {
98              ASTTryStatement t = (ASTTryStatement) it.next();
99  
100             if ((t.getBeginLine() > id.getBeginLine()) && (t.hasFinally())) {
101                 ASTBlock f = t.getFinallyBlock();
102                 List names = new ArrayList();
103                 f.findChildrenOfType(ASTName.class, names, true);
104                 for (Iterator it2 = names.iterator(); it2.hasNext();) {
105                     if (((ASTName) it2.next()).getImage().equals(target)) {
106                         closed = true;
107                     }
108                 }
109             }
110         }
111 
112         // if all is not well, complain
113         if (!closed) {
114             RuleContext ctx = (RuleContext) data;
115             ctx.getReport().addRuleViolation(createRuleViolation(ctx, id, getMessage()));
116         }
117     }
118 
119     private boolean importsJavaSqlPackage(ASTCompilationUnit node) {
120         List nodes = node.findChildrenOfType(ASTImportDeclaration.class);
121         boolean ok = false;
122         for (Iterator i = nodes.iterator(); i.hasNext();) {
123             ASTImportDeclaration n = (ASTImportDeclaration)i.next();
124             if (n.getImportedNameNode().getImage().startsWith("java.sql")) {
125                 ok = true;
126                 break;
127             }
128         }
129         return ok;
130     }
131 
132 }