Problems uploading/deploying large WAR’s to Tomcat7?

I’ve run into this a few times as my web applications got larger. Often this has been seen when builds automated by Jenkins start failing as they increase in size. It has also occurred to me when doing manual deployments as the Jenkins WAR itself is larger than 50MB lately.

Let’s just go in and increase the maximum expected file size…

This change should work on any platform, but the following is from my experience with Ubuntu.

sudo vi /opt/tomcat7/webapps/manager/WEB-INF/web.xml

Default is:

<multipart-config>
<!-- 50MB -->
<max-file-size>62428800</max-file-size>
<max-request-size>62428800</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>

Change to something a bit larger (to your liking):

<multipart-config>
<!-- 50MB max 62428800, 100MB = 104857600 -->
<max-file-size>104857600</max-file-size>
<max-request-size>104857600</max-request-size>
<file-size-threshold>0</file-size-threshold>
</multipart-config>

Restart with either…
sudo /etc/init.d/tomcat7 restart
or
sudo service tomcat7 restart

HTTP Forward vs. Redirect

A Controller servlet may perform either a forward or a redirect operation at the end of processing a request. It is important to understand the difference between these two cases, in particular with respect to browser reloads of web pages.

Forward

  • a forward is performed internally by the application (servlet).
  • the browser is completely unaware that it has taken place, so its original URL remains intact
  • any browser reload of the resulting page will simple repeat the original request, with the original URL

Redirect

  • a redirect is a two step process, where the web application instructs the browser to fetch a second URL, which differs from the original
  • a browser reload of the second URL will not repeat the original request, but will rather fetch the second URL
  • redirect is marginally slower than a forward, since it requires two browser requests, not one
  • objects placed in the original request scope are not available to the second request.

There are several ways to perform a Redirect, here are a few common ones:

  • URL Redirection (HTTP 301):
    HTTP/1.1 301 moved permanently
    
    Location: http://www.example.org/
  • HTTP Refresh Header (Not Recommended)
    HTTP/1.1 200 ok
    
    Refresh: 0; url=http://www.example.com/
  • HTML <meta /> tag
    <meta http-equiv="refresh" content="0; URL=http://www.example.org/" />
  • JavaScript (many possible solutions, generally not accessible or searchable)
    <script type="text/javascript">location.href='http://www.example.org/';</script>

In general, a forward should be used if the operation can be safely repeated upon a browser reload of the resulting web page; otherwise, redirect must be used. Typically, if the operation performs an edit on the datastore, then a redirect, not a forward, is required. This is simply to avoid the possibility of inadvertently duplicating an edit to the database.

More explicitly :

  • for SELECT operations, use a forward
  • for INSERT, UPDATE, or DELETE operations, use a redirect

In HTML, a <FORM> tag can either GET or POST its data. In this context, a GET corresponds to a SELECT-then-forward, and a POST corresponds to an edit-then-redirect.

It is strongly recommended that forms for the input of search criteria should use GET, while forms for editing database records should use POST.

SECURITY NOTE: When using GET, be sure to not expose sensitive data in the URL’s.

Href Links as HTTP POST (complex DOM solution)

The earlier post, while easy to implement, has some well known security issues. Now lets get around them. First we’ll remove the FORM from the HTML itself, and instead build it dynamically and insert it into the BODY via the DOM and then submit it with JavaScript.

Again, if you’ve already implemented my prior solutions, this is just a small code refactor.

<html>
<head>
<title>Example FORM Post - DOM</title>
<script type="text/javascript">
/*
* Uses "location.replace()" vs. "location.href()" for all valid links.
* 'replace' has side-effect of 'restricting' back-button, or 'location'
* @param obj Object clicked
* @param x URL
*/
function xlinkObj(obj,url){
//Consider replacing w/ "return xlinkFrm(obj,url); " if you want POST behavior.
window.location.replace(uniqueUrl(url));
return false;
}
/*
* uses a FORM for the requested URL, could be POSTed!
* This is a simple solution, more complex solution COULD build the FORM and then parse attributes into INPUT's
* NOTE: you probably SHOULD NOT use this for external links, unless you intend for them to receive your params!
* @param obj Object clicked (NOT USED in this Example)
* @param x URL
*/
function xlinkFrm(obj,x){
var url=uniqueUrl(x);
var frmObj=xlinkFrmHelp(url);
if(frmObj!=null){
frmObj.submit();
}else{
alert('ERROR!');
}
return false;
}
/*
* Expects URL with queryString as param href
* @param x URL
* @return obj Object of the generated FORM
*/
function xlinkFrmHelp(x){
var rc=null;
try{
var ar = x.split("?");
var act = ar[0];
var str = ar[1];
var id="frm" + xmillis();
var oFORM=document.createElement("form");
oFORM.setAttribute("id",id);
oFORM.setAttribute("method","post");
oFORM.setAttribute("action",act);
if(str!=null){
var parms=str.split('&');
for(i=0; i < parms.length; i++){
var parm=parms[i];
var pair=parm.split('=');
var oINPUT=document.createElement("input");
oINPUT.setAttribute("type","hidden");
oINPUT.setAttribute("name",pair[0]);
oINPUT.setAttribute("value",pair[1]);
oFORM.appendChild(oINPUT);
}
}
var oBODY=document.getElementsByTagName("body")[0];
oBODY.appendChild(oFORM);
rc=oFORM;
}catch(e){
alert("Error"+e);
}
return rc;
}
/*
* generates a timestamp as a number
*/
function xmillis(){
return new Date().getTime();
}
/*
* adds timestamp to URLs to make them unique
* @param URL String
*/
function uniqueUrl(x){
return urlAppender(x,'time',xmillis());
}
/*
* helps to add parms to the url
* @param URL String
* @param aname String
* @param avalue String
*/
function urlAppender(x,aname,avalue){
var delim = "?";
if(x.indexOf("?") >=0) { delim = "&"; }
return x + delim + aname + '=' + avalue;
}
/*
* Abstracts "document.getElementById()" with appropriate error handling.
* @param id String
* @returns Object (NULL when not found!)
*/
function xgetHelper(id){
var obj = null;
try {
obj = document.getElementById(id);
} catch(z) {
var dummy=alert("Error:" + z);
}
return obj;
}
</script>
<a href="javascript:void(0);" onclick="return xlinkObj(this,'index.php');">REFRESH</a>
<a href="javascript:void(0);" onclick="return xlinkFrm(this,'index.php');">TEST-POST</a>
<a href="javascript:void(0);" onclick="return xlinkFrm(this,'index.php?a=b');">TEST-POST2</a>
<a href="javascript:void(0);" onclick="return xlinkFrm(this,'http://www.skotfred.com/hello');">TEST-XSS</a>
</body>
</html>

