7

I am creating an initial, extensive static code analysis report about our Java/JSP web application (legacy of course ;-) for management to raise awareness for quality issues. The main Java code is no problem, there are plenty of free tools available, e.g. PMD, Checkstyle, Classcycle, etc.

But what about the JSPs? There is considerable Java code embedded in our JSPs (unfortunately) and this needs to be analysed. What tools could I use or what process do I need to follow to statically analyse JSPs?

  • I know about PMD 5's new functionality covering JSPs, is it worthwile to explore?
  • I could generate the source Servlets of the JSPs somehow and use basic Java analysis tools, if so then what is the easiest way to get the Java source for JSPs?
Peter Kofler
  • 9,252
  • 8
  • 51
  • 79

4 Answers4

3

A very simple metric may work for your purpose - "does this jsp contain scriptlets" or "number of lines scriptlet code", which you ought to be able to cobble together with grep or something similar.

Would detail beyound this add any value if you are making a pitch to management for time to fix things?

---- Edit ----

A quick google suggests that you might be able to extract the stats I suggested above for jsps using this

http://www.semanticdesigns.com/Products/SearchEngine/

Which was suggested as an answer in this question

What would be a good way to measure the size of a JSP project?

Community
  • 1
  • 1
henry
  • 5,923
  • 29
  • 46
  • +1 I this is a very good metric. Any line of code in a JSP is a bad line of code :). – Augusto Mar 14 '13 at 12:07
  • and the relation of `directives <-> LoC scriptled <-> LoC Html` gives me a nice overview what is going on: many imports/dependencies vs. too much code vs. page ok. Thank you. – Peter Kofler Mar 23 '13 at 12:45
3

As henry pointed out correctly, a good metric for JSPs is "number of lines scriptlet code". This can be done with some regular expressions.

I found the ratio of lines of code versus lines HTML (=LoC_Java/LoC_Html) to be most expressive: The smaller this number is, the better. Ratios up to 20 or 30% look ok, but values above 50% or even > 1 are bad. In my analysis I found JSPs with ratios up to 6 (really bad).

Also the number of page directives provides rough insight. As this number is mainly caused by imports, high numbers indicate high coupling.

Similar metrics are available in PMD which hase a JSP Ruleset. Is is based on HTML page analysis and is able to report the number of scriptlets, length of scriptlets, duplicate imports and encoding issues as well as plain HTML anti patterns.

Peter Kofler
  • 9,252
  • 8
  • 51
  • 79
1

I don't want to sound bitter, but what are trying to get out of this?

I think (and I might be wrong) that getting code quality metrics on JSP is not really useful as the foundation is actually a very bad practice.

Personally, I would try to invest any resources or time into figuring out how to migrate the logic from the JSPs to an MVC or DCI framework.

Augusto
  • 28,839
  • 5
  • 58
  • 88
  • Ty for asking. It's a big project and I am new. There are 500+ JSPs and I can not check every one of them. I saw some with logic mixed, but need an assessment how bad/widespread the problem is. Depending on that we can work on fixing it (or maybe fixing sth else, because there are plenty of options ;-) – Peter Kofler Mar 14 '13 at 12:35
0

Honestly speaking this analysis (if possible) will give you nothing . Mixed up code inside JSP just sucks . Only solution is to move those at least in a service class , if initially not possible to migrate to any MVC framework . Later those service class can be reused and analyzed more easily.

Example of a painful jsp code i worked recently : the system is 9 years old

oListCode.setDB(driverclass, databaseurl, databasetype.intValue());
java.util.Vector oVecActiveStatusListCodes = oListCode.getListCodes(13, user.getAdminId());


java.text.DecimalFormat dformat = new java.text.DecimalFormat("###0.00");
java.text.DecimalFormat averageformat = new java.text.DecimalFormat("###0.##");

java.util.Vector userDirectoryVector = new java.util.Vector();

