Мы создали специальную проверку Checkstyle, которая предотвращает утечки этих операторов.Код ниже.Прелесть checkstyle в том, что вы можете настроить свои чеки, используя API , который предоставляет Java AST.Мы создали десятки пользовательских проверок.Как только вы получите суть, создать новые чеки очень просто.Мы также создали хук предварительной фиксации Subversion, который запускает проверку и предотвращает попадание кода с нарушениями в хранилище.Разработчик получает четкое сообщение (см. Вызов журнала 'ниже), указывающее проблему и строку.
import java.util.List;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
/**
* Code blocks that invoke Connection.prepareStatement(...), Connection.prepareCall(...) or
* Connection.createStatement(...) must have a finally block
* in which there is a call to ContextObject.closeStatement(Statement).
*/
public class CheckCloseStatement extends CheckTcu {
@Override
public int[] getDefaultTokens() {
return new int[] {TokenTypes.ASSIGN};
}
@Override
public void visitToken(DetailAST aAST) {
DetailAST literalTry;
DetailAST literalFinally;
DetailAST paramCloseStmt;
List<DetailAST> idents;
List<DetailAST> identsInFinally;
String stmtVarName;
idents = findAllAstsOfType(aAST, TokenTypes.IDENT);
for (DetailAST ident : idents) {
if ((ident.getText().equals("prepareStatement") || ident.getText().equals("createStatement") ||
ident.getText().equals("prepareCall")) && ident.getParent().getType() == TokenTypes.DOT) {
// a Statement is created in this assignment statement
boolean violationFound = true;
// look for the surrounding try statement
literalTry = ident;
do {
literalTry = literalTry.getParent();
} while (literalTry != null && literalTry.getType() != TokenTypes.LITERAL_TRY);
if (literalTry != null) {
// good, the Statement creating assignment is within a try block
// now look for the corresponding finally block
literalFinally = literalTry.findFirstToken(TokenTypes.LITERAL_FINALLY);
if (literalFinally != null) {
// good, there is a finally block
identsInFinally = findAllAstsOfType(literalFinally, TokenTypes.IDENT);
for (DetailAST identInFinally : identsInFinally) {
if (identInFinally.getText().equals("closeStatement")) {
// good, there's a call to my closeStatement method
paramCloseStmt =
findFirstAstOfType(identInFinally.getParent().getNextSibling(), TokenTypes.IDENT);
stmtVarName = findFirstAstOfType(aAST, TokenTypes.IDENT).getText();
if (stmtVarName.equals(paramCloseStmt.getText())) {
// great, closeStatement closes the Statement variable originally assigned
violationFound = false;
break;
}
}
}
}
}
// Exception: this rule does not apply to Xyz and its subclasses (which have
// the same name Xyz followed by a suffix)
if (violationFound) {
DetailAST classDef = aAST;
do {
classDef = classDef.getParent();
} while (classDef != null && classDef.getType() != TokenTypes.CLASS_DEF);
if (classDef != null) {
String className = classDef.findFirstToken(TokenTypes.IDENT).getText();
if (className.startsWith("Xyz")) {
violationFound = false;
}
}
}
if (violationFound) {
log(ident.getLineNo(),
"Code blocks that call Connection.prepareStatement(...) or Connection.prepareCall(...) " +
"need a finally block where you should call ContextObject.closeStatement(Statement).");
}
}
}
}
}
Эта пользовательская проверка расширяет абстрактный класс, который содержит два служебных метода, показанных ниже.
import java.util.ArrayList;
import java.util.List;
import com.puppycrawl.tools.checkstyle.api.Check;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
/**
* Utility methods used in custom checks.
*/
public abstract class CheckTcu extends Check {
/**
* Recursively traverse an expression tree and return all ASTs matching a specific token type.
*
* @return list of DetailAST objects found; returns empty List if none is found.
*/
protected List<DetailAST> findAllAstsOfType(DetailAST parent, int type) {
List<DetailAST> children = new ArrayList<DetailAST>();
DetailAST child = parent.getFirstChild();
while (child != null) {
if (child.getType() == type) {
children.add(child);
} else {
children.addAll(findAllAstsOfType(child, type));
}
child = child.getNextSibling();
}
return children;
}
/**
* Recursively traverse an expression tree and return the first AST matching a specific token type.
*
* @return first DetailAST found or null if no AST of the given type is found
*/
protected DetailAST findFirstAstOfType(DetailAST parent, int type) {
DetailAST firstAst = null;
DetailAST child = parent.getFirstChild();
while (child != null) {
if (child.getType() == type) {
firstAst = child;
break;
}
DetailAST grandChild = findFirstAstOfType(child, type);
if (grandChild != null) {
firstAst = grandChild;
break;
}
child = child.getNextSibling();
}
return firstAst;
}
}