I was told to optimize a web application. To do so I use JProfiler.
I noticed a large part of the response time is spent in the presentation layer. Especially, when the code builds the HTML code.
Drilling down this bottle neck, I saw that the code deals a lot with OGNL classes/methods (nearly 50% of the calls).
I tuned the JSP code (see below for details) by removing some unnecessary OGNL statements. I managed to gain 200ms. However the code still spend more than 2000ms on average in this bottle neck. The overall response time is around 3000ms.
The OGNL documentation says this:
As of OGNL 2.5.0 the OgnlContext object can automatically tracks evaluations of expressions. (...) you can access the last evaluation through the lastEvaluation property of OgnlContext.
Since I use Struts 2.3.15.1 (OGNL v3.0.6) how can I access this property or call the getLastEvaluation of OgnlContext either from JSP file or Action class ?
JSP file
<%# page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%# taglib prefix ="s" uri="/struts-tags" %>
<!DOCTYPE html>
<html>
<head>
<title><s:text name="domain.domain0.title"/></title>
<script type="text/javascript" src="<s:url value='/js/domain/domain0.js'/>"></script>
<script type="text/javascript" src="<s:url value='/js/core/html.js'/>"></script>
</head>
<body>
<div style="width:100%;color:green;font-weight: bold;text-align:center">
<s:text name="domain.domain0.information"/>
</div>
<div class="content selectFen">
<s:set var="noAnchor">false</s:set>
<s:iterator value="artefactList" status="numRow">
<s:if test="court == nomenclatureCritere && #noAnchor!=true">
<s:set var="noAnchor">true</s:set>
<a id="NomencAnchor" href="#NomencAnchor" style="visibility:hidden;"></a>
</s:if>
<s:set var="isOpen" value="nomenclatureCritere.startsWith(court) || ligProd != '80'"/>
<div class="parcoursNomenc">
<table onmouseover='this.style.backgroundColor="gainsboro"' onmouseout='this.style.backgroundColor=""' style="width: 100%;font-weight:bolder">
<tbody>
<tr>
<s:url var="childNomenUrl" action="ajaxPopupNomenChild" escapeAmp="false">
<s:param name="dateCritere" value="dateCritere"/>
<s:param name="sidNomenclaturePere" value="sidArtNomenc"/>
</s:url>
<s:if test="#isOpen">
<s:if test="#numRow.last">
<td onclick="noeudPlusMoins(this);loadChild('<s:property value="childNomenUrl" />', 'divParcours<s:property value="sidArtNomenc"/>')" class="open_last"> </td>
</s:if>
<s:else>
<td onclick="noeudPlusMoins(this);loadChild('<s:property value="childNomenUrl" />', 'divParcours<s:property value="sidArtNomenc"/>')" class="open"> </td>
</s:else>
</s:if>
<s:else>
<s:if test="#numRow.last">
<td onclick="noeudPlusMoins(this);loadChild('<s:property value="childNomenUrl" />', 'divParcours<s:property value="sidArtNomenc"/>')" class="closed_last"> </td>
</s:if>
<s:else>
<td onclick="noeudPlusMoins(this);loadChild('<s:property value="childNomenUrl" />', 'divParcours<s:property value="sidArtNomenc"/>')" class="closed"> </td>
</s:else>
</s:else>
<td style="vertical-align: top; width: 8.5em; padding-top: 5px;">
<s:text name="domain.domain0.label"/> <s:property value="court"/>
</td>
<td class="description">
<s:property value="description"/>
</td>
</tr>
</tbody>
</table>
<div id="divParcours<s:property value="sidArtNomenc"/>" class="<s:if test="numRow.last">subtree_last</s:if><s:else>subtree</s:else>" >
<s:if test="#isOpen">
<s:action name="popupNomenExist" executeResult="true">
<s:param name="dateCritere" value="dateCritere"/>
<s:param name="nomenclatureCritere" value="nomenclatureCritere"/>
</s:action>
<s:action name="ajaxPopupNomenChild" executeResult="true">
<s:param name="dateCritere" value="dateCritere"/>
<s:param name="sidNomenclaturePere" value="sidArtNomenc"/>
</s:action>
</s:if>
</div>
</div>
</s:iterator>
</div>
</body>
</html>
You can access OgnlContext like
OgnlContext context = (OgnlContext) ActionContext.getContext().getValueStack().getContext();
context.setTraceEvaluations(true);
Evaluation evaluation = context.getLastEvaluation();
How to trace evaluations is described in the document you linked in the section for Trace Evaluations
You can also set system property ognl.traceEvaluations before the context is instantiated to turn on this feature.
Looking at your JSP code, it's evident that the culprits are the <s:action> tags:
You call two actions on each iteration... that basing on the number of rows you are iterating, may become a serious problem.
I would try refactoring that section; a big part of the bottleneck is in the mechanism to interpret the tag, call the action, pass through the Interceptor Stack, and so on.
If you can generate the same output minimizing the calls to actions, the bottleneck would decrease significantly.
P.S: a very minimal optimization is to change the order of the boolean and string comparison in your s:if, to exploit the short circuit (boolean comparison is faster):
<s:if test="#noAnchor!=true && court == nomenclatureCritere">
EDIT
It works better without s:action however the page need them and I don't know how to replace them
To be more clear, an Action should
perform one (or few) logical action;
never be used as an helper class.
When you call popupNomenExist , your only requirements is to call the server, do something, and receive the result to inject it in the JSP.
There are several ways to receive that result, and calling an Action is probably the worst (in terms of performances, because of the reasons listed above).
You can take the code from the execute() method of your popupNomenExist Action, and :
place it in a method of YOUR current action, like described in RomanC linked answer;
place it in a static method of an helper class;
precalculate it in your current action, and expose the results as List of Strings;
and so on...
In addition, we can't argue what is exactly being performed inside your popupNomenExist's execute() method, but I guess there are chance that it could be refactored to prevent executing some part of logic that is common to each iteration, grouping them and executing before the iteration as a sort of cache.
Related
i need some help with these
I have header.jsp, login.jps and register.jsp.
I build my Body with Header.jsp and another JPS.
I want to change my 'Help' link of Header.jsp depending on the Jsp i build. How i can do that?
I looked for javascript solution but is a 'dirty' solucion and Sonar is saying 'God DONT DO THAT'.
More details to explain mysefl, my Header.jsp has that code:
<ul class="pull-right">
<c:if test="${empty sessionScope.USER}">
<li><spring:message code="header.manual"/><span><spring:message code="header.help"/></span></li>
</c:if>
If i build with Login.jsp i want that href linked to "url.com/home.public?help_login" and if is Register.jsp i want href liked to "url.com/home.public?help_register".
INFO: when i use register.jsp my url is "url.com/register.public" otherwise i use login.jsp my url is "url.com/login.public".
Sorry if my english is bad, but i think you will understand it.
EDIT: Layout.jsp
<body class="home" onload="nobackbutton();">
<c:choose>
<c:when test="${not empty sessionScope.ADMIN}">
<tiles:insertAttribute name="headerAdmin" />
<tiles:insertAttribute name="menuAdmin" />
</c:when >
<c:otherwise>
<tiles:insertAttribute name="header" />
<tiles:insertAttribute name="menu" />
</c:otherwise>
</c:choose>
<div class="container-fluid">
<tiles:insertAttribute name="miga" />
<tiles:insertAttribute name="body" />
</div>
</body>
Finally i used a document.ready (javascript) to identify de href and modify it and make sonar ignore that because i found nothing better than these. I don't want lose more time ...
Thnk Hooch for help
Can't you simply do something like the following?
<%
if (${sessionScope.USER} != null) {
%>
<li><spring:message code="header.manual"/><span><spring:message code="header.help"/></span></li>
<%
}else{
%>
<li><spring:message code="header.manual"/><span><spring:message code="header.help"/></span></li>
<%
}
%>
Edit: since you need to know the current page you could check
<%= request.getServletPath() %>
In the header.jsp
I have the following basic html table with a Java function to print only the table content However the Print button does nothing when clicked,I figure probably the java coding is the issue, however it is not working could I get a third pair of eyes to assist please
<html>
<body>
<table border="1" cellpadding="3" id="printTable">
<tbody><tr>
<th>First Name</th>
<th>Last Name</th>
<th>Points</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>80</td>
</tr>
<tr>
<td>Adam</td>
<td>Johnson</td>
<td>67</td>
</tr>
</tbody></table>
<br />
<br />
<button>Print me</button>
<script>
function printData()
{
var divToPrint=document.getElementById("printTable");
newWin= window.open("");
newWin.document.write(divToPrint.outerHTML);
newWin.print();
newWin.close();
}
$('button').on('click',function(){
printData();
})
</script>
</body>
</html>
When working with javascript, it is often useful to look at your browser's console. After opening this page, mine has the error:
ReferenceError: $ is not defined
$ is a global variable inserted by jQuery. Try adding
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
to the code. This will include the jQuery library.
The call to the javascript function is not correct. When you're using the '$' symbol it means that you're using JQuery. You have 2 options:
1 - Include the jquery library to your page:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
2 - Call the javascript function directly on the button on the 'onclick' event. This is the best option in your case:
<button onclick="printData()">Print me</button>
Change this var divToPrint=document.getElementById("printTable");
to this var divToPrint=document.getElementById("<%=printTable.ClientID>");
and add jquery library <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
$("#medicine").append('<tr class="hide1 newRow" id="row'+medCurrentIndex+'">'
+'<td>'+medtype[medCurrentIndex]+'</td>'
+'<td>'+medicineName[medCurrentIndex]+'</td>'
+'<td>'+frequency[medCurrentIndex]+'</td>'
+'<td>'+dose[medCurrentIndex]+'</td>'
+'<td>'+quantity[medCurrentIndex]+'</td>'
+'<td>'+numberofDays[medCurrentIndex]+'</td>'
+'<c:choose>'
+'<c:when test="${role eq 'doctor' }">'
+'<td><button class="btn" type="button" name="edit" value="Edit" onclick="editMedRow('+medCurrentIndex+');">Edit</button></td>'
+'<td><button class="btn" type="button" name="delete" value="Delete" onclick="deleteMedRow('+medCurrentIndex+');">Delete</button></td>'
+'</c:when>'
+'<c:otherwise>'
+'<td><input type="text" id="cost" /></td>'
+'</c:otherwise>'
+'</c:choose>'
+'</tr>');
is this code legal to write in javaScript of JSP?
because its giving me following error:
org.apache.jasper.JasperException: Validation error messages from TagLibraryValidator for c in /WEB-INF/views/prescriptionTemporary.jsp79: Illegal text inside "c:choose" tag: "'
Any Java code inside JSP will be evaluated at render time. This means, the server will read the JSP code and will start replacing any scriptlet or custom tag (like JSTL) for the necessary Java code. This can be easily explained by this example:
<script type="text/javascript">
var x = '${x}';
</script>
Or in JSTL form:
<script type="text/javascript">
var x = '<c:out value="${x}" />';
</script>
Assuming x is a request attribute with a value of "Hello World", this will generate this output in HTML:
<script type="text/javascript">
var x = 'Hello World';
</script>
In your case, it will try to convert this part of the code to its JSTL form:
<c:choose>'
<!-- ^ this character is invalid. There must be a <c:when> right after <c:choose>
v this character is also invalid. There must be a <c:when> right after <c:choose> -->
+'<c:when test="${role eq 'doctor' }">'
In short, you should not try to append any kind of Java code (scriptlet, expression language, custom tags like JSTL, etc) from JavaScript.
Writing jstl tags in javascript is not going to work. JQuery's .append(...) method is going to fire on the client side long after jstl tags are parsed.
If your goal is to choose, via jstl, what to append, a significant rewrite is needed.
edit:
Try this:
$("#medicine").append('<tr class="hide1 newRow" id="row'+medCurrentIndex+'">'
+'<td>'+medtype[medCurrentIndex]+'</td>'
+'<td>'+medicineName[medCurrentIndex]+'</td>'
+'<td>'+frequency[medCurrentIndex]+'</td>'
+'<td>'+dose[medCurrentIndex]+'</td>'
+'<td>'+quantity[medCurrentIndex]+'</td>'
+'<td>'+numberofDays[medCurrentIndex]+'</td>'
<c:choose>
<c:when test="${role eq 'doctor' }">
+'<td><button class="btn" type="button" name="edit" value="Edit" onclick="editMedRow('+medCurrentIndex+');">Edit</button></td>'
+'<td><button class="btn" type="button" name="delete" value="Delete" onclick="deleteMedRow('+medCurrentIndex+');">Delete</button></td>'
</c:when>
<c:otherwise>
+'<td><input type="text" id="cost" /></td>'
</c:otherwise>
</c:choose>
+'</tr>');
I want to emphasize that although the above snippet might work, I wouldn't recommend it. A better solution would be to dump ${role} into a global variable or a hidden input and do the decision making from the JS side. Or set the whole append string in jstl. Mixing the two makes this significantly harder to read.
I am having a problem with JSTL and empty operator. I already made few simple pages and everything worked fine, but now I have:
<%# page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
<form action="/Projekt/myaccount" method="post">
<table border="1">
<tr>
<td>Artist</td>
<td>Record Name</td>
<td>Delete</td>
</tr>
<c:forEach var="item" items="${records}">
<tr>
<td>${item.artist}</td>
<td>${item.recordName}</td>
<td>
<input type="checkbox" name='${item.recordName}|${item.recordName}'/>
</td>
</tr>
</c:forEach>
</table>
<hr/>
<input type="submit" name="back" value="back"/>
<c:if test='${not empty "${records}"}'>
<input type="submit" name="delete" value="delete selected"/>
</c:if>
</form>
</body>
</html>
now no matter if I set the records attribute or not, the delete button shows up:
<c:if test='${not empty "${records}"}'>
<input type="submit" name="delete" value="delete selected"/>
</c:if>
in normal situation to records attribute I pass ArrayList and then use foreach, but sometimes ArrayList is empty, so in those situations I don't want delete button to show up, I fought that easiest way to achieve this would be to use this empty operator. Where am I making a mistake?
I even tried to manually set this attribute to null:
if (ar.size() != 0)
request.setAttribute("records", ar);
else
request.setAttribute("records",null);
EDIT:
#Qwe: yes you are right, it worked for me before because I tested if attribute was empty in my way, it was always true, because I used wrong construct, but it worked because I just wanted to show one string, if there was no String nothing showed up so I was thinking that everything worked fine.
<c:if test='${not empty "${records}"}'> as well as <c:if test="${!empty '${showWarning}'}"> (from your comment) will always resolve to true because you're are actually testing if a String ${records} is empty or not, and obviously it is not.
Just to be sure - by String ${records} I mean a String value, just as if you were assigning it in Java like String foo = "${records}";.
The next line of code will test if records variable (that is looked up from page, request, session or application scope) is empty or not:
<c:if test="${not empty records}">
The line of code is 100% guaranteed to work :)
Also, request.setAttribute("records",null) is a bad way to remove attributes because empty tests not just request scope, but page, session etc. Use <c:remove var='records'/> instead.
I am a beginner with jsp servlets, and I need some help having my page not show up blank when I make changes. I am trying to include the the code from a donate-box.jsp into the layout of the org-about-panel.jsp page, which itself is included in another view jsp file. When I makes change to donate-box, then load the view file, I am seeing a blank site, and then upon refreshing, it shows up as before without the donate-box.
Here is the code I am trying to include, from the donate-box:
<logic:notEqual name="hideDonateBox" value="true">
<form action="https://<%=request.getServerName()%><%=contextPath%>/cartDonate.do method="get">
<input type="hidden" name="targetPage" value="<%=requestedPathWithQuery%>">
<div class="donateBox">
<logic:notEmpty name="detailOrgBean" property="offerings">
<table class="donateBox" cellpadding="0" cellspacing="0">
<logic:iterate name="detailOrgBean" property="relatedOfferings" id="receiver" indexId="oppIndex" length="5">
<tr class="donateBox">
<td class="donateBox">
<input type="radio" name="receiverId"
value="<bean:write name="receiver" property="id"/>">
</td>
<th class="donateBox" align="right">
$ -
/donate/<%= formatter.getUrlPath((Receiver) receiver) %>">
" CHECKED>
">
$ (Other)
This file is being included in another file with this code. I have tried commenting out the logic:notequal or logic:empty tags.
<%# include file="/templates/donate-box.jsp" %>
Thanks in advance!
This problems appears because your jsp (donate-box) can't be compiled (Such behavior I've seen in tomcat).
From quick look, I can say that requestedPathWithQuery - variable is undefined in scripting.
For more precise description you can take a look in logs