String searchoptionvalue = request.getParameter("iscombinesearch");
if (searchoptionvalue != null && searchoptionvalue.equals("on")) {
    java.util.Vector oVectorStudents = (java.util.Vector) session.getAttribute("studentsearchresult");

    if (oVectorStudents != null) {
        oVector.addAll(oVectorStudents);
    }
}


java.util.Vector<StatusSequenceInfo> oStaSeqVector = student.getStatusSequence(user.getAdminId());
java.util.Vector<StatusSequenceInfo> oEnrollStatusvecto = student.getProgramStatus(user.getAdminId());


String parameterList = "?columns=" + java.net.URLEncoder.encode(columns, "UTF-8") +
                    "&activestatus=" + activestatus +
                    "&studenttype=" + studenttype +
                    "&faith=" + faith +
                    "&race=" + race +
                    "&levelid=" + levelid +
                    "&levelidlist=" + levelidlist +
                    "&curriculumid=" + curriculumid +
                    "&programmeid=" + programmeid +
                    "&programmelevelid=" + programmelevelid +
                    "&semesterid=" + semesterid +
                    "&sex=" + sex +
                    "&idnumber=" + java.net.URLEncoder.encode(idnumber, "UTF-8") +
                    "&batchnumber=" + java.net.URLEncoder.encode(batchnumber, "UTF-8") +
                    "&firstname=" + java.net.URLEncoder.encode(firstname, "UTF-8") +
                    "&middlename=" + java.net.URLEncoder.encode(middlename, "UTF-8") +
                    "&lastname=" + java.net.URLEncoder.encode(lastname, "UTF-8") +
                    "&nationality=" + java.net.URLEncoder.encode(nationality, "UTF-8") +
                    "&address=" + java.net.URLEncoder.encode(address, "UTF-8") +
                    "&city=" + java.net.URLEncoder.encode(city, "UTF-8") +
                    "&state=" + java.net.URLEncoder.encode(state, "UTF-8") +
                    "&zip=" + java.net.URLEncoder.encode(zip, "UTF-8") +
                    "&homephone=" + homephone +
                    "&email=" + email +
                    "&advisor=" + java.net.URLEncoder.encode(advisor, "UTF-8") +
                    "&dob=" + dob +
                    "&visaexpiredate=" + java.net.URLEncoder.encode(visaexpiredate, "UTF-8") +
                    "&regstatus=" + regStatus +
                    "&regstartDate=" + java.net.URLEncoder.encode(regstartDate, "UTF-8") +
                    "&regendDate=" + java.net.URLEncoder.encode(regendDate, "UTF-8") +
                    "&shashmap=" + shashmap +
                    "&studentcount=" + studentcount + extendedurl;

String paginationLink = "admin_search_student_result.jsp" + parameterList;
long totalpage = new Double(Math.ceil(new Double(studentcount).doubleValue()/number_of_student)).longValue();//for pagination

String downloadStudentLink = "download_search_student_result.jsp" + parameterList + "&number_of_student=" + studentcount;
String printStudentLink = "admin_print_search_student_result.jsp" + parameterList + "&from_row=" + from_row;

%>


<html <%if (user.getLanguageId()>0 && oDictionary.getLanguageInfo(user.getLanguageId()).getDirection()==1) out.print("dir='rtl'");%>>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-15"/>
<title><%if (user.getRole()==6) out.print(oDictionary.getTranslatedWord(userLanguageId, "Sub-Administrator", adminId)); else out.print(oDictionary.getTranslatedWord(userLanguageId, "Administrator", adminId));%></title>
<meta name="GENERATOR" content="Microsoft FrontPage 5.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<meta name="Microsoft Theme" content="blocks 000, default">
<meta name="Microsoft Border" content="tlb, default">

</head>


<body bgcolor="#FFFFFF" text="#000000" link="#CC0000" vlink="#CC0000" alink="#CC0000" leftmargin=5 topmargin=5 marginheight="0" marginwidth="0" onResize="if (navigator.family == 'nn4') window.location.reload()">


