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