All - >Regarding the declarative exception handling, never really heard much >since the last posting I made about it. But in that I proposed keeping >the default perform signature and adding another signature that could be >used to accomplish the try{}catch(Exception ){} semantics I was looking >for. Basically the base Action perform would call the lower level hook >and handle any exception by making it the root of a SevletException to >be handled by the Servlet. I really think this keeps existing Struts >apps from breaking while providing a more flexible environment for >future development.
the solution proposed by Laine sounds great and it would be nice if it would be introduced in the new Struts release. I'll try to summarize (my ;-) general knowledge about exceptions and discuss some scenarios of their handling. Let's have a look on exceptions from the point of view of EJB container. It divides all exceptions in 2 big categories: 1) application exceptions (derived from java.lang.Exception) Application exceptions does not cause the EJB container to rollback the transaction giving client the opportunity to retry the operation Examples of application exception are standard EJB exceptions like: CreateException, FinderException, ObjectNotFoundException, etc. All user defined exceptions derived from java.lang.Exception are also considered as application exceptions. User / password mismatch is a typical example of such exception. 2) runtime exceptions (derived from java.lang.RuntimeException) This exception do cause the EJB container to rollback the transaction. Examples of this exceptions are EJB standard EJBException and such exceptions as NullPointerException, ClassCastException, etc. --- Now. how should all this exceptions be handled by the client. I suppose, that all runtime exceptions would cause logging the exception and forwarding to the general error page with smth like "Oops, Houston, we have a problem..." All application exceptions would normally force the user to stay on the same page, display some error message and let hime a chance to correct them, for example to specify valid password. In the project I am envolved at the moment we defined 2 base exception classes: my.package.AppException and my.package.MyRuntimeException. All other exceptions derive from this two exceptions. Most of all we don't need to derive, but just use the exceptions as is, for example: throw new my.package.AppException("error.password.mismatch"); throw new my.package.AppException("database.load", "myDatabase"); throw new my.package.MyRuntimeException("What the hell we are doing here"); It is quite obvious, that the text for this exception comes from application resources file, well we have a separate file for that purpose. This basis exception classes come with some goodies, like formatting of stack trace, and handling of nested exceptions, so the client can know the origin of the exception, even if original exception was thrown somewhere deep in EJB. The exceptions are similar to weblogic NestedException and NestedRuntimeException. We have managed to get rid of exception handling in Action classes - there is no try / catch block there - in the way, which was proposed by Laine, but programmatically. We have introduced an Action class of our own: (Coding conventions was not my invention ;-) protected abstract ActionForward doPerform( ActionMapping oMapping, ActionForm oForm, HttpServletRequest request, HttpServletResponse oResponse) throws IOException, ServletException, BspAppException; public ActionForward perform(ActionMapping oMapping, ActionForm oForm, HttpServletRequest oRequest, HttpServletResponse oResponse) throws IOException, ServletException { HttpSession oHttpSession = oRequest.getSession(); // Check if we have a new HttpSession. // It can happen only if is was expired or tomcat was restarted. if (oHttpSession.isNew()) { addException(new BspAppException("ERR_INVALID_HTTP_SESSION")); return oMapping.findForward("logon"); } ActionForward oAction = null; try { // Clear old messages. this.clearMessages(oRequest); // Call the overridden method. oAction = doPerform(oMapping, oForm, oRequest, oResponse); } catch (BspAppException e) { String sMessage = e.getMessage(); // Add exception to the message list. this.addException(e); // Check for some specific exceptions. if ("ERR_JMS_RECV_TIMEOUT".equals(sMessage)) { s_oLog.warn(e); // Save the complete request and ask user to retry. this.saveRequest(oRequest); oAction = oMapping.findForward("retry"); } else if ("ERR_INVALID_HTTP_SESSION".equals(sMessage)) { s_oLog.warn(e); // Force user to logon. oAction = oMapping.findForward("logon"); } else if ("ERR_INVALID_HOST_SESSION".equals(sMessage) || "ERR_INVALID_APP_SESSION".equals(sMessage)) { s_oLog.warn(e); // Save the complete request and force user to logon. this.saveRequest(oRequest); // Remove user key, to let user to login again. oRequest.getSession().removeAttribute(Constants.USER_KEY); oAction = oMapping.findForward("logon"); } else { // This is an application exception, user have to retry. // We don't need to log this exception. if (s_oLog.isDebugEnabled()) { s_oLog.debug(e); } // Do we have an exception mapping ? oAction = oMapping.findForward("exception"); if (oAction == null) { // Stay on the same page. oAction = new ActionForward(oMapping.getInput()); } } } catch (Exception e) { // This is a real problem. Forward to error page. s_oLog.error(e); oAction = oMapping.findForward("error"); } // If due to some reasons oAction equal to null, forward to empty page. if (oAction == null) { s_oLog.warn("oAction = null"); oAction = oMapping.findForward("empty"); } // Save messages in request (if they exist of cource) this.saveMessages(oRequest); if (s_oLog.isDebugEnabled() && oAction != null) { s_oLog.debug("perform: forward name=" + oAction.getName() + " path=" + oAction.getPath()); } return oAction; } There is nothing special about this class exception special handling of some application exceptions, like expired host session or JMS problem. Don't blame me for using strings. We have to support host exception from Cobol applications, which come to us as Strings through XML protocol. And now back to Laine's solution. I really like it very much, but I am not not sure, that application exceptions should be wrapped as ServletException. I would like to get rid of handling exceptions and any kind of wrapping in Action classes. I see no harm in introducing of execute method, which throws Exception. All old applications would still work, and all new applications can use new feature. Configuration of exceptions, proposed by Laine expects mapping between key and exception. And what do you think about regarding the exception message as a key and search for it in application resources ? If yes, then it would be not bad to provide a couple of basic exceptions, like AppException and RuntimeException. It is not wise to reinvet the wheel. I have a couple of wishes more, they have nothing to do with Lanes's proposal: 1) extend html:errors tag with format attribute: <html:text property="password" size="16" maxlength="16"/> <html:errors property="password" format="short"/> provided application resources contain: error.short=<font size='4' color='#B22222'><b>!</b></font> it would cause "!" to appear beside the field in case the password field contains an exception. It is not always reasonable to show the whole text. 2) it would be not bad to have a chance to specify the list of "must" fields in action configuration (similar to "in role" / "not in role") proposal, so struts could handle exception like "Please fill in all required fields" automatically. Personally I prefer this way instead of having list of errors like "missing field one", "missing field two" and so on. Regards, Dmitri Valdin -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>