<p>
<table width=100%>
<td>
<img src="images/search.gif"> <b><font color="#666666" face="Arial, Arial, Helvetica" size="5"><%=oDictionary.getTranslatedWord(userLanguageId, "Search Result", adminId)%> &nbsp;&nbsp;</font></b>
</td>
<td align=right>
<table>
<%if (user.getRole()==4 || (user.getRole()==6 && (user.getOptions().indexOf("[STUDENT_RECORDS=") != -1 ))) {%>
<td>
&nbsp;<img src="images/write.gif"> <font face=arial size=2 color="#666666"><b><a href="new_student.jsp"><%=oDictionary.getTranslatedWord(userLanguageId, "New Student", adminId)%></a></font></b>
</td>
<%} %>
<td>
&nbsp;<img src="images/search.gif"> <font face=arial size=2 color="#666666"><b><a href="admin_search_student.jsp"><%=oDictionary.getTranslatedWord(userLanguageId, "Search Student", adminId)%></a></font></b>
</td>
<td>
&nbsp;<img src="images/read.gif"> <font face=arial size=2 color="#666666"><b><a href="students.jsp"><%=oDictionary.getTranslatedWord(userLanguageId, "Student Directory", adminId)%></a></font></b>
</td>
</table>
</td>
</table>


<%if (user.getRole()==4 || (user.getRole()==6 && (user.getOptions().indexOf("[STUDENT_RECORDS=F]") != -1 || user.getOptions().indexOf("[STUDENT_RECORDS=V]") != -1))) {%>

<table width="100%">
<tr>
<td>
    <%@ include file="paginate.jsp" %>
</td>

<td align="right">
<table>
<tr>
<td>
<img src="images/download.jpg">
<a href="<%=downloadStudentLink%>" target=_><font face=arial size=-1>[<%=oDictionary.getTranslatedWord(userLanguageId, "Download search result", adminId)%>]</font></a>
</td>
<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a href="<%=printStudentLink%>" target=_><img border=0 src="images/printer.gif"><font face=arial size=-1>[<%=oDictionary.getTranslatedWord(userLanguageId, "Print search result", adminId)%>]</font></a>
</td>
</tr>
</table>
</tr>
</table>

</font>
<table border="0" cellpadding="1" cellspacing="0" style="border-collapse: collapse" width="100%" id="AutoNumber1" bordercolordark="#666666" bordercolorlight="#999999">

<%if (!columns.equals("")) {%>

<tr>
<td nowrap bgcolor="<%=user.getColor()%>" bordercolor="#CC3300"><font face="Arial, Arial, Helvetica" size=2><b><%=oDictionary.getTranslatedWord(userLanguageId, "Student Name", adminId)%></b></font></td>
<%for (int i=0; i<oVectorColumns.size(); i++) {%>
<td nowrap bgcolor="<%=user.getColor()%>" bordercolor="#CC3300"><font face="Arial, Arial, Helvetica" size=2><b><%=oVectorColumns.get(i)%></b></font></td>
<%}%>
</tr>

<%

    for (StudentInfo studentinfo:oVector) {

        userDirectoryVector.add(new Long(studentinfo.getStudentId()));

        java.util.HashMap oHashMapProfielValues = oExtendedProfile.getProfileFieldValues(studentinfo.getStudentId(), 1, user.getAdminId());
        java.util.Vector oVectorStudentAllPrograms = registration.getStudentAllProgrammes(studentinfo.getStudentId(), user.getAdminId());

        String programname = "";
        String levelname = "";
        String programlevelname = "";
        String RegistrationDate="";

So , isn't it better to clean up these rather analyzing ? Answer is YES i think

Asraful
  • 1,241
  • 18
  • 31
  • I agree, short answer is of course yes. But I can not fix all of the 500 JSPs, so the team has to work on that beside the main business and then the management has to approve it to give time. I need to "create a case" to ask for time to fix it, I need some numbers, as rough as they might be. – Peter Kofler Mar 14 '13 at 12:37
  • agreed with you. bit tough situation from project management view. – Asraful Mar 17 '13 at 04:11