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 }