D. How to customize a UWS ?

8. Redirection and errors

In your servlet, after the initialization of your UWS, the only interaction with it is done at each HTTP request when calling executeRequest(HttpServletRequest, HttpServletResponse). Many occasions to throw an exception may occur during this call and particularly while executing an action. That's why this method is able to catch these errors. Its behavior in function of the occurred errors can be customized. Hence this part of the tutorial which will explain you in first how errors are caught and what is done with them.

Warning

The errors explained in this part are only about the UWS management. They must not be confused with the errors which occur during the execution of a job (those they are thrown within AbstractJob.jobWork()). These last one are already managed: they are stored as ErrorSummaries in the corresponding job. See A.3. Defining the job - Writing the task and C.4. Error summary for more details.

How does it work ?

The whole content of executeRequest(HttpServletRequest, HttpServletResponse) is put into a try block, so that any instance of UWSException can be caught. This kind of exception is thrown only by this library, and particularly when a UWS action is executed. So when an exception occurs the method sendError(UWSException, HttpServletRequest, HttpServletResponse) is called. By default, it displays an Apache error with the given HTTP error code and the given message, except if the HTTP error code is 303 (= See Other). In this last case a redirection is made thanks to the method redirect(String, HttpServletRequest, HttpServletResponse):

public abstract class AbstractUWS<JL extends JobList<J>, J extends AbstractJob> {
...
	public void sendError(UWSException error, HttpServletRequest request, HttpServletResponse response) throws IOException, UWSException {
		if (error.getHttpErrorCode() == UWSException.SEE_OTHER)
			redirect(error.getMessage(), request, response);
		else
			response.sendError(error.getHttpErrorCode(), error.getMessage());
	}
...
}

Nevertheless any other kinds of exception may also occur. For that reason, a second catch block has been added to catch Exception. In this block the function sendError(Exception, HttpServletRequest, HttpServletResponse) is called, which prints the stack trace of the exception in the standard output and then displays an Apache error with the given error message:

public abstract class AbstractUWS<JL extends JobList<J>, J extends AbstractJob> {
...
	public void sendError(Exception error, HttpServletRequest request, HttpServletResponse response) throws IOException, UWSException {
		error.printStackTrace();
		response.sendError(UWSException.INTERNAL_SERVER_ERROR, error.getMessage());
	}
...
}

Obviously the second function can also be used for UWSException, but the result will be different, especially about the management of the HTTP error code: no more redirection will be done anymore.

Note:

Because of redirect(...), sendError(Exception, ...) and sendError(UWSException, ...), the method executeRequest(HttpServletRequest, HttpServletResponse) may also throw exceptions (UWSException and IOException). That's why you should put the call to this method in try...catch block !In that way you can still manage errors as you wish.

How to customize ?

The three methods sendError(Exception, HttpServletRequest, HttpServletResponse), sendError(UWSException, HttpServletRequest, HttpServletResponse) and redirect(String, HttpServletRequest, HttpServletResponse) can be overrided. However you should still beware of the specified status code with the UWSException so that errors with the 303 status code always make a redirection to the URL given in the exception message.

For instance, here is how a UWS service could be modified so that errors are displayed in a different way:

public class MyExtendedUWS extends ExtendedUWS {
...
	@Override
	public void sendError(UWSException error, HttpServletRequest request, HttpServletResponse response) throws IOException, UWSException {
		// Reset the whole response to ensure the output stream is free:
		if (response.isCommitted())
			return;
		response.reset();
		
		// If HTTP status code = 303 (see other), make a redirection:
		if (error.getHttpErrorCode() == UWSException.SEE_OTHER)
			redirect(error.getMessage(), request, response);
		
		// Else, display properly the exception:
		else{
			// Set the HTTP status code and the content type of the response:
			response.setStatus(error.getHttpErrorCode());
			response.setContentType(UWSSerializer.MIME_TYPE_HTML);
			
			PrintWriter out = response.getWriter();
			
			// Header:
			out.println("<html>\n\t<head>");
			out.println("\t\t<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />");
			out.println("\t\t<link href=\""+UWSToolBox.getServerResource("styles/uwstuto.css", request)+"\" rel=\"stylesheet\" type=\"text/css\" />");
			out.println("\t\t<title>UWS ERROR</title>");
			out.println("\t</head>\n\t<body>");
			
			// Title:
			String errorColor = (error.getUWSErrorType() == ErrorType.FATAL)?"red":"orange";
			out.println("\t\t<h1 style=\"text-align: center; background-color:"+errorColor+"; color: white; font-weight: bold;\">UWS ERROR - "+error.getHttpErrorCode()+"</h1>");
			
			// Description part:
			out.println("\t\t<h2>Description</h2>");
			out.println("\t\t<ul>");
			out.println("\t\t\t<li><b>Type: </b>"+error.getUWSErrorType()+"</li>");
			String msg = error.getMessage();
			int start=msg.indexOf("["), end=msg.indexOf("]");
			String context=null;
			if (start >= 0 && start < end){
				context = msg.substring(start+1, end);
				msg = msg.substring(end+1);
			}
			if (context != null)
				out.println("\t\t\t<li><b>Context: </b>"+context+"</li>");
			out.println("\t\t\t<li><b>Exception: </b>"+error.getClass().getName()+"</li>");
			out.println("\t\t\t<li><b>Message:</b><p>"+msg+"</p></li>");
			out.println("\t\t</ul>");
			
			// Stack trace part:
			out.println("\t\t<h2>Stack trace</h2>");
			out.println("\t\t<table style=\"width: ihnerit;\">");
			out.println("\t\t\t<tr><th>Class</th><th>Method</th><th>Line</th></tr>");
			StackTraceElement[] trace = error.getStackTrace();
			for(int i=0; i<trace.length; i++){
				String className = trace[i].getClassName();
				if (className.startsWith("uws."))
					className = "<a href=\"/uwstuto/javadoc/"+className.replaceAll("\\.", "/")+".html\" class=\"javadoc\">"+className+"</a>";
				out.println("\t\t\t<tr"+((i%2 != 0)?" class=\"alt\"":"")+"><td>"+className+"</td><td>"+trace[i].getMethodName()+"</td><td>"+trace[i].getLineNumber()+"</td></tr>");
			}
			out.println("\t\t</table>");
			out.println("\t</body>\n</html>");
			
			out.close();
		}
	}
...
}

Trick !

You can test this customization by typing a bad jobs list name (for a fatal error) or a bad attribute name (for a transient error ; ex: params rather than parameters) in the URL.