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