Hi,
We noticed that when we send emails through the our web interface, when the emails becomes too much and the transaction gets too much time to perform, the request is invoked twice. Standalone jboss does not have this type of behavour and it waits until request is done. But when we run apache + mod_jk connector + jboss we noticed that a second request(duplicate request) is started. I searched through the Internet and I found that this is standard behaviour for mod_jk. If mod_jk does not receive response for some seconds it repeats the request (retry). We have repeated request which is bad for us.
worker.worker1.socket_timeout=40
Socket timeout in seconds used for the communication channel between JK and remote host. If the remote host does not respond inside the timeout specified, JK will generate an error, and retry again. If set to zero (default) JK will wait for an infinite amount of time on all socket operations.
http://tomcat.apache.org/connectors-...eference/workers.htmlThe number of retries are given with this setting in workers.properties:
worker.worker1.retries=1
The maximum number of times that the worker will send a request to Tomcat in case of a communication error. Each retry will be done over another connection. The first time already gets counted, so retries=2 means one retry after error. Before a retry, the worker waits for a configurable sleeping time. The default value is 2 and until version 1.2.16 the default value was 3, so this is why our mails are sent twice.
ANOTHER SOLUTION:
You can use double protection against duplicate requests and make some prevention of the second request in your servlet using a simple property with milliseconds inside it.
public class DefaultServlet extends HttpServlet {
//Keeps max 100 records
private static Map<String,Object> cacheDupRequests
= Collections.synchronizedMap(new SimpleLRUCache<String,Object>(100));
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
.........
//preprocessing
preprocessRequest(request);
//do your work
.....
}
private void preprocessRequest(HttpServletRequest request) {
//ignore duplicate requests which are risky
final String ignoreDupReqKey = RequestHelper.get(request, "ignoreDupReqKey");
if (ignoreDupReqKey != null) {
if (cacheDupRequests.containsKey(ignoreDupReqKey)) {
//throw your exception that you can handle
throw new DuplicateRequestException();
} else {
cacheDupRequests.put(ignoreDupReqKey, Boolean.TRUE);
}
}
}
}
SimpleLRUCache.java has this code:
package .................;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Simple LRU cache implementation that uses a LinkedHashMap.
* This cache is not synchronized.
* This map will keep at most cacheSize records( The eldest entries are removed).
*/
public class SimpleLRUCache<K, V> extends LinkedHashMap<K, V> {
private int cacheSize;
public SimpleLRUCache(int cacheSize) {
super(cacheSize + 10);
this.cacheSize = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > cacheSize;
}
}
So when you want to prevent some important transaction to be duplicate, then just add this special hidden field in your html form:
<form action="<%=request.getContextPath()%>/....." class="form">
<input type="hidden" name="ignoreDupReqKey" value="<%=System.currentTimeMillis()%>MAIL">
......
</form>
And here is your protected code working just once!