Cheers!

Href Links as HTTP POST (simple solution)

Another interesting challenge, The standard <a href=”…”></a> style link performs an HTTP GET, however you might want to perform a POST in some cases. HTML does not natively support this behavior, but it can be accomplished in JavaScript. If you have already implemented some of my security ‘hacks’ from previous posts this is only a small change. As usual I’ve included the minimum code required for this task in the example, but you should be able to easily merge the different features back into this!

Later on (in a different post), I’ll expand this to make it even more secure as this solution simply puts all of the URL into the FORM ‘action’ attribute and it would be better to pass them in the FORM body itself to hide them from the URL shown in the browser.

<html>
<head>
<title>Link to FORM POST simple example</title>
<script type="text/javascript">
/*
* Uses "location.replace()" vs. "location.href()" for all valid links.
* 'replace' has side-effect of 'restricting' back-button, or 'location'
* @param obj Object clicked (NOT used in this example)
* @param x URL
*/
function xlinkObj(obj,url){
//Consider replacing w/ "return xlinkFrm(obj,url); " if you want POST behavior in all cases!
window.location.replace(uniqueUrl(url));
return false;
}
/*
* uses a FORM for the requested URL, could be POST'ed!
* This is a simple solution, using an existing empty FORM on the page.
* A more complex and secure solution COULD build the FORM dynamically and then parse attributes into INPUT's
* NOTE: you probably SHOULD NOT use this for external links, unless you intend for them to receive your params!
* @param obj Object clicked (NOT used in this example)
* @param x URL
*/
function xlinkFrm(obj,x){
var frmObj=xgetHelper('frmXlink');
if(frmObj!=null){
frmObj.action=uniqueUrl(x);
frmObj.submit();
}else{
alert('ERROR!');
}
return false;
}
/*
* generates a timestamp as a number
*/
function xmillis(){
return new Date().getTime();
}
/*
* adds timestamp to URLs to make them unique
* @param URL String
*/
function uniqueUrl(x){
return urlAppender(x,'time',xmillis());
}
/*
* helps to add parms to the url
* @param URL String
* @param aname String
* @param avalue String

*/
function urlAppender(x,aname,avalue){
var delim = "?";
if(x.indexOf("?") >=0) { delim = "&"; }
return x + delim + aname + '=' + avalue;
}
/*
* Abstracts "document.getElementById()" with appropriate error handling.
* @param id String
* @returns Object (NULL when not found!)
*/
function xgetHelper(id){
var obj = null;
try {
obj = document.getElementById(id);
} catch(z) {
var dummy=alert("Error:" + z);
}
return obj;
}
</script>
</head>
<body>
<form id="frmXlink" action="#" method="post"></form>
<a href="javascript:void(0);" onclick="return xlinkObj(this,'index.html');">REFRESH</a>
<a href="javascript:void(0);" onclick="return xlinkFrm(this,'index.html');">TEST-POST</a>
<a href="javascript:void(0);" onclick="return xlinkFrm(this,'index.html?a=b');">TEST-POST PARMS</a>
<a href="javascript:void(0);" onclick="return xlinkFrm(this,'http://www.giantgeek.com/hello');">TEST-XSS</a>
</body>
</html>

This just uses an “empty” FORM in the page and uses the new ‘xlinkFrm()’ method to copy the URL to the FORM ‘action’.

Like i said, this is a simple solution as the params are still on the URL making them less secure. I’ll be refactoring it to parse the params to dynamically build the FORM (that will no longer be hardcoded).

Cheers!