KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hammurapi > inspectors > ResourceHandlingRule


1 /*
2  * Hammurapi
3  * Automated Java code review system.
4  * Copyright (C) 2004 Hammurapi Group
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  * URL: http://www.hammurapi.org
21  * e-Mail: support@hammurapi.biz
22  */

23 package org.hammurapi.inspectors;
24
25 import java.text.MessageFormat JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
33
34 import org.hammurapi.InspectorBase;
35
36 import com.pavelvlasov.config.ConfigurationException;
37 import com.pavelvlasov.config.Parameterizable;
38 import com.pavelvlasov.jsel.Initializer;
39 import com.pavelvlasov.jsel.JselException;
40 import com.pavelvlasov.jsel.LanguageElement;
41 import com.pavelvlasov.jsel.VariableDefinition;
42 import com.pavelvlasov.jsel.expressions.MethodCall;
43 import com.pavelvlasov.jsel.expressions.PlainAssignment;
44 import com.pavelvlasov.jsel.statements.CompoundStatement;
45 import com.pavelvlasov.jsel.statements.Statement;
46 import com.pavelvlasov.jsel.statements.TryBlock;
47
48 /**
49  * The rule checks if allocated resource is properly diposed.
50  * It follows try/finally pattern.
51  * It is configurable via parameters.
52  * Example configuration:
53  * <pre>
54  * <parameter type="java.lang.String" name="resource-def">javax.sql.DataSource:getConnection:java.sql.Connection:close</parameter>
55  * </pre>
56  * Where:
57  * - "javax.sql.DataSource" is a full class name for the resource allocator
58  * - "getConnection" is a method name of the class that allocates resource
59  * - "java.sql.Connection" is a full class name for the resource DE-allocator
60  * - "close" is a method name of the class that DE-allocates resource
61  *
62  * The following code will be checked by the paramemeter above:
63  * <pre>
64  * javax.sql.DataSource ds;
65  * // connection is allocated here...
66  * java.sql.Connection conn = ds.getConnection();
67  * ...
68  * // connection is "returned" back to data source...
69  * conn.close();
70  * </pre>
71  * @author zorror8080
72  * @version $Revision: 1.9 $
73  */

