Simple Exception Handling
Be Aware
This is a very easy to implement solution for handling and acting upon exceptions. For more advanced usages or fine grained exception handling see Advanced Exception Handling
When an HstComponent implementation class is executed, exceptions can happen. This can be some java.lang.RuntimeException, java.lang.Throwable, or a more domain logic kind of exception: for example, I expect some content to find for the detail page, but the corresponding HippoBean is null. Or, I expect a document of type NewsItem, but it is an AgendaItem.
Depending on your situation, you need to determine how to handle an unexpected error. For example, when the component which is responsible for showing some related documents has an error, perhaps you do not want the entire page to be a 404, but just log some warning. At the same time, when the main document you want to show on the page cannot be found, you might want to return a 404. In this latter case, you could show an empty 'slot' on the page where the document contents should be shown, or, you might want to forward the page to a different sitemap item creating a 404 page.
So, let's walk through all these examples one by one. However do realize, that even though you have quite some options here, the real power in exception handling is described in Advanced exception handling.
Use Case 1
Just log a warning or info for non critical unexpected behaviour
Suppose, I have a document, and from this document I do a lookup for some child bean, because this bean contains related documents. And this bean, happens to not be there, so on the right of the page, we cannot show related documents. However our domain model might be that we expect this bean, it also might not be critical enough to return a 404 page, as the content of the main document is just found. In that case, your HstComponent might contain code like:
Use Case 1:
public class Home extends BaseHstComponent { public static final Logger log = LoggerFactory.getLogger(Home.class); @Override public void doBeforeRender(HstRequest request, HstResponse response) throws HstComponentException { HippoBean myDocument = this.getContentBean(request); MyRelatedBean myRelatedBean = myDocument.getBean("related", MyRelatedBean.class); if(myRelatedBean) { log.warn("No related bean found where we expected one"); // continue } }
Use Case 2
A critical error / exception happend
Now, we for example cannot find the document that should be displayed in the main content slot on the page. This time, we might want to create a 404. There are at least four ways to do this.
-
hstResponse.sendError(int sc);
-
hstResponse.setStatus(int sc);
-
hstResponse.forward(path);
-
HstResponseUtils.sendRedirect(hstRequest, hstResponse, path);
Let's now inspect the four different options
1 response.sendError(int sc);
You might not want to use this one, as it continues the HstRequestProcessing for all HstComponents and finally hits the application container. Then you can only catch the error-code in your web.xml, see HST-2: 1. Handling error codes and exceptions by the web.xml
A code snippet of your HstComponent in this case might look like. So, as already stated, this does not stop the HstRequestProcessing. After the sendError, all other HstComponents are still executed
Use Case 2: poor man's solution:
HippoBean myDocument = this.getContentBean(request); if(myDocument == null) { try { hstResponse.sendError(404); return; } catch (IOException e) { // do your thing } }
2 hstResponse.setStatus(int sc)
Sets the status code on the response. The HstRequestProcessing is continued. If multiple HstComponents set the status, the last invocation will be applied.
Because multiple HstComponents can set the status code, this method is not preferred either. But you might choose it when you have some usecase for it.
Code snippet example:
Use Case 2: response.setStatus(int sc);:
HippoBean myDocument = this.getContentBean(request); if(myDocument == null) { response.setStatus(404); myDocument = this.getSiteContentBaseBean(request).getBean( "common/errorPage"); }
3 hstResponse.forward(path);
An internal forward to 'path' which is a new path that is matched to a new sitemap item. Note that the path must start with a "/", and, the forward method is short-circuiting the HstRequestProcessing: HstComponents that have not yet been executed won't be executed anymore
Thus, this method leaves the URL intact, but does an internal forward. You can for example forward to a "/error" sitemap item, creating some error page.
Code snippet example:
Use Case 3: hstResponse.forward(path);:
try { HippoBean myDocument = this.getContentBean(request); if(myDocument == null) { response.forward("/error/404"); return; } // do custom logic } catch(MyUnauthorizedException e) { log.warn(e.getMessage()); response.forward("/error/401"); } catch (IOException e) { throw new HstComponentException("forward failed", e); }
Now, there must be a sitemap matcher that can be mapped for /error/xxx, for example:
sitemap snippet:
+ error + *
where the sitemap item has for example a parametername 'error-code' and a parametervalue '\${1}', where \${1} equals the wildcard *. The HstComponent for the error sitemap item can set the error-code, for example:
response.setStatus(Integer.parseInt(this.getParameter("error-code", request)));
4 HstResponseUtils.sendRedirect(hstRequest, hstResponse, path);
An external (browser) redirect to 'path'. Note that the path must start with a "/", and, the sendRedirect method is short-circuiting the HstRequestProcessing: HstComponents that have not yet been executed won't be executed anymore
Code example snippet:
HippoBean myDocument = this.getContentBean(request); if(myDocument == null) { HstResponseUtils.sendRedirect(hstRequest, hstResponse, "/error/404"); return; }
Note that the HstResponseUtils.sendRedirect nicely handles the inclusion or exclusion of the context path, depending on the virtualhosts configuration