Page 3 of 3 FirstFirst 123
Results 21 to 30 of 30

Thread: Conceptual: context information

  1. #21
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by uenluena View Post
    Your idea would be to a have a CommonAbstractManagedBean that would have an abstract method doExecute(). My Concrete SearchManagedBean would then implement doExecute() and in different places in the managed bean, like in the doSearchButton() method i would call the doExecute() method. Was that correct?
    Something like that, yes. Unfortunately, I am not qualified to advise on JSF-specific solutions. But the idea is to find a single place where you can implement a global catch for every single uncaught exception originated anywhere within the code that is invoked by the UI component submission - to handle the error and redirect the application to the standard error view. In Struts (at least the old one, I haven't used it since then), the best place to do that would be a base abstract action class - exactly how I described it. In Spring MVC, it is much easier: you only need to implement and register an exception resolver that will be invoked by Spring if any RT exception bubbles up from the controllers or below. So, Spring listens for the exceptions for you. I showed you how to implement and register such handler in one of my earlier posts in this thread. Check if JFS has some generic exception-listening mechanism. If it doesn't, there you have another reason not to use JSF...
    Last edited by constv; Dec 1st, 2008 at 01:53 PM.

  2. #22

    Thumbs up

    Sooo, constv....i really enjoyed exchanging ideas with you and i had after some time of tests, implementations...conceptual work my first discussion with my (IT) manager, that came with additional requirements because of the scope of our project that has been widened...

    I think we both share the same idea about exceptions that are catched at DAO level, namely SQLExceptions...Until today all of those exceptions were wrapped to Unchecked exceptions and went up the ladder...

    Today i got introduced to some legacy systems, where 99.9999~ of the whole business logic is in the DB and they have this special java-like-stacktrace concept that they build into PL/SQL.

    This stack trace actually more a "message"-stack-trace as it has a structure like that:

    Error-Code1 - Context information
    Error-Code2 - Context information

    e.g
    3234 - Couldn't search for the contract
    3466 - The end date is before start date


    You can clearly see that NONE of this so called "exceptions" are really exceptions but much more validations. Whatever, i tried to justfy my point for over 3hours but it's hard to explain them my point when they are actually a part of the problem and mainly those who build up such a structure...

    So the idea would be that i would have a strategy before wrapping those runtime exceptions, where i have to decide because of the error code if it's an DB-Technical exception or a Business Exception coming from the DB.

    My idea would be to throw Runtime Exceptions anyway and do check for the type only for throwing two different Runtime Exceptions. One would be a DBException, which is a technical rte and a DBBusinessException rte for business specific DB exceptions.

    With the those two rte's, the technical exceptions would go up the ladder and at controller level i would catch them and act properly and the business exceptions would be catched at service layer.

    The reason why we need to catch them at service level is because my manager put another constraint in place, as he saw use cases where a service could call different PL/SQL procedures, through different DAO methods and act according the so called "business exceptions". So for him the existance of the service layer was vital (which was the case for me from the beginning).

    So i have my multi tier application having business logic in a seperate Service layer + business logic in the PL/SQL world. We agreed on a solution where all commonly used functionalities should be encapsulated in Web Services and provided through contract to the consumers...

    What do you think about that? I mean..i really came out of the room with lots of headache as he was not able to understand and every sentence ended with "....yeaaaah you java developers just dont see the real business", which made me really raging and most of the concepts i defended are commonly accepted practices put in place in different companies and are widely known as correct (btw. i bought and read your recommended book Clean Code and enjoyed every single page of it ;-) - i was just not able to force him to look at it ;-D)

  3. #23

    Default

    any input to my last post?

  4. #24
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Hi uenluena,
    sorry for the delay, and sorry to hear about your situation. I know how frustrating it may feel when you are working with people like that. Since there is not much you can do to change that, you have to find the most elegant solution to deal with that while abstracting your application from the stupid things that other people have done. It sounds you are on the right track. I agree, you need to catch those crazy DB-originated checked exceptions, distinguish the two types (system/programming error or validation cases) and re-throw them as two distinct RTEs to ensure that they don't stink up any of your application code - until the point they reach your handler on the front end. I am not sure why you are suggesting catching the data field validation cases like your "end date before start date" - in the service. Why? What can your service do about that? It seems to me that this information should freely propagate to the front end where a message would be resolved (or extracted, if it is already in the exception created by the DB developer - which is bad, as I had explained!) and presented to the user. As I have said before, an exception instance must never carry any messages intended for the end user. If your co-workers are doing that they are incompetent, or, at least, seriously misinformed on exceptions. Exception-stored (hard-wired) info is for diagnostics only. Exceptions must be mapped to application-specific user-friendly messages separately!

    That said, I would put the catching and re-throwing of your exceptions into an aspect, which would consolidate that logic and make your DAOs "unaware" of the ugliness. Also, introduce an aspect on the front-end that would catch and handle them - without exposing them to the controllers.

    HTH. Good luck,
    Constantine

  5. #25

    Wink

    Im back!


    After several weeks of holidays and some military (yeah...in switzerland we're still forced to do military each year ;-D) i found again some time to work on the problem.

    We have some progress...

    At first the leader of another development group introduced a Java Exception Stack-like concept to the DB which most of the DB-developers use now...

    So, when i work with some Business Logic inside the DB, the exception from the PL/SQL procedure call stack comes through JDBC as a SQLException.

    Im using the SQLErrorCodeTranslator in Spring to get our Business Specific Oracle Error Code (must be between 20000-29999) and wrap them to a DatabaseBusinessException...

    What i get from the DB is actually XML holding exception entries in it, with error codes and context parameters. I parse them one by one and transform them to DatabaseBusinessException (rte) which are resulting in a Java Exception Stack and not the original " Error at line 5 of the blah package" message.

    Every other exception coming from the DB is rethrown as a DataAccessException (rte)

    My first idea was to stick to the idea that the DatabaseBusinessException is not catched as you proposed in the Service Layer...i face the problem that i can try to explain in an example:

    my service method does insert a user which results in several PL/SQL procedure calls. Now, the 6. of this calls raises an exception and each of the caller are adding or propagating to this exception in the DB.

    So, when the exception comes up to my exception "barrier" in my web level, i have 6-7 exception stack elements and as i have business logic in the PL/SQL procedures i cant do a generic try/catch as i do with normal rte's.

    I would have to give the user a quiet specific error message like "sorry, but your username already exists".

    Maybe the first or the last stack element has this information in the stack, so i would have to get this first/last message and translate it to the user...

    Do you see any possibilities to solve such a problem?

  6. #26
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Hi, uenluena,

    my service method does insert a user which results in several PL/SQL procedure calls. Now, the 6. of this calls raises an exception and each of the caller are adding or propagating to this exception in the DB.
    I am not sure I clearly understand what you are describing. Are you saying that those several SP calls are executed in a sequence - regardless of whether any of them fails and throws an exception? So, depending on the combination of the results of those calls, your UI must produce the appropriate error message to the user? Did I understand you correctly?

    Assuming that's exactly what your situation is (and setting aside any questions about the validity of the original design that is out of your control), here's a thought... At the point where you are translating the SQL exceptions and parsing the whatever stuff is inside them - since you are doing that anyway - you might want to construct a "business condition code" out of all the codes of the underlying exceptions you have extracted. These exceptions, as you have stated, represent real business conditions, like "username already exists." So, if someone had come up with such a crazy deign that a legitimate business condition is represented by a certain combination of SQLExceptions (again, if I understood you correctly, and if so, your system was designed by maniacs!!!) - then, it seems, the best thing you can do is consolidate that bizarre bunch of error codes into a single code and map it to a UI message on the front-end. You could:

    1) Create some com.yourcompany.commons.BusinessException (RTE) - whose constructor takes the message, and cause, and the business use case regex. (The only reason for that is because your organization uses DB-born exceptions to signal business conditions. Otherwise, you know I don't like the idea of passing any kind of codes in exceptions, or using exceptions for legitimate business use cases all together.)

    2) Extract all individual error codes (at the very first line of defense, where you do the SQL exceptions translation) and construct, say, a regular expression out of them that would uniquely identify each use case;

    2) Wrap the original SQLException (with all the stacked crap that is in it - so you don't lose any valuable info for logging) into your new custom service-specific business RTE that extends your com.yourcompany.commons.BusinessException.

    3) On the application level, provide a configuration file and a mapper class that would map those regex's to user-friendly UI messages.

    4) Let the business exception freely propagate to your handler at the very top, in the front-end. That handler, before catching Throwable, would catch any instances of your BusinessException, extract that stupid use case code, run it through the code resolver and retrieve the appropriate message that would be passed to the view.

    I wouldn't put any notion of a legitimate business use case into a DataAccessException. A DataAccessException should only mean one thing: something is really not working with your data access, not that the user name already exists, etc. You get my point... So, that's why, I think you will need a service-specific "business" RTE to communicate those conditions. I don't know much about your system, but, generally, I would prefer to return such use case code as some kind of status code inside the object returned by the service API, and use exceptions to only indicate errors. The client may inspect the returned object's status, e.g. insertUser(...) would return an object, like an update User object. You could have a status property on that object that would indicate "newly created", "approved", whatever, or "error/code". It may be not applicable to your particular use cases/system, I don't know, but I always consider such possibilities before I decide to use an exception for a business case.

    HTH,
    Constantine

  7. #27

    Default

    i get your point :-)

    An idea would also be to transform all the stack elements from the XML into "PlSqlStackElement"' objects which are encapsulating each stack element.

    I could then put all this elements to the DatabaseBusinessException (rte) with an addPlStackElements() as part of the Exception object.

    This exception would be thrown in the custom SQLErrorCode Translator class and be catched in the Service layer or at the exception barrier and the developer would have the choice between adding a custom error message into the ErrorMessage object, which is displayed in the UI or picking the first/last stack element from the PL/SQL Stack element list and translate it with the right language (as we know the language setting at the web layer).

    So the PL/SQL stack elements would be transported inside the DatabaseBusinessException object up the ladder...

    What do you think?


    p.s i really enjoy discussing my ideas with you as i think that they are more then constructive and im really thankful for all your support so far :-)

  8. #28

    Default

    really getting mad here...

    Now i have my DatabaseBusinessException where i have the PL/SQL exception stack elements attached with an ArrayList.

    So when i first get the error stack from the DB i do the following:


    Code:
        private DataBaseBusinessException handleDBBusinessException(String errorMessage) {
    	List plStackTrace = PlSqlStackTraceParser.parse(errorMessage);
    
    	DataBaseBusinessException dbbe = new DataBaseBusinessException("Some Business Errors happened in the DB - Please check the PL/SQL Stack trace") ;
    	dbbe.setPlsqlExceptionStack(plStackTrace);
    	
    	return dbbe;
        }
    So the only stack element i see in the java exception stack is this message, which is just read by the developers.

    Now comes the problem....after throwing this runtimeexception it gets caught by a JSF based class, that is trying to resolve the Expression Language element in my jsp.

    To make it understandable...to have a better i18n i have created an annotation with a custom Property/Variable resolver in JSF that is able translate the following syntax.

    MySimpleService.myBean.myattribute

    JSF then calls the service method getMyBean() and resolves the myattribute attribute

    As an exception is happening in the SQLErrorCodeTranslation the EL-Resolver of JSF is automatically throwing the javax.faces.el.EvaluationException
    and the portlet PortletException etc.

    That just means that i can never be sure that my exception that i threw is the last one or not and have to iterate programaticall through the error
    stack and do some instanceof's to get my DatabaseBusinessException. Im i dreaming or do i really have to get that far? ;-D

    I just wanted to show you the error stack (shortened as it exceeded the character limit):

    Code:
    [12.02.09 14:16:51:718 CET] 00000083 jsf           W com.sun.faces.application.ViewHandlerImpl getFacesMapping Unable to determine FaceServlet mapping for servlet path '/portlet'.
    [12.02.09 14:16:51:734 CET] 00000083 jsf           W com.sun.faces.application.ViewHandlerImpl getFacesMapping Unable to determine FaceServlet mapping for servlet path '/portlet'.
    [12.02.09 14:16:51:734 CET] 00000083 jsf           W com.sun.faces.application.ViewHandlerImpl getFacesMapping Unable to determine FaceServlet mapping for servlet path '/portlet'.
    [12.02.09 14:16:57:359 CET] 00000083 PropertyAware I   Setting up the connection for further use...
    [12.02.09 14:16:57:406 CET] 00000083 ServletWrappe E   SRVE0068E: Die Methode service() für das Servlet /XPOPublicSampleView.jsp konnte nicht aufgerufen werden. Ausgelöste Ausnahme: javax.servlet.ServletException: javax.faces.el.EvaluationException: Error getting property 'sampleBean' from bean of type pagecode.sample.XPOPublicSampleView: ch.xxxxxxx.xnet.fwk.exception.DataBaseBusinessException: Some Business Errors happened in the DB - Please check the PL/SQL Stack trace
    	at org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:648)
    	at com.ibm._jsp._XPOPublicSampleView._jspService(_XPOPublicSampleView.java:117)
    	at com.ibm.ws.jsp.runtime.HttpJspBase.service(HttpJspBase.java:93)
    	at javax.servlet.http.HttpServlet.service(HttpServlet.java:856)
    	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1696)
    	at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:825)
    	at com.ibm.wsspi.webcontainer.servlet.GenericServletWrapper.handleRequest(GenericServletWrapper.java:121)
    	at com.ibm.ws.jsp.webcontainerext.JSPExtensionServletWrapper.handleRequest(JSPExtensionServletWrapper.java:216)
    	at com.ibm.ws.webcontainer.webapp.WebAppRequestDispatcher.include(WebAppRequestDispatcher.java:670)
    	at org.apache.pluto.core.impl.PortletRequestDispatcherImpl.include(PortletRequestDispatcherImpl.java:112)
    	at com.ibm.faces.portlet.httpbridge.PortletRequestDispatcherWrapper.include(PortletRequestDispatcherWrapper.java:61)
    	at com.ibm.faces.portlet.httpbridge.PortletRequestDispatcherWrapper.forward(PortletRequestDispatcherWrapper.java:35)
    	at com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:325)
    	at com.ibm.faces.portlet.httpbridge.PortletExternalContextWrapper.dispatch(PortletExternalContextWrapper.java:83)
    	at com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:262)
    	at com.ibm.faces.portlet.PortletViewHandlerImpl.renderView(PortletViewHandlerImpl.java:74)
    	at com.ibm.faces.portlet.PortletViewHandlerImpl.renderView(PortletViewHandlerImpl.java:74)
    
    
    
    ---- Begin backtrace for Nested Throwables
    javax.faces.el.EvaluationException: javax.faces.el.EvaluationException: Error getting property 'sampleBean' from bean of type pagecode.sample.XPOPublicSampleView: ch.xxxxxxx.xnet.fwk.exception.DataBaseBusinessException: Some Business Errors happened in the DB - Please check the PL/SQL Stack trace
    	at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:186)
    	at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:137)
    	at javax.faces.component.UIOutput.getValue(UIOutput.java:147)
    	at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:84)
    	at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:211)
    	at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:171)
    	at com.ibm.faces.renderkit.DefaultAjaxRenderer.encodeEnd(DefaultAjaxRenderer.java:83)
    	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:762)
    	at javax.faces.webapp.UIComponentTag.encodeEnd(UIComponentTag.java:604)
    	at javax.faces.webapp.UIComponentTag.doEndTag(UIComponentTag.java:527)
    	at com.sun.faces.taglib.html_basic.OutputTextTag.doEndTag(OutputTextTag.java:202)
    	at com.ibm._jsp._XPOPublicSampleView._jspx_meth_h_outputText_0(_XPOPublicSampleView.java:178)
    	at com.ibm._jsp._XPOPublicSampleView._jspx_meth_h_form_0(_XPOPublicSampleView.java:399)
    	at com.ibm._jsp._XPOPublicSampleView._jspx_meth_hx_scriptCollector_0(_XPOPublicSampleView.java:449)
    
    
    Caused by: javax.faces.el.EvaluationException: Error getting property 'sampleBean' from bean of type pagecode.sample.XPOPublicSampleView: ch.xxxxxxx.xnet.fwk.exception.DataBaseBusinessException: Some Business Errors happened in the DB - Please check the PL/SQL Stack trace
    	at com.sun.faces.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:90)
    	at com.ibm.faces.databind.SelectItemsPropResolver.getValue(SelectItemsPropResolver.java:41)
    	at ch.xxxxxxx.xnet.fwk.jsf.resolver.CommonPropResolver.getValue(CommonPropResolver.java:47)
    	at com.sun.faces.el.impl.ArraySuffix.evaluate(ArraySuffix.java:167)
    	at com.sun.faces.el.impl.ComplexValue.evaluate(ComplexValue.java:151)
    	at com.sun.faces.el.impl.ExpressionEvaluatorImpl.evaluate(ExpressionEvaluatorImpl.java:243)
    	at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:156)
    	at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:137)
    	at javax.faces.component.UIOutput.getValue(UIOutput.java:147)
    	at com.sun.faces.renderkit.html_basic.HtmlBasicInputRenderer.getValue(HtmlBasicInputRenderer.java:84)
    	at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.getCurrentValue(HtmlBasicRenderer.java:211)
    	at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeEnd(HtmlBasicRenderer.java:171)
    	at com.ibm.faces.renderkit.DefaultAjaxRenderer.encodeEnd(DefaultAjaxRenderer.java:83)
    	at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:762)
    	at javax.faces.webapp.UIComponentTag.encodeEnd(UIComponentTag.java:604)
    	at javax.faces.webapp.UIComponentTag.doEndTag(UIComponentTag.java:527)
    	at com.sun.faces.taglib.html_basic.OutputTextTag.doEndTag(OutputTextTag.java:202)
    	at com.ibm._jsp._XPOPublicSampleView._jspx_meth_h_outputText_0(_XPOPublicSampleView.java:178)
    	at com.ibm._jsp._XPOPublicSampleView._jspx_meth_h_form_0(_XPOPublicSampleView.java:399)
    	at com.ibm._jsp._XPOPublicSampleView._jspx_meth_hx_scriptCollector_0(_XPOPublicSampleView.java:449)
    	at com.ibm._jsp._XPOPublicSampleView._jspx_meth_f_view_0(_XPOPublicSampleView.java:476)
    	at com.ibm._jsp._XPOPublicSampleView._jspService(_XPOPublicSampleView.java:110)
    
    
    
    
    Caused by: ch.xxxxxxx.xnet.fwk.exception.DataBaseBusinessException: Some Business Errors happened in the DB - Please check the PL/SQL Stack trace
    	at ch.xxxxxxx.xnet.fwk.exception.CommonSQLErrorCodeTranslator.handleDBBusinessException(CommonSQLErrorCodeTranslator.java:28)
    	at ch.xxxxxxx.xnet.fwk.exception.CommonSQLErrorCodeTranslator.customTranslate(CommonSQLErrorCodeTranslator.java:19)
    	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.translate(SQLErrorCodeSQLExceptionTranslator.java:230)
    	at org.springframework.orm.ibatis.SqlMapClientTemplate.execute(SqlMapClientTemplate.java:212)
    	at org.springframework.orm.ibatis.SqlMapClientTemplate.update(SqlMapClientTemplate.java:411)
    	at org.springframework.orm.ibatis.SqlMapClientTemplate.update(SqlMapClientTemplate.java:405)
    	at ch.xxxxxxx.xnet.sample.dao.SampleDAO.getBusinessException(SampleDAO.java:20)
    	at ch.xxxxxxx.xnet.sample.service.SampleService.getBusinessException(SampleService.java:22)
    	at pagecode.sample.XPOPublicSampleView.getSampleBean(XPOPublicSampleView.java:39)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:85)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:58)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:60)
    	at java.lang.reflect.Method.invoke(Method.java:391)
    	at com.sun.faces.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:80)
    	at com.ibm.faces.databind.SelectItemsPropResolver.getValue(SelectItemsPropResolver.java:41)
    	at ch.xxxxxxx.xnet.fwk.jsf.resolver.CommonPropResolver.getValue(CommonPropResolver.java:47)

  9. #29
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Hi uenluena,

    I have to admit I am reluctant to even try to understand what exactly is going on there. However, as I have said before, one should not use any data inside an exception class to handle business cases. It is just incorrect. That is not what exceptions are for. Such data is for diagnostics only. And exceptions are not meant to signal use cases in general. They should be used to transport the error data and nature to the designated handler. (Hence, checked exceptions are nonsense because they do not allow to transport anything - by stopping the exception immediately.) It may be ok for the handler to redirect to a certain business use case based on the nature of the error, but not based on the error detail/ID/Key/description/stack trace/etc. encapsulated within the exception. The latter is bad design and misuse of exceptions. The fact that many people do that, and many books advocate that, doesn't make it right. Most books focus on explaining other things and error handling in their examples is nothing more than an afterthought, something of little importance, something they just add at the last moment. Unfortunately, I have not seen a single Java book that has a good chapter on exceptions or error handling. The better frameworks (e.g. Spring) that implement proper exception management do not go to a great length explaining the rationale. It's not their job really. It's a framework, not a Java tutorial. Most Internet articles on exceptions are extremely misguiding and only help to spread confusion and misconceptions.

    Sadly, most new programmers these days start by learning technologies instead of understanding the basic principles first - or ever. Such approach, unfortunately, has an implication that the technology should be taken as a given, almost as a flawless concept. That, in turn, results in many people (SEs and managers) mistakenly viewing programming as a process reduced to using the given set of technologies and recommended recipes (patterns) - rarely questioning the validity of the technologies themselves. Such people may debate whether a specific technology is applicable to the given project or not, but only the best few actually go as far as analyzing the technology itself and the fundamental principles it is built upon, whether its complexity is justified, etc. That's why the industry was stuck with horrendous things like EJB, JSP, checked exceptions, etc - for so long. All those things were designed so that they all but enforced complexity, poor programming practices, and guaranteed mistakes to be made left and right. And yet, the majority of Java programmers have been blindly following the suit - like cattle to the slaughter. Not all, fortunately. That's why we have Spring, for one...

    Back to your issue. See what's been happening? Instead of programming your business logic, you have already spent several months struggling with exception handling! The design those people have imposed on you is a great example of how hugely error handling is misunderstood. Considering that you are dealing with someone else's design that you can't change, you should focus on isolating the bad design and abstracting it from the rest of the application - not propagating it all the way. This means the following...

    1) If some of the DB exceptions in your application in reality mean legitimate use cases, catch those exceptions as early as possible - i.e. in your data access tier, and translate them into meaningful [to the business tier] things. It's up to you to decide what it would be. As we have discussed, you may parse each exception (each combination of that horrendous "stack" of exceptions you mention) and map each combination to an enum type that represents a legitimate status of a db operation. Return that status [as part of the returned object, perhaps] from your service API call. Don't let any such "exceptions" propagate. On your front-end, analyze the status and act appropriately - without dealing with exceptions in such cases at all.

    2) If the DB exception means a real data access error, wrap it into a RTE and re-throw all the way. Don't bother with details, just display a generic error message but log the whole exception stack trace. There's nothing else you can do.

    When you create a new exception that will wrap an original exception, always use a constructor that takes the cause! I see you are creating a new exception ignoring the cause and then just add the stack trace retrieved from the cause. That's not good.

    And finally, keep in mind that nothing in the front-end should ever even know about the existence of the data tier. The words like SQL or DB should not show in any front-end class names. Your front-end is only exposed to the business tier that may or may not have a database underneath - for all you care. So, your front-end catch blocks must not specifically bother with any exceptions that have "SQL" in their class names. (Anything of that sort should be cought by your "catch Throwable".) Just look for some instances of your business exceptions - if you really need and can do something different about those, and, ultimately, any Throwable. That brings us back to my point: any use cases represented - foolishly! - by DB exceptions absolutely must be converted into meaningful "business" (non-DB) concepts before they reach the client.

    BTW, unless we discuss general architecture issues that may be of interest to other people, I think it may be more appropriate to use "private messages" or emails if the conversation gets too focused on one particular project.

    Cheers,
    Constantine
    Last edited by constv; Feb 12th, 2009 at 11:06 AM.

  10. #30
    Join Date
    Mar 2008
    Posts
    261

    Default

    I did a personal text memo, for my own use, that I have not published anywhere. It is made of the excellent posts of Constv in this thread.
    It is mostly copy paste with some rewording of the above posts among others. Credits for its value go to Constv. Potential mistakes are mine.

    I had edited this text before finding out about the blog article of Constantin Vasilyev on the subject:
    http://articles.vconst.com/2009/08/e...s-in-java.html
    which does a much better job at explaining the history, rationale and best practice with error handling than my cheap text memo.

    But there you are, my text memo:

    An exception is not an error code, but rather a mechanism designed to ease error handling, without having to deal with the error until it gets to its dedicated exception handler, often away from the origin of the error.

    There are two types of exceptions, checked exceptions and unchecked exceptions.

    Checked exceptions need to be caught or re-thrown. This polutes the application with unnecessary source code.
    With a checked exception, the compiler forces an implementation, as the source code will not compile if a checked exception is not caught or re-thrown.
    This results in the exception being exposed in places it has nothing to do, exposing implementation details that should remain hidden, and with the risk of the exception being swallowed or mishandled.
    Checked exceptions have another side effect. They imply that those checked exceptions that must be handled, are the only ones that will ever occur. To think that no other unchecked exceptions will ever happen is a risky bet. This misconception that only these checked exceptions should be handled, results in the actual handling of a subset of all the possible exceptions, and makes the application less safe.

    Unchecked exceptions do not need to be caught nor re-thrown. With an unchecked exception, the application developer can decide if, where and how to react to the exception. The exception is only exposed when needed. It is not exposed through out the methods of the call stack, in some source code that has no business in doing anything about the exception, like mishandling it instead of letting it bubble up to its appropriate handler.

    So, use unchecked exceptions and avoid checked exceptions !

    Implement the exception handler where it belongs to, that is, in the component that actually knows what to do with the error, and let the exception freely bubble up to the exception handler.

    Have few centralized exception handlers, and no error handling code anywhere else in the application. Errors must travel freely through the call stack, up to the exception handler.

    Exceptions must keep the complete information. Never discard the stack trace and never replace it with a custom error messages.

    The controller is a good place to have the exception handler, as it has all the context to know what to do with the exception. It catches all bubbled-up unchecked exceptions, it can handle some of them based on some business cases of the application, and redirect the rest of them to a system error page, displaying just a user friendly message.

    The front-end controller is a good place for the generic exception handler that handles all exceptions and logs them in a text file. The only responsibility the generic exception handler has, is to log the data, whatever it is, without inspecting it, and let the application proceed whichever way is appropriate for a system error. It is the responsibility of the service that threw these exceptions to supply any information that should be logged by the generic exception handler that waits up the call stack. The front-end controller must therefore have a catch on the base contextual exception class, that will catch all exceptions extended from the base contextual exception class.

    The exception handler can also be in a service or even in a data access tier (Dao). For example, if the Dao has enough knowledge to interpret a specific sql error, then it's okay for the Dao to catch that checked or unchecked exception. The Dao must then wrap it into a more meaningful unchecked exception with a readable message. But the Dao cannot recover from the exception. It can only help clarify the error. The new unchecked exception must contain the nested original exception, and it must reach the exception handler in the controller. The user is not interested in what exactly happened, if there is no business case for that condition. The user must simply be redirected to an error page and the exception logged. The complete stack trace with all the additional information supplied by the Dao that wrapped the original exception can then be logged.

    If some exception represents some legitimate business condition, then the service or Dao must catch that exception, and reflect the condition in the state of some business object. Alternatively, it may re-throw a business specific unchecked exception, which the controller must catch so as to redirect to take appropriate action. In that latter case, do not add any kind of data, like some error code, into the exception for later handling by an exception handler in the controller. Exceptions are not error codes and their handlers must expect at most a particular type of exception class and they should not need to check for anything else. If a handler catches a certain type of exception, it may expect to be able to call certain methods on the caught exception, but it should do it blindly without worrying what data is actually stored inside the exception. For example, the handler may retrieve the message or the stack trace, but it would be very risky to inspect the message string for some specific content and act upon it.

    The exception handler must always log the full exception stack trace. The exception handler should also log some application context. The context usually is in the user session, accessible by the controller. To avoid having additional contract between the controller and the service and not pass the application context back and forth between them, the application context must be stored in the exception. The exception then extends a base contextual exception class that can accept any arbitrary object so as to store the application context. The service can throw some unchecked exception that extends that base contextual exception class. When throwing an exception in the service, some application context available in the service can be stored in the exception. And some additional application context can later be stored on the exception by the controller.

    The base contextual exception class has a method that simply writes out a string representation of the context stored on the exception, and is placed in some common package.

    The only factor that should determine where to handle errors in the application is the specific requirements of the application. In other way, based on what behavior of the application is needed, in case of any error, decide which source code in the application has sufficient contextual knowledge to handle the error. This generally means that all exceptions in the middle tier, that is, the service and Dao, must freely propagate to the controller, that would ultimately handle them. To ensure this behavior, all the exceptions must be unchecked. This means that any checked exception in the service must be caught, wrapped into some clarifying unchecked exceptions with added messages, and re-thrown, only to be caught and handled by the designated handler in the controller, since only the latter would know what exactly the application needs to do with those errors. In some cases, an unchecked exception may be caught, wrapped and re-thrown if this can add valuable information to it on the way. But no matter what, never discard the original exception, and never replace the valuable stack trace with an error message. Always keep the full original stack trace and nested exception, because they provide the most important infomtionabout the source of the error.

    As for Dao exceptions, ideally, these should not be caught in the Dao but in the service, where more contextual data can be added, before being re-thrown. A service specific unchecked exception, wraps the original exception that came from the Dao and adds the service specific context to it. Any Dao exception is a system exception, something that should not happen. So, if it happens, from the standpoint of handling, it carries no business condition, it's bad, and its handled by the generic exception handler. The service, before re-throwing it, will add whatever information is appropriate to it, so as to be read by the developer. This is a good case for an aspect, to automatically place all the service method's arguments into the exception as context. That allows for a single aspect around every public method of every service to ensure that any throwable inside the method will be caught, wrapped into an instance of a service exception, augmented with the method's input arguments, and re-thrown. The generic exception handler will have a catch for the base contextual exception, log everything, and redirect to the error page. If the context needs to be more specific for a particular service method, then a try/catch/throw must be implemented in the service, but that would not be very pretty.
    Stephane

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •