Announcement Announcement Module
Collapse
No announcement yet.
Conceptual: context information Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Conceptual: context information

    Im thinking about a concept to keep contextual information in my portlet applicaiton, which uses spring and jsf.

    The basic idea is that there's a context object where each developer can add contextual information into it. Means that through the layered application which has the following structure

    JSF/Portlet --> Service Layer (Managed by spring) --> DAO Layer (Manager by spring)--> ORM layer (iBatis + Spring Support classes)

    Each developer should have the possibility to add contextual information from each level.

    In case of an exception this context information should be gathered and added to the error message. In case of checked exception, this is quiet easy, as each layer can add contexual information through the exception call stack. The core problem are runtime exceptions.

    As im working in the insurance business it's vital that each exception, even runtime exceptions are providing us some minimal context information, as we need to trace the problem, which can be quiet problematic.

    I dont see any reason which we should have our applications filled with checked exceptions just to have better contextual information about the problem.

    I see different scenarios which i would like to discuss with you guys.

    1. having a Context object as part of the contract between the layer calls.

    2. Having a threadlocal that keeps those information...


    what other possibilities do you see?

  • #2
    A couple of things... In the presentation tier, you, obviously have all your context in the user session context. If your middle-tier services add other parameters that you want to trace, you may pass you custom context object (defined in your domain model, not the HttpSession object, of course ) to all your service API and, from then on, attach it to your ThreadLocal.

    However, make sure you don't over-engineer things. Think carefully. In many cases, all you need to see what exactly happens is a complete stack trace of the original unaltered exception. Checked exceptions all but guarantee that you will be losing that info more than often. Use proper exception handling. That is, implement exception handlers where they belong - on the boundaries of the components that actually know what to do with the error, and let those exceptions freely bubble up to those handlers. For that you absolutely need unchecked exceptions. They make life easier, as soon as you understand that exceptions - in the first place - are not error codes, but rather a mechanism designed to ease error handling away from the origin of errors but rather where it is most appropriate, without having to mess with the error info until it gets to the dedicated handler. If you use proper exception handling, you will end up with very few centralized handler and no error-handling code anywhere else in your system. Errors will be freely traveling through your call stacks to the handler with the complete information. Analyze stack traces, and never discard them, never replace them with "error messages." The latter is just wrong and very dangerous.

    Here's something off the top of my head... If you have a web application, in most cases (according to what you have described), you would have an error handler in the presentation tier that would catch all bubbled-up RTEs, perhaps handle one or two distinctly, and redirect the rest of the Throwables to the generic system error page - with just a user friendly message. At the same time, the handler will log the full exception stack trace + the context to the logger destination (including the file system and, possibly, database if required.) You will take some of the user/application context data that you find necessary from the user session, that's easy. Instead of introducing additional contracts with the service APIs and passing the context object back and forth, to get the context from your MT services you can do something like this... You can implement some base RT exception class that allows setting a context - arbitrary objects - on it. Place that class in some common package. Your services should then throw custom service specific RT exceptions that extend that exception class. So, when your developers throw an exception within the service, they may first set the contextual objects on the exception. The class may provide some sort of getContextInfo() method that would simply write out the toString representations of all application objects the developer stored in the exception instance before throwing it. Here's a very rough implementation of such exception class:

    Code:
    package com.yourcompany.common;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * This exception class allows storing contextual information on the exception instances. The contextual data may
     * be added to an exception instance as arbitrary application objects and written out as a simple string of toString()
     * representation of all stored objects.
     */
    public class ContextualException extends RuntimeException {
    
        private final transient List<Object> context = new ArrayList<Object>();
    
        public ContextualException(String msg) {
            super(msg);
        }
    
        public ContextualException(String msg, Throwable t) {
            super(msg, t);
        }
    
        /**
         * Adds an object to the exception context.
         * @param o application object whose content may be of interest in the context of this exception
         */
        public void addToContext(Object o) {
            context.add(o);
        }
    
        /**
         * Gets the string representation of all objects stored in this exception's context.
         * @return concatenated string of all context object toString() representations
         */
        public String getContext() {
            StringBuilder contextStr = new StringBuilder(1000);
            if (context != null) {
                for (Object o : context) {
                    contextStr.append(o.toString());
                }
            }
            return contextStr.toString();
        }
    
    }
    Note that it doesn't format your context string in any way, but you probably won't need it as long as your objects have decent toString() methods. Also, there is one ugly issue with serialization here. I made the context list transient (that's why I am checking for null in the getContext() method) so it won't get initialized on deserialization. I just don't want to go crazy here and implement a fully blown thing with "readObject", etc. But you get the general idea.
    Last edited by constv; Nov 20th, 2008, 12:52 PM.

    Comment


    • #3
      thanks a lot for your input :-)

      As im using the Spring DAO Support classes, all our SQLExceptions are wrapped to unchecked exceptions. In like 90% of the cases that makes sense. In the other 10% we have PL/SQL code that covers some business rule realisation that we dont have in the java level, as this is done company wide in those PL/SQL procedures.Would you advice to let them get up to the controller layer and catch them there or just catch in those cases the dao call and retrow them, so that the service layer can catch and handle them?

      My idea was to use AOP. As we have a strict DAO and Service layer naming convention, *DAO and *Service, it would be easy to catch the DAO layer exceptions (unchecked) in our Portlet Filter and handle the Service layer exception also properly.

      I mean your suggestion of adding those context information to the exception class is quiet good in my opinion.

      i could just catch my exception (checked) and add some contexual information in the catch clause with addContexualInformation(Object o) and then rethrow the exception as ServiceException so that the Portlet, in our case, can catch the exception and show an error message, with just the message and the exception stack would be also logged properly at the same time.

      The same concept could be used for unchecked exceptions.

      Instead of giving a POJO to the addContexualInformation method as parameter, i could also just add a Map with key/value pairs with important information, as not all of a POJO's information might be needed at the end. e.g. if i have a Contract object, i might only be interested in the ContractId, as i can get all the information from this Id.

      What do you think?

      Comment


      • #4
        I think you are generally on the right track. However, it is not good that you have your business logic divided between your services and your... database... Ooohhh... Not good.

        Anyway... I agree with you regarding AOP. Generally, I would also consider using an aspect to consolidate all repetitive logic that you want to apply to absolutely all DAOs and/or services.

        One thing I am trying to convince you to do is reconsider using checked exceptions. Seriously, it's just wrong. Everything you ever need to do about error handling you can do better and safer with RTEs.

        Normally I don't see the point of catching and treating PL/SQL exceptions in the java code. Every such exception usually indicates a serious system/programming error. Normally, you just need the following:

        1) To make sure that your DB programmers (if they throw an exception within an SP, for example) put valuable information into it (because, unfortunately, Oracle or other DB exceptions are often very cryptic.)

        2) Make sure that the exception goes all the way to the point where you know what to do with it - unaltered. This may be as far up as in your front-end controller, or as close as in your DAO. If you do have enough knowledge in your DAO to interpret a specific PL/SQL error, then it's ok to catch that exception in the DAO and wrap it into a more meaningful RT exception (since your DAO can't recover from it anyway, it can only "clarify" the error.) Then, re-throw the new RTE with the nested original and don't touch it until it reaches your very top level handler on the front-end. That guy also couldn't care less what exactly happened, if there is no business case for that condition. All it knows is that it must log it, and redirect the user to the error page: "Sorry, the system is currently unavailable. " Now, the complete stack trace with all your additional data - supplied by the method that wrapped the original exception - will be logged. The users won't be looking at the log file - thank God! But you will. The stack trace and your supplementary data should give you all the info you need about what happened, where, and when.

        3) Now, since you have said that some of your SQL exceptions actually represent legitimate business conditions (do I understand you correctly?), it may be necessary for your application to translate those particular exceptions into legitimate alternative actions. Again, you don't need a checked exception for that. You are correct: it's best for your service (that's where the business logic should live!) to catch that type of error and either reflect the status in the domain object it returns, or, fine, throw a very specific, clear RTE. Again, that exception must be only caught by the front-end controller that makes the decision where to go next. If it is a legitimate use case, your controller must be programmed to expect such condition (exception or no exception.) If the service signals this particular use case with an exception (ok in some cases, but I would think twice before settling on that) then the controller should catch that particular exception and redirect the user to the appropriate view. In all other error cases, the default exception handler redirects to the generic error page.

        The idea here is that you don't need the compiler to tell you which logic to implement. Things like that are dictated by your application's requirements, not the compiler. On the other hand, there is only one place in the whole application where the decision can be made how to treat the particular error condition. Only the programmers knows what that module is. There must be absolutely no exposure of this error to the rest of the application code. Because once a method (that has no business of doing a thing about that error) is suddenly - and for no reason - exposed to it, there is a huge risk that someone will do something to the exception, mishandle it instead of letting it fly to its legitimate destination. Not to mention breaking the principle of abstraction, exposing implementation details, killing the scalability and maintainability of code, etc. Also, checked exceptions imply a horrible lie: not all software modules may result in errors! That is the worst and most dangerous thing about checked exceptions in Java. Instead of making your code safer, they make it totally unsafe by burning into your brain the misconception that you should only handle a subset of errors. Ah well... we are not going to start this again.
        Last edited by constv; Nov 20th, 2008, 03:37 PM.

        Comment


        • #5
          my issue is that there's a whole process server concept built into the DB layer which is running and doing the insurant companies business since 10-15 years..the idea that the DB technology stays longer then a specific programming language hurts my idea of a well layered application but from the business point of view its correct...

          Dont be afraid ;-D i didnt support one single line of business logic in the DB. All of the business logic that is provided by PL/SQL functions are legacy stuff that was done way before i joined the company...im trying to fight against the other 20 PL/SQL developer and like 7-8 java developers to make them think different ;-D

          The issue with checked exceptions is quiet complicated actually...the PL/SQL developers in here just tried to build a java-like-stacktrace for their errors and build their old applications with CHECKED exceptions only as they believe that thats the only way to keep context information...

          The problem is that half of their code is full with swallow exceptions or rethrowing the same exception again, as they dont know what to do with it ;-) But thats another story ;-D

          The PL/SQL exceptions are not generic SQL exceptions...the technical exceptions, in this case RTE, are not touched and have their original error message with an error key (ORA-XXX)..they just came up with a concept for "business" exceptions and give their own custom error code and message back. Those should also be translated as they expect those messages to be catched and to be enriched by each layer.

          This means that the PL/SQL exception would say

          "Too many contracts as result"

          and the dao would just rethrow this as a checked exception with the same message and the service would enrich this message with the line

          "The search for contracts with the query '43*' could not be processed"

          So they could follow the stack entry from each layer and give enough feedback to analyse complex problems.

          The problem is usually the complexity. If someone submits a contract and the creation of this contract is supposed dependent from 5-6 service calls and double that much DAO calls and the only answer i can give the user is "Sorry, the system is currently unavailable" and in the stack the only thing they see, as it's a RTE exception that was thrown at the DAO layer through a wrapped SQLException and catched at controller layer, is the message "Unique constraint violation" with the rest of the stacktrace, there's no way they will be able to search for the source of the problem :-/ And then again...it's not the job of exception handling to keep your context in case of errors or give you a control flow.
          Last edited by uenluena; Nov 20th, 2008, 04:11 PM.

          Comment


          • #6
            Originally posted by uenluena View Post
            im trying to fight against the other 20 PL/SQL developer and like 7-8 java developers to make them think different ;-D
            I know how you feel...

            The issue with checked exceptions is quiet complicated actually...
            It always is with checked exceptions.

            the PL/SQL developers in here just tried to build a java-like-stacktrace for their errors and build their old applications with CHECKED exceptions only as they believe that thats the only way to keep context information...

            The problem is that half of their code is full with swallow exceptions or rethrowing the same exception again, as they dont know what to do with it ;-)
            That's exactly my point!

            The PL/SQL exceptions are not generic SQL exceptions...the technical exceptions, in this case RTE, are not touched and have their original error message with an error key (ORA-XXX)..they just came up with a concept for "business" exceptions and give their own custom error code and message back. Those should also be translated as they expect those messages to be catched and to be enriched by each layer.

            This means that the PL/SQL exception would say

            "Too many contracts as result"

            and the dao would just rethrow this as a checked exception with the same message and the service would enrich this message with the line

            "The search for contracts with the query '43*' could not be processed"

            So they could follow the stack entry from each layer and give enough feedback to analyse complex problems.
            Sound like you have a really messed up [legacy] design, and not so bright co-workers... (at least some of them) which is very sad, but that happens often.

            The problem is usually the complexity. If someone submits a contract and the creation of this contract is supposed dependent from 5-6 service calls and double that much DAO calls and the only answer i can give the user is "Sorry, the system is currently unavailable" and in the stack the only thing they see, as it's a RTE exception that was thrown at the DAO layer through a wrapped SQLException and catched at controller layer, is the message "Unique constraint violation" with the rest of the stacktrace, there's no way they will be able to search for the source of the problem :-/ And then again...it's not the job of exception handling to keep your context in case of errors or give you a control flow.
            You are missing the point, I am afraid. First, the stack trace of a RT, uninterrupted exception says a lot by itself. It shows which method threw the exception, and where it was called, etc. Now, you obviously don't need to tell the user (some insurance agent, perhaps?) any more than that the system is not available, or request may not be processed at the moment, or something to that effect. The insurance agent is not going to fix your system. It will be an admin and developers who would have to look at the problem. So, what you log for those people to see is a different story. For one thing, you must log the full stack trace, that's a given. Both stack trace and detail message - are context information, so whoever told you that conveying the context is not the job of exception handling, is full of it! Also exception handling is, indeed, whatever your application requires. So if your application requires to capture some kind of execution context, then you have to do it - as you handle the exceptions. Now where do you put it? All over the place as your smart co-workers are doing? Or, perhaps, consolidate it as much as you can? I would do the latter.

            If you read my previous posts carefully, I explicitly stated that I would think twice before I would opt for using an exception to control logic. That's why I told you in a different thread that most of the time other methods are more appropriate. From what I understand it is your team who are using checked exceptions to signal business conditions, and the only "alternative logic" they implement in response to those exceptions is placing different messages into the exception instances. As I explained in the previous post, nothing prevents you from identifying where the first line of defence is in your application where you can safely interpret a particular error condition, and wrap the original exception inside a more readable service-specific exception with all the context info that you need (that is available to the service.) Your service can put whatever message you like. But when it re-throws the exception it should go to the same generic system exception handler that will blindly log it and redirect the user to the error page. The log will contain whatever you put there - deep in the service that threw it. The benefit is enormous: no one else needs to know anything about that exception, you don't have to write any more code for that specific condition, and all the data you wanted was logged. All you have to do is open the log file or DB and read the text.

            Trust me, I have built systems for insurance companies, banks, and US govt. And I have seen the same requirements, the same problems in the legacy systems you are talking about, and the same level of misunderstanding and incompetence. My approach worked always, like a charm. But I had to fight for it quite often. (Much less these days because more and more people come to the same conclusions.) The biggest problem in my line of work - I have discovered - is to convince people how simple things may be if you do them the right way. Unfortunately, there's way too much resistance... Well, it pays my bills, I guess.
            Last edited by constv; Nov 20th, 2008, 05:06 PM.

            Comment


            • #7
              Yeah i started to work as a developer and got promoted to a software architect but spending my days fighting with 20 ppl about concepts. Im allways willing to learn new stuff but having 3h discussions with your manager, who himself was a techie 3-4 years ago, about checked exceptions is really tiresome ;-)


              ok, maybe i didnt put my thoughts clear enough ;-) with "not the job to keep context" i meant the case where each error ends in a checked exception and ppl think that they can enrich the error stack with a message to keep contexual information, which are not allways exceptional ;-), in the exception stack. As i said "errors" and not "exceptions" as there's a quiet big difference in the meaning for me :-)

              So with such a exception class that holds contexual information AND exception message+stack trace one would encapsulate the exception information and the context information outside of the exception, but needed to verify the exception, in the same object.

              What do you think?

              Comment


              • #8
                Originally posted by uenluena View Post
                Yeah i started to work as a developer and got promoted to a software architect but spending my days fighting with 20 ppl about concepts. Im allways willing to learn new stuff but having 3h discussions with your manager, who himself was a techie 3-4 years ago, about checked exceptions is really tiresome ;-)
                Yeah, I hear ya... Try this. Get a copy of the new book by Robert Martin, etc. called "Clean Code. A Handbook of Agile Software Craftsmanship." (Prentice Hall, 2008.) Take it to your manager. Open the chapter called "Error Handling" (Chapter 7), page 106, section "Use Unchecked Exceptions". Point your finger at the first sentence: "The debate is over." Maybe he will at least consider listening from then on... If not, start looking for another place to work. There are plenty of sharp guys out there who are working on interesting stuff...

                So with such a exception class that holds contexual information AND exception message+stack trace one would encapsulate the exception information and the context information outside of the exception, but needed to verify the exception, in the same object.

                What do you think?
                I am not sure I follow your thought in that paragraph. Can you re-phrase it?

                Generally, I am against putting any kind of data (such as error codes) inside the exception instance that imply that the handler must specifically check for the value and act based on which value it finds there. It defeats the purpose of exceptions. Exceptions are not error codes. They should be handled in a polymorphic way. The handler should - at most - expect a particular type of the exception class and it should not need to check for anything else. If the handler catches a certain type of exception, it may expect to be able to call certain methods on the caught instance, but it should do it "blindly" without worrying what data is actually stored inside th exception instance. For example, the handler may retrieve the detail message or stack trace, but it would be extremely incompetent and crazy to afterwords inspect that string for some specific content and act upon it. So, what I suggested top you was: have a generic handler that catches all "ContextualExceptions" that bubble up to it, and treat them all in the same way. The only responsibility the 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 will be the responsibility of your services that throw these exceptions (original or by wrapping some underlying error conditions they catch) to supply any data that should be logged by the generic logger that waits up the call stack - somewhere in the presentation tier. Make sense?

                Comment


                • #9
                  hehe ok i give it another try :-)

                  what i wanted to say is that i would have an Exception Class that would have our discussed addContexualInformation(Object o) just to have more contexual information to the exception. This information is not the direct cause of the problem, but would be useful while analysing the problem.

                  The only issue is that i have no possibility to add _any_ information in case of DAO to this ContexualInformation as it will be thrown by my DAO class before i can add any information and would be catched on controller level where the only information available would be the stack trace that came from my dao call. In the Service Layer i have way more information about the context then in my DAO, as the only thing i do in my DAO layer are CRUD operations.

                  So where would you be able to add contexual information to that RTE?

                  Comment


                  • #10
                    Originally posted by uenluena View Post
                    hehe ok i give it another try :-)

                    what i wanted to say is that i would have an Exception Class that would have our discussed addContexualInformation(Object o) just to have more contexual information to the exception. This information is not the direct cause of the problem, but would be useful while analysing the problem.

                    The only issue is that i have no possibility to add _any_ information in case of DAO to this ContexualInformation as it will be thrown by my DAO class before i can add any information and would be catched on controller level where the only information available would be the stack trace that came from my dao call. In the Service Layer i have way more information about the context then in my DAO, as the only thing i do in my DAO layer are CRUD operations.

                    So where would you be able to add contexual information to that RTE?
                    Ok, now it makes perfect sense. It's exactly what I would expect: this kind of logic is business logic, and it belongs in the business service. So, ideally, you should not need to catch anything in the DAO but catch all possible exceptions in the service, add your contextual data, and re-throw - as I have said in the earlier posts - a service-specific RTE that extends your ContextualException, adds the service-specific context to it, and wraps the original exception that came from the data access tier. See, any data tier exception is a system exception, something that you don't want to happen. So, if it happens, you really don't care what it is - from the stand point of handling. You know it's bad, and you handle all flavors of that condition in the same way, e.g. display a generic error page. The only difference is that your service API, before re-throwing it will add whatever info is appropriate for it - to be read by the admins and developers. This sounds like a great case for an aspect here - if it is satisfactory, for example, to automatically place all the API method's arguments into the exception's instance as your "context". That would allow you to have a single aspect around every public method on each service to ensure that any Throwable inside the method will be caught, wrapped into an instance of SomeServiceException (that extends ContextualException), updated with the input arguments, and re-thrown. The generic front-end handler will have a catch for ContextualException, log everything, direct to the error page. Done.

                    If you need to be more specific what you place into the added context, you may have to manually implement a try/catch Throwable in each service API, which would be ugly. It sounds, though, that in your case, the more the better. So, you it wouldn't hurt to log all the objects that were passed to the service when something blew up.
                    Last edited by constv; Nov 20th, 2008, 06:49 PM.

                    Comment


                    • #11
                      just a short comment on the "error" codes for exceptions.

                      What i have at the moment is a code concept where the exception is not thrown bye a message but much more by a key. This is done to give the flexibility to change the Exception message with the text the developer desire, without the need of refactoring. There are no key/value pairs like structure.

                      To be more specific, we have a ErrorDefinitionService witt the medho getErrorById(Long id) which is used like

                      throw new MyException(errorService.getErrorById(longValue))) ;

                      The reason why its called "Error" might be misleading but EVERY error displayed in the web application is automatically translated etc.

                      The technical exceptions however dont need to be translated as they never reach the user in that form. In that case we only use it to avoid refactoring for error message editing.

                      Comment


                      • #12
                        The AOP issue i could imagine an around advisor:

                        Code:
                        public class MyInterceptor   implements MethodInterceptor {
                        
                          public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                                Object call = null;
                                try {
                                    call = methodInvocation.proceed();
                                } catch (ContexualException e) {
                                   //get some context information
                                   e.addContexualInformation(context);
                                   throw e;
                                }
                                return result;
                            }
                        }
                        and with the spring configuration and BeanNameAutoProxyCreator i could wildcard the beanNames to get this advisor work with all my Service objects as they all end with *Service

                        On my Portlet Filter i could catch the ContextException and dispatch to the generic webpage with the standard error message or the UI with the BusinessException error message.

                        Comment


                        • #13
                          A couple of things...

                          1) I have slightly revisited the contextual exception class that I suggested earlier in this thread. I didn't like my using of a collection to store the context, if all we need is a complementary message that reflects the states of some arbtrary application objects that developers may add. So, here's the revised class:

                          Code:
                          package com.yourcompany.common;
                          
                          /**
                           * This exception class allows storing contextual information on the exception instances. The contextual data may
                           * be added to an exception instance as arbitrary application objects and written out as a simple string of toString()
                           * representations of all stored objects.
                           */
                          public class ContextualException extends RuntimeException {
                          
                              private final StringBuilder contextMessage = new StringBuilder(1000);
                          
                              public ContextualException(String msg) {
                                  super(msg);
                              }
                          
                              public ContextualException(String msg, Throwable t) {
                                  super(msg, t);
                              }
                          
                              /**
                               * Adds an object to the exception context string.
                               * @param o application object whose content may be of interest in the context of this exception
                               */
                              public void addToContext(Object o) {
                                  contextMessage.append(o.toString()).append(System.getProperty("line.separator"));
                              }
                          
                              /**
                               * Adds an object and its description to the exception's context string.
                               * @param o application object whose content may be of interest in the context of this exception
                               * @param description clarifying description of the object being added to context
                               */
                              public void addToContext(Object o, String description) {
                                  contextMessage.append(description).append(": ").append(o);
                              }
                          
                              /**
                               * Gets the string representation of all objects stored in this exception's context.
                               * @return concatenated string of all context object toString() representations
                               */
                              public String getContextMessage() {
                                  return contextMessage.toString();
                              }
                          }
                          As you see, you have an option to add a description for each contextual object as well. I chose not to override the getMessage() method to include the context info by default because I don't think it would be appropriate. Let the clients decide when and how to use that information. See an example below...

                          2) I see a little problem with your code example... How do you know that the method you are intercepting is throwing that specific type of exception. And what happens to all other exceptions that may be thrown inside. What you need to do is to catch any exception, wrap it into your ContextualException, add context data, and re-throw.

                          Code:
                          ...
                          catch (Throwable t) {
                                     //get some context information
                                     // ...
                                     ContextualException ce = new ContextualException("Your generic message here: service failure, blah-blah...", t);
                                     // loop through args of the method 
                                     for (Object arg : allArgs) {
                                         ce.addToContext(arg);
                                     }
                                     throw ce;
                                  }
                          3) In your handler - if, for example, you are using Spring MVC in the front-end - you'd have a generic exception resolver for all your system errors that will catch all server-side exceptions that bubble up to it:

                          Code:
                          package com.yourcompany.common.web.springmvc;
                          
                          /**
                           * This class implements an exception resolver that redirects to the designated view in cases of any exception that
                           * <i>has not been previously caught and handled</i> by the application or framework (including any Spring Webflow error
                           * handlers registered with the application). Those are any system and programming errors that should result in a
                           * graceful termination of the application.
                           * <p/>
                           * A bean of this class must be registered with the Spring application context with the order number the highest amongst
                           * all error resolvers. By default, the prioritization order value for this error handler is set to {@link
                           * Integer#MAX_VALUE} to ensure that this resolver is evaluated <i>after</i> all other registered resolvers have been
                           * considered for any exceptions that may require more specific handling. The value may be changed via the {@link
                           * #setOrder} method, however, in most cases, that should not be done.
                           */
                          public class SystemErrorResolver implements HandlerExceptionResolver, Ordered {
                          
                              private int order = Integer.MAX_VALUE; // prioritization order number for this Ordered object
                              private String errorViewName;
                              private static final Logger logger = Logger.getLogger(SystemErrorResolver.class);
                              private static final String ERR_MESSAGE =
                                      "Unrecoverable error occured in the application. See the nested exception stack trace for details. Redirecting to the Error View."
                                      ;
                          
                              /**
                               * Constructs a SystemErrorResolver object with the given name of the view to render in the case of a system error.
                               * This view name must be properly resolved by the Spring MVC DispatcherServlet's view resolver.
                               *
                               * @param viewName name of the View to render, to be resolved by the DispatcherServlet's ViewResolver
                               */
                              public SystemErrorResolver(String viewName) {
                                  errorViewName = viewName;
                              }
                          
                              /** {@inheritDoc} */
                              public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse response, Object handler,
                                                                   Exception e) {
                                  // return the Error Page view in the case of any unhandled exception
                                  if (e != null) {
                                      logger.error(ERR_MESSAGE, e);
                                      
                                      // below is the additional logging of any context message if it is available
                                      if (e instance of ContextualException) {
                                           logger.error(e.getContextInfo());
                                      }
                                      return new ModelAndView(errorViewName);
                                  } else {
                                      return null;
                                  }
                              }
                          
                              /** {@inheritDoc} */
                              public int getOrder() {
                                  return order;
                              }
                          
                              /**
                               * Sets the prioritization order number for this Ordered object. Spring looks at all handler exception resolvers
                               * (beans implementing the appropriate HandlerExceptionResolver interface) in the application context. Any instances
                               * that implement the Ordered interface are sorted (lowest order first), and others are added at the end of the
                               * list. The resolvers are considered in the order they are listed.
                               * <p/>
                               * NOTE: By default, the prioritization order value for this error handler is set to {@link Integer#MAX_VALUE} to
                               * ensure that this resolver is evaluated <i>after</i> all other registered resolvers have been considered for any
                               * exceptions that may require more specific handling. <b>Do not call this method unless this behavior must
                               * change.</b>
                               *
                               * @param num order number
                               */
                              public void setOrder(int num) {
                                  this.order = num;
                              }
                          
                          }
                          This is a class I wrote a few projects ago, and I have re-used it since on many projects. It's very basic stuff, I am sure you are familiar with it and know how to register an exception resolver with the Spring context. What I added just now, is the optional check for the exception instance being of the ContextualException type. If so, we can also log the context message.

                          You could write a generic "Around" aspect for each public service method that would do something like that (assuming you have some common marker interface for all your services called IService, for example):

                          Code:
                             
                              @Pointcut("execution(* com.yourcompany.common.service.IService.*(..))")
                              private void anyServiceOperation() {
                              }
                          
                          @Around("anyServiceOperation()")
                          public static void handleServiceFailure(ProceedingJoinPoint point) {
                                  try {
                                      point.proceed();
                                  } catch (Throwable t) {
                                      ContextualException ce = new ContextualException(STD_MSG, t);
                                      for (Object arg : point.getArgs()) {
                                           ce.addToContext(arg);
                                     }
                                     throw ce;
                                  }
                              }
                          This is just off the top of my head, and you certainly will need to work with it to make this do whatever you really need. But I hope this gives you the general idea. Let me know if this makes sense.

                          Good luck,
                          Constantine

                          Comment


                          • #14
                            Thanks a lot for your input :-) your described structure is pretty much the one that i designed on paper atm :-)

                            As we dont use the Spring MVC, the AOP approach is more suitable :-)

                            Comment


                            • #15
                              Originally posted by uenluena View Post
                              Thanks a lot for your input :-) your described structure is pretty much the one that i designed on paper atm :-)

                              As we dont use the Spring MVC, the AOP approach is more suitable :-)
                              Oh, one does not exclude the other... In fact, the Spring resolvers indeed implement aspect-like behavior. You register a resolver and spring ensures that it listens to the exceptions. Also, you need to use AOP to wrap pall your service-layer errors - something similar to what I showed you. Then, you can do whatever you like on the front-end as long as you make sure that your filter catches all exceptions. In fact, using an aspect in the service tier is more important because you would otherwise have to duplicate the same error-context-handling code in every public method on every service. On the front-end, you only need a single place for your try/catch.

                              I have re-read your earlier comment about exception codes. I would implement that differently. Please bear with me... See, the String returned by getMessage() on an exception class is not meant to be seen by the user. This text is for error logging and analysis by developers and admins. That is why, the content of that message is only - and foremost - relevant in the context of the very code that throws that particular exception instance. Therefore, not only there is nothing wrong with hard-coding that text in the very call to the exception constructor before it is being thrown, it is the best and most appropriate approach. This is - by all means - part of your error-handling code and your diagnostics. The worst thing you can do is to extract those valuable messages from your code and hide them behind some crafty message-resolving mechanism. Now, you are robbing yourself and your developers from the ability to easily figure out what the exception represents even by looking directly at the code that throws it! Talk about over-engineering and maintenance nightmare. Please think well about what you guys are doing.

                              The problem is that you are mixing two things that should not be mixed. When you construct an exception instance, always provide the most detailed technical message you can come up with - for debugging purposes. Never intend to expose this message to the end user. That message should not change with application refactorings because it must reflect one and only one very specific error condition. The exception itself must not contain anything that will ever be exposed to the user! Now, if you want to ease the way for the client to map an exception instance to a particular user-friendly message, and use the exception object itself to assist you with that, there is a better way to do it. (DISCLAIMER: Let me just say that this may not be such a good idea, generally. It's probably better - in most cases - to use external mappings rather than one built-in within an exception. Exceptions should ideally be agnostic of the outside world and only transport the error context.) Ok, here's one way...

                              1) First of all, I am sure you agree that error messages that are meant for the end-user must be externalized, preferably in a message resource bundle where they are easily accessible and manageable during development or in production. I know you work with a bunch of DB who, I am sure, want to put everything into the database. Perhaps you can convince them to store user-oriented messages where they rightfully belong - in the presentation tier configuration: message resource files, where your config files, and both Java classes and JSPs can effortlessly access them.

                              2) You may have your application-specific exception classes (the ones that, in your opinion represent business cases) extend some MsgResourceAwareException that stores the message key - in addition to the detail message and nested Throwable. (Note, I don't advocate this as a general-purpose solution, see my disclaimer above...) When you throw an instance of such exception, you set the message key that corresponds to a message in your message resource. Of course, this means that the classes that throw this type of exception should live in the same tier with the message resource, otherwise it gets ugly.

                              3) Then, in your front-end filter, you check if the incoming exception is message-resource-aware, and if so, you retrieve the message key and put it into your request object. Redirect the app to the error page. (I am talking about your scenario when all errors direct to the same page but with different messages.)

                              4) Your generic error JSP may look similar to the following:

                              Code:
                              <?xml version="1.0" encoding="UTF-8" ?>
                              <%@ page contentType="text/html;charset=UTF-8" language="java" %>
                              <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
                              <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
                              
                              <%-- If no custom message key is passed in request, use standard default System Error messages; assign "messageKey" variable to default  --%>
                                 <c:if test="${messageKey == null}">
                                  <c:set var="messageKey" value="error.system.default"/>
                                 </c:if>
                              
                                  <%-- retrieve message from msg resource by key and display --%>
                                  <fmt:message key="${messageKey}"/>
                                      
                                  <p/>
                              
                                  ... place your buttons here, or whatever....
                              That's it! When an error occurs, your technical note (produced by Exception.getMessage()) - along with the full stack trace - will be logged for the experts to look at. Your UI will display whatever message you have put into your message resource for this case. The only "fishy" thing here is that the developer needs to insert the appropriate message key into the exception instance and make that exception "aware" of the application that uses it. If you have architected your system properly, that business condition would be evaluated in the client (because it is the client's requirement to distinguish one error from another; your database should not be telling the client which messages to display and when!) So, there is no violation of architectural principles. If you try to introduce "user message keys" somewhere in the back end, well, then, it's just plain wrong, anyway.

                              Ok, I hope you'll find at least some of this helpful, if not for the current project, then maybe for some future designs... Good luck!
                              Last edited by constv; Nov 24th, 2008, 03:39 PM.

                              Comment

                              Working...
                              X