As far as I know Java Servlets are handling multiple requests concurrently and I've searched through StackOverflow as well as Google, and confirmed what I thought. However I am quite confused right now, I wrote a simple servlets that seem to show blocking behaviour.
so I have a simple Servlet:
public class MyServlet extends HttpServlet
{
private static final long serialVersionUID = 2628320200587071622L;
private static final Logger logger = Logger.getLogger(MyServlet.class);
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
logger.info("[doGet] Test before");
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
logger.info("[doGet] Test after");
resp.setContentType("text/plain");
resp.getWriter().write("OK");
}
}
Then I have 2 browser windows, I opened at the same time that hit my Servlet.
And the result is the first request blocking the 2nd one. The log also shows:
10:49:05,088 [http-8383-Processor14] INFO MyServlet - [doGet] Test before
10:50:05,096 [http-8383-Processor14] INFO MyServlet - [doGet] Test after
10:50:05,106 [http-8383-Processor22] INFO MyServlet - [doGet] Test before
10:51:05,112 [http-8383-Processor22] INFO MyServlet - [doGet] Test after
I feel like I am missing something ... Servlets supposed to be able to handle concurrent request, but it doesnt seem to be doing it. I also did the same as above on the service method instead of doGet and it does the same thing.
Any pointers?
Thanks
Your browser is apparently using the same HTTP connection in different windows. The servlet container uses a single thread per HTTP connection, not per HTTP request. You should run two physically different webbrowsers to test this properly. E.g. one Firefox and one Chrome.
Related
I have a class extends HttpServlet contains following two methods. When a client makes a request to web app running on a Tomcat server, http request will be handled via this class however if doSomething method throws RunTimeException how it gets handled based on below example?
protected void doPost(HttpServletRequest reqst, HttpServletResponse resp) {
doSomething();
}
private void doSomething() {
if (false) {
throw new RuntimeException("exception caught calling doSomething method");
}
else {
...
}
}
[update]
Can I add try/catch block inside of doPost where doSomething() is invoked?
All exceptions that reach servlet container code (i.e., those thrown or re-thrown by the doPost() method) will get handled such that Tomcat generates a 500 response, sets Content-Type to text/html, and generates a full HTML page in the body which says that an internal server error has occured, attaching the name and stack trace of the exception.
RE: your [Update]: yes, you can add a try-catch there. In fact, any reasonable implementation will include it. You want your application to dictate what response is sent to your client, not Tomcat or any other container.
When something fails on the server side because the database and the application are out of sync instead of getting an error and the application crashing spring/tomcat seems to swallow the exception and pretend nothing has happened.
Call me crazy but if the program fails catastrophically I want it to actually fail catastrophically! Is there anyway to switch this behaviour off? It's really slowing development down when the server pretends that everything is fine when it's just thrown up into the logs.
If this isn't the spring/tomcat default then what else might be causing it?
We are using a boatload of libraries and frameworks unfortunately. Spring would be the usual suspect but it could be something else.
Update
It's a sql server database which we are connecting to using SqlServerDataSource. Hibernate is in use in some parts of the project but is used to query the database at login time. On the client side we are using extjs and we are also using ExtDirectSpring to annotate methods for the client side to talk to. To translate the data going across the wire there's Jackson, which then gets wrapped by the extdirect json handler.
There's some AOP stuff going on thats to do with logging exceptions but deleting that code results in the same behaviour.
Further update
Ok its not a good idea to let your sever crash! See my answer below for my proposed middle ground.
If you really want to do that (but IMHO you should not ...) you can use a filter that will block the application once it let an uncaught exception go. It could be something like :
public class CrashFilter implements Filter {
private boolean crashed = false;
private String msg = "Major problem : application stopped";
#Override
public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
if (crashed) {
HttpServletResponse resp = (HttpServletResponse) sr1;
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
return;
}
try {
fc.doFilter(sr, sr1);
}
catch (Exception ex) {
crashed = true;
throw new ServletException(ex);
}
}
// init and destroy omitted from brevity
}
Ok so I did this in the end. I've basically used the ideas above but thought there was enough extra to post my own answer.
It turns out you really shouldn't do this as other people suggested and I've added a bit at the bottom to say why!
Here's my filter:
public class FailOnErrorFilter implements Filter
{
#Override
public void init(FilterConfig config) throws ServletException
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
{
try {
filterChain.doFilter(request, response);
}
catch (Exception exception) {
System.exit(1);
}
}
#Override
public void destroy()
{
}
}
To get this working you have to modify the web.xml:
<filter>
<filter-name>failingFilter</filter-name>
<filter-class>fullyQualified.FailOnErrorFilter</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>failingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The top one defines the filter + the second one says use it everywhere.
ExtDirectSpring
After doing the above I realised that ExtDirectSpring was a further culprit. It's default behaviour is to swallow all exceptions in server side methods.
I was worried I was going to have to patch the library but thankfully someone else had already complained about this and it got fixed in 1.3.6. Initially I tried upgrading to the latest version but it broke a load of code! What a great library. Anyway 1.3.6 added in the ability to switch off the suppression of errors by doing the following:
//this is to switch off blanket switching of exceptions in extdirect spring
#Component
public class NoExceptionHandling implements RouterExceptionHandler
{
#Override
public Object handleException(MethodInfo methodInfo, BaseResponse baseResponse, Exception e, HttpServletRequest httpServletRequest)
{
throw new RuntimeException(e);
}
}
As the name suggests extdirectspring uses spring and so doesn't make its dependencies obvious to calling code, however if you go digging (its on github). You'll see in RouterController it calls the following method in the catch
private Object handleException(MethodInfo methodInfo, BaseResponse response, Exception e, HttpServletRequest request) {
return configurationService.getRouterExceptionHandler().handleException(methodInfo, response, e, request);
}
Where router controller does this:
#Autowired(required = false)
private RouterExceptionHandler routerExceptionHandler;
public RouterExceptionHandler getRouterExceptionHandler() {
return routerExceptionHandler;
}
It sets up a default one if you dont provide one.
Update - why you shouldn't do this
It turns out you really shouldn't call System.exit in a tomcat application. Not only does it bring down your application it also causes the server to exit. This brings down any other applications running aswell!
It's also not appropriate for a number of other reasons:
if the first in a series of tests throws an exception then all subsequent tests will fail
Its time consuming restarting the server and you have to be the person who breaks it to see the exception
if you are running a manual test deployment on a seperate machine then you have to restart the server if something caused a problem somewhere.
Likewise:
In production it will take everyone's application down and most users wont be in a position to restart the server.
What I'm doing instead
The errors were already being written the the tomcat logs + the database.
In debug we now also going to redirect to an error page with the stacktrace
In production we are going to just redirect to a 'something went wrong' page. We're also going to set up an email service that notifies us of exceptions.
For UI/selenium tests - it'll work the same as debug
For headless Js tests the server rejects subsequent requests until the next test resets the error state of the server
Just to make things more complicated unsurprisingly the original webapp is too flaky to not mask errors so I've kept the old error suppression in place for that as we're not actively developing/fixing it at the moment.
I've written a servlet deployed in tomcat.
public class myServlet extends HttpServlet {
public int NumberOfThreads = 0;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(NumberOfThreads);
NumberOfThreads++;
....
..a lot of code..
....
NumberOfThreads--;
}
}
Now when I get too many requests the NumberOfThreads keeps rising and never goes down again. My problem is that there is a few tasks that have to be performed by each request before leaving.
I just don't understand why this happens. Is it that some of the threads get lost on the way? I really need each request to say properly goodbye.
Thanks
You are doing it wrong.
System.out.println(ManagementFactory.getThreadMXBean().getThreadCount());
Alternatively, just use JMX/JConsole.
As you're speaking about this taking a long time and requests are being cancelled (in your comment): Yes, the whole doGet will be executed, even when the user cancelled the request: Request cancelling is only on HTTP level. However, when the request is cancelled, the HTTP connection might be closed, resulting in exceptions when you actually want to write to the response's output stream.
Combining the other answers already given:
You'll need to synchronize your modifications of the counter (see didxga's answer)
there's probably a better way to solve your problem (as Ravi Thapliyal states)
use try{ ... } finally { ... } to ensure you actually decrease the counter
make your code more maintainable by moving it out of the servlet into a proper, non-UI, class
Pseudo code:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
synchronized(this){NumberOfThreads++;}
doSomething();
} finally {
synchronized(this){NumberOfThreads--;}
}
}
Also, be aware that long-running execution in the actual http connector thread blocks all subsequent http requests - it might be a good idea to trigger background processing and just query that background process in later HTTP requests. That way you can also queue multiple invocations and not start a huge number of background threads at the same time. Keep in mind, there's a limited number of HTTP request handlers.
I'm assuming the try/finally will be your main problem (or an endless loop in your code) - synchronizing will solve rare race conditions, especially as you're speaking of a lot of code executed in this servlet.
The different servlet threads are caching NumberOfThreads. You have to mark it as volatile.
public volatile int NumberOfThreads = 0;
But, I have a feeling that there are better ways of doing what you probably want to achieve with this code.
You need to synchronize modification to NumberOfThreads
public class myServlet extends HttpServlet {
public int NumberOfThreads = 0;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(NumberOfThreads);
synchronized(this){NumberOfThreads++;}
....
..a lot of code..
....
synchronized(this){NumberOfThreads--;}
}
}
Server errors result in HTTP 500-responses to the client with a generic error message ("The server encountered an error..."). Is there any way to intercept this message and write a custom one?
I'm would like to have a way to uniquely identify a server error from the client. If I could for instance generate a GUID which I logged server-side upon a server error and then send that ID to the client, that would make it easy to search for that particular exception in the log at any point later in time.
I do realize that server errors are generated by exceptions in the code, so I'm looking for some kind of catch all exception hook in the app engine API. Of course, if such a hook exists, and the code which executes here generates a second exception, it would have to default to the general 500-error again.
I'm using the Java API for GAE
For GAE generated errors you can configure a custom error page. For errors generated by your code you should use a catch-all wrapper inside a first servlet filter.
I ended up coding a Servlet Filter by following the answer in this SO question. The filter wraps the doFilter() call in a general try-catch block and creates a reference number for the client while logging it at the server. I think this little snippet might be useful for others out there:
public class ExceptionFilter implements Filter {
private FilterConfig filterConfig;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
try {
filterChain.doFilter(request, response);
}
catch (Exception ex) {
String errorId = UUID.randomUUID().toString();
Mylog.e("Server error " + errorId); // Use whatever logging mechanizm you prefer
String clientResponse = "Server error. Reference no: " + errorId;
((HttpServletResponse) response).setStatus(500);
response.getWriter().write(clientResponse);
}
}
public FilterConfig getFilterConfig() {
return filterConfig;
}
public void init(FilterConfig filterConfig) {
this.filterConfig = filterConfig;
}
public void destroy() {}
}
You also need to configure web.xml like this (goes somewhere under the <web app> tag):
<filter>
<filter-name>ExceptionFilter</filter-name>
<filter-class>your.package.ExceptionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ExceptionFilter</filter-name>
<servlet-name>Your Servlet Name As Defined In servlet-mapping</servlet-name>
</filter-mapping>
You didn't mention if your using python or java. Python error display https://developers.google.com/appengine/docs/python/config/appconfig#Custom_Error_Responses
Note these are just static pages that are shown in the event of any uncaught errors.
You can try and catch these errors in your main handler (I am talking about python), but you can't always. For instance you maybe able to catch a DeadlineExceededError some times, and you may have a tiny bit of time to emit a log, or a redirect (maybe to the same page to try again or to your own static page, with an arg with the GUID you mentioned, then have javascript render it some useful way) but often that won't work. So it very much depends on the nature of the error.
I have a task that I want to wrap in a servlet to provide the ability to run the task remotely, by http request.
I know I can achieve this with REST API, but currently I assume (and please correct me if I'm wrong) that a simple servlet will do.
One of the things I want to achieve is that if a request to the servlet is made while another request is still processed, I'll get an appropriate response - "Task is already running".
I've built a simple servlet, using servlet-3.0, that calls the jar I want to run, but when I make 2 requests, the second one is not processed until the first one is finished.
EDIT:
My servlet is a simple http serlvet. service method overriden.
I have a system.out.println("a") in the start.
when I call the servlet in debug mode and then (while stopped at breakpoint) call it again, the message is printed only one time and printed the second time when I release the breakpoint and the first run finishes.
First of all, this does not seems like REST at all. If you really just want to spawn a (single) background task, make sure you do it in a separate worker thread, not the request thread.
Maybe you need a lock:
public class Task extends HttpServlet {
// for singleton
//private volatile boolean running = false;
// or try this:
public static boolean running = false;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
if(running){
PrintWriter out = response.getWriter();
out.println("running");
return;
}
synchronized(Task.class){
if(!running){
running = true;
// run the task
running = false;
}
}
}
}