74 public class ResourceHandlingRule
75   extends InspectorBase
76   implements Parameterizable
77 {
78   private static final String JavaDoc TRY_FIN_SHOULD_FOLLOW =
79     "try/finally block should follow right after allocated resource \"{0}"
80       + "\" to ensure that it is properly deallocated.";
81   private static final String JavaDoc FIN_DEALLOC =
82     "Allocated resource \"{0}\" should be de-allocated in 'finaly' clause.";
83   private static final String JavaDoc FIN_DEALLOC_FIRST =
84     "To ensure that resource \"{0}\" is properly deallocated - it should be the first in 'finaly' clause.";
85
86   private Map JavaDoc resources2Check;
87   private Set JavaDoc violatedElements;
88
89   public ResourceHandlingRule()
90   {
91     resources2Check = new HashMap JavaDoc();
92     violatedElements = new HashSet JavaDoc();
93   }
94
95   private void reportViolation(final LanguageElement element, final String JavaDoc msg)
96   {
97     if (!violatedElements.contains(element.getLocation()))
98     {
99       violatedElements.add(element.getLocation());
100       context.reportViolation(element, msg);
101     }
102   }
103
104   private ResInfo checkAllocation(
105     final LanguageElement codeFragment,
106     final MethodCall mCall)
107     throws JselException
108   {
109     final String JavaDoc methodName = mCall.getMethodName();
110     ResInfo res = null;
111
112     if (resources2Check.containsKey(methodName))
113     {
114       final ResourceDefinition resDef =
115         (ResourceDefinition)resources2Check.get(methodName);
116
117       if (resDef
118         .obtainClassName
119         .equals(mCall.getProvider().getDeclaringType().getName()))
120       {
121         res = new ResInfo(methodName, codeFragment);
122       }
123     }
124     return res;
125   }
126
127   private boolean checkDeAllocation(
128     final ResInfo resInfo,
129     int index,
130     List JavaDoc stms)
131     throws JselException
132   {
133     boolean res = true;
134     Statement statement = (Statement)stms.get(index);
135     ResourceDefinition resDef =
136       (ResourceDefinition)resources2Check.get(resInfo.resourceAllocatedKey);
137     // first check if TryFinally follows right after allocation...
138
CompoundStatement fSt = null;
139     if (statement instanceof TryBlock
140       && (fSt = ((TryBlock)statement).getFinallyClause()) != null)
141     {
142       // = ((TryBlock)statement).getFinallyClause();
143

144       List JavaDoc fSts = fSt.getStatements();
145       boolean foundDeAllocation = false;
146       boolean firstDeAllocationOperation = false;
147       MethodCall releaseMethodCall = null;
148       for (int jIndex = 0, jSize = fSts.size(); jIndex < jSize; jIndex++)
149       {
150         if (fSts.get(jIndex) instanceof CompoundStatement)
151         {
152           List JavaDoc ffSts = ((CompoundStatement)fSts.get(jIndex)).getStatements();
153           for (int kIndex = 0, kSize = ffSts.size(); kIndex < kSize; kIndex++)
154           {
155             if (ffSts.get(kIndex) instanceof MethodCall
156               && resDef.releaseMethodName.equals(
157                 ((MethodCall)ffSts.get(kIndex)).getMethodName())
158               && resDef.releaseClassName.equals(
159                 ((MethodCall)ffSts.get(kIndex))
160                   .getProvider()
161                   .getDeclaringType()
162                   .getName()))
163             {
164               foundDeAllocation = true;
165               firstDeAllocationOperation = (kIndex == 0);
166               releaseMethodCall = (MethodCall)ffSts.get(kIndex);
167               break;
168             }
169           }
170         }
171         if (!foundDeAllocation)
172         {
173           reportViolation(
174             resInfo.resourceAllocatedBy,
175             MessageFormat.format(
176               FIN_DEALLOC,
177               new Object JavaDoc[] { resInfo.resourceAllocatedKey }));
178         }
179         else
180         {
181           if (!firstDeAllocationOperation)
182           {
183             reportViolation(
184               (LanguageElement)releaseMethodCall,
185               MessageFormat.format(
186                 FIN_DEALLOC_FIRST,
187                 new Object JavaDoc[] {
188                   resDef.releaseClassName,
189                   resDef.releaseMethodName }));
190           }
191         }
192       }
193     }
194     else
195     {
196       if (index < stms.size() - 1)
197       {
198         checkDeAllocation(resInfo, index + 1, stms);
199       }
200       reportViolation(
201         resInfo.resourceAllocatedBy,
202         MessageFormat.format(
203           TRY_FIN_SHOULD_FOLLOW,
204           new Object JavaDoc[] { resInfo.resourceAllocatedKey }));
205     }
206     return res;
207   }
208
209   public boolean setParameter(String JavaDoc name, Object JavaDoc value)
210     throws ConfigurationException
211   {
212     if ("resource-def".equals(name))
213     {
214       StringTokenizer JavaDoc tk = new StringTokenizer JavaDoc((String JavaDoc)value, ":");
215
216       String JavaDoc obtainClassName = null;
217       String JavaDoc obtainMethodName = null;
218       String JavaDoc releaseClassName = null;
219       String JavaDoc releaseMethodName = null;
220
221       if (tk.hasMoreTokens())
222       {
223         obtainClassName = tk.nextToken();
224       }
225       if (tk.hasMoreTokens())
226       {
227         obtainMethodName = tk.nextToken();
228       }
229       if (tk.hasMoreTokens())
230       {
231         releaseClassName = tk.nextToken();
232       }
233       if (tk.hasMoreTokens())
234       {
235         releaseMethodName = tk.nextToken();
236       }
237       if (tk.hasMoreTokens()
238         || obtainClassName == null
239         || obtainMethodName == null
240         || releaseClassName == null
241         || releaseMethodName == null)
242       {
243         throw new ConfigurationException(
244           "Parameter '" + name + "' has invalid value: '" + value + "'");
245       }
246
247       resources2Check.put(
248         obtainMethodName,
249         new ResourceDefinition(
250           obtainClassName,
251           obtainMethodName,
252           releaseClassName,
253           releaseMethodName));
254     }
255     else
256     {
257       throw new ConfigurationException(
258         "Parameter '" + name + "' is not supported by " + getClass().getName());
259     }
260     return true;
261  }
262
263   public void visit(CompoundStatement stmt) throws JselException
264   {
265     ResourceDefinition resDef;
266     ResInfo resInfo = null;
267     Statement currentStatement = null;
268
269     List JavaDoc stmts = stmt.getStatements();
270
271     for (int index = 0, size = stmts.size(); index < size; index++)
272     {
273       currentStatement = (Statement)stmts.get(index);
274       if (resInfo != null)
275       {
276         checkDeAllocation(resInfo, index, stmts);
277         resInfo = null;
278       }
279       if (currentStatement instanceof VariableDefinition)
280       {
281         Initializer init =
282           ((VariableDefinition)currentStatement).getInitializer();
283
284         if (init instanceof MethodCall)
285         {
286           resInfo =
287             checkAllocation(
288               (VariableDefinition)currentStatement,
289               (MethodCall) ((VariableDefinition)currentStatement)
290                 .getInitializer());
291         }
292       }
293       else
294       {
295         if (currentStatement instanceof PlainAssignment)
296         {
297           PlainAssignment plainAssignment = (PlainAssignment)currentStatement;
298         Collection JavaDoc operands = (plainAssignment).getOperands();
299           for (int jIndex = 0, jSize = operands.size();
300             jIndex < jSize;
301             jIndex++)
302           {
303             if (plainAssignment.getOperand(jIndex) instanceof MethodCall)
304             {
305               resInfo =
306                 checkAllocation(
307                   (LanguageElement)currentStatement,
308                   (MethodCall)plainAssignment.getOperand(jIndex));
309               if (resInfo != null)
310               {
311                 break;
312               }
313             }
314           }
315         }
316         else
317         {
318           if (currentStatement instanceof MethodCall)
319           {
320             resInfo =
321               checkAllocation(
322                 (LanguageElement)currentStatement,
323                 (MethodCall)currentStatement);
324           }
325         }
326       }
327     }
328     if (resInfo != null)
329     {
330       checkDeAllocation(resInfo, stmts.size() - 1, stmts);
331     }
332   }
333
334   private static final class ResourceDefinition
335   {
336     private String JavaDoc obtainClassName;
337     private String JavaDoc obtainMethodName;
338     private String JavaDoc releaseClassName;
339     private String JavaDoc releaseMethodName;
340
341     private ResourceDefinition(
342       String JavaDoc obtainClassName,
343       String JavaDoc obtainMethodName,
344       String JavaDoc releaseClassName,
345       String JavaDoc releaseMethodName)
346     {
347       this.obtainClassName = obtainClassName;
348       this.releaseClassName = releaseClassName;
349       this.obtainMethodName = obtainMethodName;
350       this.releaseMethodName = releaseMethodName;
351     }
352   }
353
354   private static final class ResInfo
355   {
356     private String JavaDoc resourceAllocatedKey;
357     private LanguageElement resourceAllocatedBy;
358
359     private ResInfo(
360       String JavaDoc resourceAllocatedKey,
361       LanguageElement resourceAllocatedBy)
362     {
363       this.resourceAllocatedKey = resourceAllocatedKey;
364       this.resourceAllocatedBy = resourceAllocatedBy;
365     }
366   }
367 }
368
Popular Tags