001: /*
002: * Hammurapi
003: * Automated Java code review system.
004: * Copyright (C) 2004 Johannes Bellert
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.org
021: * e-Mail: CraftOfObjects@gmail.com
022:
023: */
024:
025: package org.hammurapi.inspectors;
026:
027: import java.util.Collection;
028: import java.util.Iterator;
029: import java.util.List;
030: import java.util.Vector;
031:
032: import org.hammurapi.InspectorBase;
033:
034: import com.pavelvlasov.config.ConfigurationException;
035: import com.pavelvlasov.config.Parameterizable;
036: import com.pavelvlasov.jsel.Code;
037: import com.pavelvlasov.jsel.CompilationUnit;
038: import com.pavelvlasov.jsel.JselException;
039: import com.pavelvlasov.jsel.LanguageElement;
040: import com.pavelvlasov.jsel.Method;
041: import com.pavelvlasov.jsel.Operation;
042: import com.pavelvlasov.jsel.OperationInfo;
043: import com.pavelvlasov.jsel.Repository;
044: import com.pavelvlasov.jsel.VariableDefinition;
045: import com.pavelvlasov.jsel.expressions.Expression;
046: import com.pavelvlasov.jsel.expressions.Ident;
047: import com.pavelvlasov.jsel.expressions.MethodCall;
048: import com.pavelvlasov.jsel.expressions.PlainAssignment;
049: import com.pavelvlasov.jsel.expressions.TypeCast;
050: import com.pavelvlasov.jsel.statements.ReturnStatement;
051: import com.pavelvlasov.review.SourceMarker;
052: import com.pavelvlasov.util.Visitor;
053:
054: /**
055: * Fix 5: Ignore all createStatement & preparedStatements method calls which are NOT provided by java.sql.Connection
056: * Fix 4: handle TypeCast of parameters in "releaseSafe" helper methods
057: * Fix 3: find multiple occurance of a "releaseSafe" helper methods
058: * Fix 2: Count create & close statements in a method and report violation if close less than create
059: * Fix 1: ReturnStatement added in visit(MethodCall)
060: */
061: public class SqlCreateStatementWithoutCloseRule extends InspectorBase
062: implements Parameterizable {
063:
064: private java.util.Set releaseResourceMethodList = new java.util.HashSet();
065:
066: boolean ret;
067:
068: //-- State Engine
069: private String currentStatementVarDef = "";
070:
071: private Vector createStatementList = new Vector();
072: private Vector closeStatementList = new Vector();
073: private SourceMarker compilationUnitSourcingMarker = null;
074: private SourceMarker methodSourcingMarker = null;
075:
076: public void visit(CompilationUnit cu) {
077: compilationUnitSourcingMarker = (SourceMarker) cu;
078: }
079:
080: public void visit(Method methodDecl) {
081:
082: checkNumberOfCreateCloseAPI(); // initalize State Engine
083:
084: //--- re-initialize
085: createStatementList = new Vector();
086: closeStatementList = new Vector();
087: methodSourcingMarker = (SourceMarker) methodDecl;
088: }
089:
090: public void visit(MethodCall methodCall) {
091:
092: //System.out.println( methodCall.getMethodName() );
093:
094: checkForReleaseSafeMethod(methodCall);
095: checkForCloseMethod(methodCall);
096: checkForCreateMethod(methodCall);
097: }
098:
099: private void checkForReleaseSafeMethod(MethodCall methodCall) {
100: boolean ret = false;
101: try {
102: int i = 0;
103: Object[] releaseResourceMethodStrings = releaseResourceMethodList
104: .toArray();
105: while (!ret && i < releaseResourceMethodStrings.length) {
106:
107: String releaseResourceMethodName = (String) releaseResourceMethodStrings[i];
108: i++;
109: // System.out.println(releaseResourceMethodName +" -- " + methodCall.getMethodName() );
110: if (releaseResourceMethodName.equals(methodCall
111: .getMethodName())) {
112: //-- search for parameters
113: List parameterList = methodCall.getParameters();
114: Iterator pit = parameterList.iterator();
115: while (pit.hasNext() && !ret) {
116: Object parameter = pit.next();
117:
118: //!! refactoring: code doublet with method CloseVisitor
119: if (parameter instanceof TypeCast) {
120:
121: if ("Statement"
122: .equals(((TypeCast) parameter)
123: .getTypeSpecification()
124: .toString())
125: || "PreparedStatement"
126: .equals(((TypeCast) parameter)
127: .getTypeSpecification()
128: .toString())) {
129:
130: methodSourcingMarker = (SourceMarker) methodCall;
131: closeStatementList.add(methodCall);
132: ret = true;
133: }
134:
135: } else if (parameter instanceof Ident) {
136: Ident p = (Ident) parameter;
137: String paramName = p.toString();
138: String paramTypDef = p
139: .getTypeSpecification().getName();
140: // System.out.println( "paramName " + paramName );
141: // System.out.println( "paramTypDef " + paramTypDef );
142: // System.out.println( "p.getTypeSpecification " + p.getTypeSpecification().getName() );
143: if (("java.sql.Statement"
144: .equals(paramTypDef)
145: // || "java.sql.PreparedStatement".equals(p.getTypeSpecification().getName()))
146: || "java.sql.PreparedStatement"
147: .equals(paramTypDef))
148: // && paramName.equals(currentStatementVarDef)
149: ) {
150:
151: methodSourcingMarker = (SourceMarker) methodCall;
152: closeStatementList.add(methodCall);
153: ret = true;
154: }
155: }
156: }
157: }
158: }
159: } catch (JselException e1) {
160: // TODO Auto-generated catch block
161: e1.printStackTrace();
162: }
163: }
164:
165: private void checkForCloseMethod(MethodCall methodCall) {
166: if ("close".equals(methodCall.getMethodName())) {
167:
168: try {
169: OperationInfo opi = methodCall.getProvider();
170:
171: if ("java.sql.Statement".equals(opi.getDeclaringType()
172: .getName())) {
173: methodSourcingMarker = (SourceMarker) methodCall;
174: this .closeStatementList.add(methodCall);
175: }
176:
177: } catch (Exception e) {
178: e.printStackTrace();
179: }
180:
181: //!! no provider type check ??
182: }
183: }
184:
185: private void checkForCreateMethod(MethodCall methodCall) {
186: Code code = ((LanguageElement) methodCall).getEnclosingCode();
187: LanguageElement parentLangElem = ((LanguageElement) methodCall)
188: .getParent();
189:
190: if ("createStatement".equals(methodCall.getMethodName())
191: || "prepareStatement"
192: .equals(methodCall.getMethodName())) {
193:
194: try {
195:
196: //!! Ugly Quick Hack
197: if (parentLangElem instanceof VariableDefinition) {
198: VariableDefinition varDef = (VariableDefinition) parentLangElem;
199: currentStatementVarDef = varDef.getName();
200: } else if (parentLangElem instanceof Ident) {
201: Ident id = (Ident) parentLangElem;
202: final Object provider = id.getProvider();
203: if (provider != null
204: && (provider instanceof VariableDefinition)) {
205: currentStatementVarDef = ((VariableDefinition) provider)
206: .getName();
207: }
208: } else if (parentLangElem instanceof TypeCast) {
209: VariableDefinition grandpaLangElem = (VariableDefinition) ((LanguageElement) parentLangElem)
210: .getParent();
211:
212: currentStatementVarDef = grandpaLangElem.getName();
213:
214: } else if (parentLangElem instanceof PlainAssignment) {
215: PlainAssignment pa = (PlainAssignment) parentLangElem;
216:
217: Collection lt = pa.getOperands();
218: if (!lt.isEmpty()) {
219: Iterator it = lt.iterator();
220: String str = ((Ident) it.next()).getText();
221: currentStatementVarDef = str;
222: } else {
223: //!!
224: context.warn(parentLangElem,
225: "!!!!!!!!! currentStatementVarDef could not be determined for "
226: + parentLangElem);
227: }
228: }
229:
230: else if (parentLangElem instanceof ReturnStatement) {
231: // Fix 1
232: ReturnStatement pa = (ReturnStatement) parentLangElem;
233: Expression ex = pa.getExpression();
234: if (ex instanceof MethodCall) {
235:
236: //--- this is not the Statement name but we can use it
237: // instead of a "NA" or null value.
238: currentStatementVarDef = ((LanguageElement) ex)
239: .getAst().getFirstToken().getText();
240:
241: //!! only method calls referenced to a var are handled
242: // .. recurively resolution of nested method calls
243: // needed.
244: // currentStatementVarDef =
245: // methodCallX.getTypeSpecification().getName().toString();
246: }
247:
248: } else {
249: context.warn((SourceMarker) methodCall,
250: " methodCall has no varDef ");
251: }
252:
253: // System.out.println(" currentStatementVarDef " +
254: // currentStatementVarDef);
255: if (code != null) {
256: Operation op = (Operation) code;
257: OperationInfo opi = methodCall.getProvider();
258: try {
259: if ("java.sql.Connection".equals(opi
260: .getDeclaringType().getName())) {
261:
262: createStatementList.add(methodCall);
263: //--reinit
264: ret = false;
265: op.accept(new CloseVisitor());
266: if (!ret) {
267: code.accept(new CloseVisitor());
268: }
269:
270: if (!ret) {
271: context
272: .reportViolation((SourceMarker) methodCall);
273: }
274: }
275: } catch (JselException e) {
276: context.warn(op, e);
277: }
278:
279: }
280: } catch (JselException e) {
281: context.warn((SourceMarker) methodCall, e);
282: }
283: }
284: }
285:
286: public void checkNumberOfCreateCloseAPI() {
287: if (closeStatementList.size() < createStatementList.size()) {
288: StringBuffer str = new StringBuffer();
289: str.append("Found " + createStatementList.size());
290: str.append(" calls of create Statements but only ");
291: str.append(closeStatementList.size());
292: str.append(" close() calls");
293:
294: // context.reportViolation( this.methodSourcingMarker , str.toString() );
295: context.getSession().getContext("ER-209").reportViolation(
296: this .methodSourcingMarker, str.toString());
297:
298: }
299: }
300:
301: public void leave(Repository repo) {
302: this .checkNumberOfCreateCloseAPI();
303: }
304:
305: class CloseVisitor implements Visitor {
306:
307: /**
308: * Demonstrates usage of VisitorStack
309: */
310: /*
311: * public void visit(MethodCall mc) throws JselException { if
312: * (VisitorStack.getThreadInstance().isIn(ForStatement.class)) {
313: * context.reportViolation((SourceMarker) mc, "Method call from for
314: * loop"); } }
315: */
316: public boolean visit(Object target) {
317: try {
318: if (target instanceof MethodCall) {
319: MethodCall subMethCall = (MethodCall) target;
320:
321: //-- check for close
322: if ("close".equals(subMethCall.getMethodName())) {
323: OperationInfo opi = subMethCall.getProvider();
324:
325: if ("java.sql.Statement".equals(opi
326: .getDeclaringType().getName())
327: || "java.sql.PreparedStatement"
328: .equals(opi.getDeclaringType()
329: .getName())) {
330:
331: Collection lt = ((MethodCall) target)
332: .getName().getOperands();
333: Iterator it = lt.iterator();
334:
335: String str = ((Ident) it.next()).getText();
336: if (!lt.isEmpty()
337: && str != null
338: && str
339: .equals(currentStatementVarDef)) {
340: ret = true;
341: }
342: }
343: } else {
344:
345: int i = 0;
346: Object[] releaseResourceMethodStrings = releaseResourceMethodList
347: .toArray();
348: while (!ret
349: && i < releaseResourceMethodStrings.length) {
350:
351: String releaseResourceMethodName = (String) releaseResourceMethodStrings[i];
352: i++;
353: // System.out.println( " check for " + releaseResourceMethodName.toString());
354: if (releaseResourceMethodName
355: .equals(subMethCall.getMethodName())) {
356:
357: //-- search for parameters
358: List parameterList = subMethCall
359: .getParameters();
360: Iterator pit = parameterList.iterator();
361: while (pit.hasNext() && !ret) {
362: Object parameter = pit.next();
363:
364: //!! refactoring: code doublet with method checkForReleaseSafeMethod
365: if (parameter instanceof TypeCast) {
366: context
367: .debug(
368: (SourceMarker) parameter,
369: "((TypeCast)parameter).getTypeSpecification().toString() "
370: + ((TypeCast) parameter)
371: .getTypeSpecification()
372: .toString());
373: if ("Statement"
374: .equals(((TypeCast) parameter)
375: .getTypeSpecification()
376: .toString())
377: || "PreparedStatement"
378: .equals(((TypeCast) parameter)
379: .getTypeSpecification()
380: .toString())) {
381:
382: methodSourcingMarker = (SourceMarker) target;
383: ret = true;
384: }
385:
386: } else if (parameter instanceof Ident) {
387: Ident p = (Ident) parameter;
388: String paramName = p.toString();
389: String paramTypDef = p
390: .getTypeSpecification()
391: .getName();
392: if (("java.sql.Statement"
393: .equals(paramTypDef) || "java.sql.PreparedStatement"
394: .equals(paramTypDef))
395: && paramName
396: .equals(currentStatementVarDef)) {
397:
398: methodSourcingMarker = (SourceMarker) target;
399: // closeStatementList.add( target );
400: ret = true;
401: }
402: }
403:
404: }
405: }
406: }
407:
408: }
409: }
410: } catch (JselException e) {
411: context.warn((SourceMarker) target, e);
412: }
413: return true;
414: }
415: }
416:
417: /**
418: * Configures the rule. Reads in the values of the parameter copyright.
419: *
420: * @param name
421: * the name of the parameter being loaded from Hammurapi
422: * configuration
423: * @exception ConfigurationException
424: * in case of a not supported parameter
425: */
426: public boolean setParameter(String name, Object parameter)
427: throws ConfigurationException {
428: if ("release-resource-method".equals(name)) {
429: String s = parameter.toString();
430: releaseResourceMethodList.add(s);
431: return true;
432: }
433:
434: throw new ConfigurationException("Parameter '" + name
435: + "' is not supported by " + getClass().getName());
436: }
437:
438: /**
439: * Gives back the preconfigured values.
440: */
441: public String getConfigInfo() {
442: StringBuffer ret = new StringBuffer(
443: "Configured releaseResource method names:\n");
444: Iterator it = releaseResourceMethodList.iterator();
445: while (it.hasNext()) {
446: ret.append(" " + it.next() + "\n");
447: }
448: return ret.toString();
449: }
450:
451: }
|