Page 2 of 6 FirstFirst 1234 ... LastLast
Results 11 to 20 of 55

Thread: Service Layer Exceptions

  1. #11
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Not not "not necessarily", but "mostly not" - checked or unchecked exceptions must be meaningful and unambiguous to provide for easily recognition of an error cause. So NPE-like exceptions should be prevented as much as possible as in many cases it is hard to recognize what exactly is null. Selection in favor of RTE does not relieve a need in a "defensive programming", at least in critical parts of the application.

    Additionally, if you have to use "external" (to you) method that throws checked exception that your method can not meaningfully handle and which represents rather implementation detail of your method then you have to catch this exception and re-raise as RTE (chained to the original exception).

    Regards,
    Oleksandr

    Quote Originally Posted by constv View Post
    Not necessarily... In some cases, where you feel appropriate, your "business" logic may (or even will need to) actually include checking for nulls or other inconsistencies with your business rules that you (your application) might want to distinguish. See the difference? If the "null" value, for example, tells you that some essential application parameter was not properly configured or not supplied at all, you may check for that condition and throw a more meaningful custom RTE, such as ConfigurationException with a detail message indicating which parameter was null or had another out-of-wack, completely illegal value. Such exception, most likely, should go all the way to the top-level resolver and result in the generic error page (assuming that in this example the parameter is so essential that the application does not have a fall-back behavior for the case of a bad parameter value.) The log file will show you what needs to be fixed, e.g. you need to fix the config files and redeploy the app, or something like that...

    HTH,
    Constantine

  2. #12
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by al0 View Post
    Not not "not necessarily", but "mostly not" - checked or unchecked exceptions must be meaningful and unambiguous to provide for easily recognition of an error cause. So NPE-like exceptions should be prevented as much as possible as in many cases it is hard to recognize what exactly is null. Selection in favor of RTE does not relieve a need in a "defensive programming", at least in critical parts of the application.

    Additionally, if you have to use "external" (to you) method that throws checked exception that your method can not meaningfully handle and which represents rather implementation detail of your method then you have to catch this exception and re-raise as RTE (chained to the original exception).

    Regards,
    Oleksandr
    I absolutely agree. To put the above in a general "rule-like" form:

    Any exceptional condition that may appear meaningless, ambiguous, or lacking clarity outside the context of the method where it is first detected should be wrapped into a meaningful descriptive exception and thrown out of the method. Bare NPEs most often carry very little useful info outside the context where they occur, so the rule applies to them more than anything. In other words, it never hurts (and often necessary) to provide additional clarifying info - if you have something useful to add that would assist in pinning down the problem.

    Note that such "condition" may be an exception itself or a simple business check for an incorrect value like "if (myParam == null)", etc. If that is an exception, it must always be wrapped inside the new meaningful exception. Do not discard the original (cause) exception when re-throwing.

  3. #13
    Join Date
    Mar 2009
    Posts
    15

    Default

    Excellent feedback by the both of you, I appreicate this very much.

    Is there any detailed examples I can look at to get a feel for good exception handling? Perhaps one of you could provide some source code i could look at and learn from?

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

    Default

    Quote Originally Posted by ramoq View Post
    Excellent feedback by the both of you, I appreicate this very much.

    Is there any detailed examples I can look at to get a feel for good exception handling? Perhaps one of you could provide some source code i could look at and learn from?
    Unfortunately, I don't know of a single book out there that has really good examples, or examples that are not confusing or just plain wrong... Even some really good books on Java (like Eckel's "Thinking in Java") make a total incoherent and ambiguous mess out of their exceptions chapters.

    Start with reading this:

    http://static.springframework.org/sp...eptionhandlers

    In addition to the code examples I have already given you (the general-purpose resolver code, the web.xml snippet), here are some more:

    Defining the above-shown "SystemErrorResolver" resolver in your spring servlet context, "yourapp-servlet.xml":
    Code:
      
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
     
       <!-- Resource view resolver definition.
        For example, it will map the logical "error" view to "/WEB-INF/jsp/error.jsp" -->
        <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <property name="prefix" value="/WEB-INF/jsp/"/>
            <property name="suffix" value=".jsp"/>
        </bean>
    
     <!-- Generic system/programming error resolver that handles the rest of the exceptions not handled by the app -->
        <!-- it is constructed with the default name of the error view to be rendered for system errors (/WEB-INF/jsp/error.jsp) -->
        <bean id="genericErrorHandler" class="com.yourcompany.common.web.springmvc.SystemErrorResolver">
            <constructor-arg value="error" />
        </bean>
        
       <!-- You can add more specific resolvers that would listen to 
             specific exceptions; use a lower priority order property value 
             to make sure they get evaluated before the generic one, and 
             in the correct order; the lower the number, the higher the
             priority of the exception evaluation; read the manual -->
    
    ...
    </beans>
    Sample error.jsp that uses your application message resources to retrieve and print the appropriate messages:

    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....
    Your exception resolvers may map exception types to particular values of the "messageKey" requiest parameter and insert that parameter into the request context. When the page gets displayed, it will pull the parameter's value, and the "fmt:message" tag will resolve the actual message - if the message is present in the resource for the given message key.

    This should get you started. This solution roughly covers a basic web application requirements. All you have to do is to 1) identify any exceptional conditions you really want to handle distinctly, and create resolvers for them. For all other, the general resolver I showed you will work. By "distinctly" I mean something other than the general error page.

    You can also re-factor the resolver I gave you to be configured to support a collection of exception types, mapping each type to its own view, falling back on the generic view page. I have done something like that in SWF applications. For example, here's the app context for exception handling configuration for a SWF app. See the example in this thread, make adjustments for SWF 2.x (also in that thread):

    http://forum.springframework.org/showthread.php?t=54392

    Should be more than enough for lesson 1. Good luck.

  5. #15
    Join Date
    Mar 2009
    Posts
    15

    Default

    Quote Originally Posted by constv View Post
    I absolutely agree. To put the above in a general "rule-like" form:

    Any exceptional condition that may appear meaningless, ambiguous, or lacking clarity outside the context of the method where it is first detected should be wrapped into a meaningful descriptive exception and thrown out of the method. Bare NPEs most often carry very little useful info outside the context where they occur, so the rule applies to them more than anything. In other words, it never hurts (and often necessary) to provide additional clarifying info - if you have something useful to add that would assist in pinning down the problem.

    Note that such "condition" may be an exception itself or a simple business check for an incorrect value like "if (myParam == null)", etc. If that is an exception, it must always be wrapped inside the new meaningful exception. Do not discard the original (cause) exception when re-throwing.

    So Essentially it's OK for me to do all the null checking I want, all the validation I desire on any parameters as long as I throw back valid RTE's? Whether custom or not? (Most likely custom wrapped RTE's seem most favourable in a service scenario). The NO-NO is doing all that validation and throwing back Checked Exceptions?

    Am I understanding this correctly?

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

    Default

    Quote Originally Posted by ramoq View Post
    So Essentially it's OK for me to do all the null checking I want, all the validation I desire on any parameters as long as I throw back valid RTE's? Whether custom or not? (Most likely custom wrapped RTE's seem most favourable in a service scenario). The NO-NO is doing all that validation and throwing back Checked Exceptions?

    Am I understanding this correctly?
    That's correct. I know I have been using a zip code class as an example a few times in other threads... Here's one version of such class from one of my old projects (very specific requirements.) It probably has more stuff than you are interested in but it demonstrates the internal basic validation and exception throwing.

    Code:
    package com.xxxxxxx.address.domain;
    
    import com.xxxxxx.common.domain.InputParsingException;
    
    import java.io.Serializable;
    import java.text.MessageFormat;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * This class implements a US Zip Code object. An instance may be  
     * created with an empty code, or a valid non-empty code that satisfies 
     * the US ZIP format. No instances of this class may be created with
     * invalid data. A RTE will be thrown if an attempt is made to pass  
     * invalid data.
     *
     * @author constv
     */
    public class ZipCode implements Serializable {
    
        private static final String ERR_MSG_INVALID_ZIP_FORMAT =
                "Invalid Zip Code format: \"{0}\". The Zip Code must be either 5 digits, 9 digits, or 5 digits followed by a dash and 4 more digits.";
        private static final String ERR_MSG_INVALID_NULL_IN_2ARG_CONSTRUCTOR =
                "Invalid invocation of constructor ZipCode(String zip5, String zip4). The 5-digit code field may not be null.";
    
        /**
         * The regular expression that validates the format of the zip code input string. It allows the following forms of
         * input:
         * <pre>
         *   #####        - a 5-digit code
         *   #####-####   - a 9-digit code with a dash between the 5-digit code and the 4-digit extension
         *   #########    - a 9-digit code without a dash
         *   #####-       - a 5-digit code with a dash after the first 5 digits but no 4-digit extension
         * <p/>
         * </pre>
         */
        @SuppressWarnings({"HardcodedFileSeparator"})
        private static final String ZIP_REGEXP = "^\\d{5}[\\-]?(\\d{4})?$";
    
        private char[] zip5;
        private char[] zip4;
    
        /**
         * Constructs a <tt>ZipCode</tt> object with no data, which could be a valid case for addresses where no zip code is
         * specified, i.e. a zip code is an empty line.
         */
        public ZipCode() {
        }
    
        /**
         * Constructs a <tt>ZipCode</tt> object from the given input string. The constructor allows an empty string to be
         * passed. If the input string is not empty, it is validated against the acceptable zip code formats.
         *
         * @param zip string of characters for the Zip code; the valid input must be either 5 digits, or 5 digits followed
         *            by a dash and 4 more digits.
         * @throws com.imagitas.common.domain.InputParsingException
         *          if the input string does not conform to the zip code format
         */
        public ZipCode(String zip) {
            if (zip != null && zip.length() > 0) {
                parse(zip);
            }
        }
    
        /**
         * Constructs a <tt>ZipCode</tt> object from two separate fields: the 5-digit code and the 4-digit add-on, if the
         * latter is not empty. This is just a convenience constructor to save the client from performing some extra string
         * concatenation.
         *
         * @param zip5Str 5-digit zip code; may not be null.
         * @param zip4Str 4-digit add-on
         */
        public ZipCode(String zip5Str, String zip4Str) {
            if (zip5Str == null) {
                throw new InputParsingException(ERR_MSG_INVALID_NULL_IN_2ARG_CONSTRUCTOR);
            }
    
            if (zip4Str != null) {
                parse(new StringBuilder(9).append(zip5Str).append(zip4Str).toString());
            } else {
                parse(zip5Str);
            }
        }
    
        /**
         * Sets the zip code on an already constructed instance of the ZipCode class.
         *
         * @param zip zip code string
         */
        public void setZip(String zip) {
            if (zip != null && zip.length() > 0) {
                parse(zip);
            }
        }
    
        /**
         * Sets the zip5 code on an already constructed instance of the ZipCode class.
         *
         * @param zip zip code string
         */
        public void setZip5(String zip) {
            setZip(zip);
        }
    
        /**
         * Gets the Zip code as the 5-digit code.
         *
         * @return zip code as the 5-digit string
         */
        public String getZip5() {
            if (zip5 == null) {
                return "";
            } else {
                return new String(zip5);
            }
        }
    
        /**
         * Gets all the digits of the zip code (5 or 9) without a dash.
         *
         * @return 5 or 9 digits of the zip code if the zip is defined
         */
        public String getZipAllDigits() {
            return new StringBuilder(9).append(getZip5()).append(getZip4Ext()).toString();
        }
    
        /**
         * Indicates whether this zip code contains the 4-digit extension.
         *
         * @return boolean <tt>true</tt> or <tt>false</tt>
         */
        public Boolean hasZip4Ext() {
            return zip4 != null;
        }
    
        /**
         * Gets the extending 4-digit part of the zip code.
         *
         * @return zip code's 4-digit part
         */
        public String getZip4Ext() {
            if (zip4 == null) {
                return "";
            } else {
                return new String(zip4);
            }
        }
    
        /**
         * Gets the full zip code string if both the 5-digit and 4-digit parts are available; if only the stored sip code is
         * represented by 5 digits only, it is returned as a 5-digit code.
         *
         * @return zip code string
         */
        public String getFullZip() {
            if (zip4 == null) {
                return getZip5();
            } else {
                return new StringBuilder(9).append(zip5).append('-').append(zip4).toString();
            }
        }
    
        /**
         * Returns the Zip Code string.
         *
         * @return zip code string
         */
        @Override
        public String toString() {
            return getFullZip();
        }
    
        /**
         * Validates and parses the input string into the zip code fields. The input string is validated using the {@link
         * #ZIP_REGEXP} regular expression.
         *
         * @param zip zip code string
         */
        private void parse(String zip) {
            validate(zip);                     // validate against regular expression
            zip = zip.replace("-", "");        // after string was successfully validated, remove dash, if present
            this.zip5 = new char[5];           // create and populate 5-digit field
            zip.getChars(0, 5, this.zip5, 0);
            if (zip.length() == 9) {           // if 4-digit extension present, create and populate that field
                this.zip4 = new char[4];
                zip.getChars(5, 9, this.zip4, 0);
            }
        }
    
        /**
         * Validates the input string for matching the requirements for a zip code.
         *
         * @param zip string to validate
         */
        private static void validate(String zip) {
            if (!isValid(zip)) {
                throw new InputParsingException(MessageFormat.format(ERR_MSG_INVALID_ZIP_FORMAT, zip));
            }
        }
    
        /**
         * Validates the input string for matching the requirements for a zip code. This <i>utility</i> method may be used
         * to check if the input string matches any of the valid ZIP code formats without creating an instance of the
         * ZipCode class.
         *
         * @param zip string to validate
         * @return true if the input string matches a valid ZIP code format
         */
        public static boolean isValid(String zip) {
            Pattern p = Pattern.compile(ZIP_REGEXP);
            Matcher matcher = p.matcher(zip);
            return matcher.matches();
        }
    }
    Note that the class is designed not to allow invalid instances to be created. If the input is completely out-of-wack and no valid ZIP code can be constructed, a meaningful runtime exception (with detail message) will be thrown. (It will be up to the application to catch that in an appropriate place, e.g. where the ZIP population occurs. A more generic and simple way - when used in a form - is to let Spring catch it during binding and produce a MethodInvokation exception that would later be mapped to the message key, like "methodInvocation.zip" during validation. But these details may be of no interest to you now, and I had some specific requirements t implement it that way. In many cases of course, all you need is a simple string for the ZIP code + simple field validation in some AddressValidator. )

  7. #17
    Join Date
    Mar 2009
    Posts
    15

    Default

    - The whole time I was confused that we should do NO EXPLICIT ERROR throwing(rte or regular exception) and just let run-time errors happen as they occur. I was thinking this was the meaning of run time errors: just don't do anything and let the program break along the way.

    - Essentially I was assuming that there is no point in validating the method params bcs the runtime error will occur (NPE or whatever) when we try access the params in the services logic. Thus we're not using checked exceptions anymore Rte's will occur naturally)

    - Because when I think runtime errors I always just assumed runtime errors means anything that the jvm throws (and only the jvm). We as programmers have no business dealing with them.

    goodness, what a learning experience. thanks guys!

  8. #18
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by ramoq View Post
    - The whole time I was confused that we should do NO EXPLICIT ERROR throwing(rte or regular exception) and just let run-time errors happen as they occur. I was thinking this was the meaning of run time errors: just don't do anything and let the program break along the way.

    - Essentially I was assuming that there is no point in validating the method params bcs the runtime error will occur (NPE or whatever) when we try access the params in the services logic. Thus we're not using checked exceptions anymore Rte's will occur naturally)

    - Because when I think runtime errors I always just assumed runtime errors means anything that the jvm throws (and only the jvm). We as programmers have no business dealing with them.
    And you are not the only one. This brilliantly illustrates my point about the overwhelming misunderstanding of error-handling in Java. When Sun introduced checked exceptions in Java, they literally announced that they had all but solved the error-handling problems. I remember reading a quote online a long ago: one of the people at Sun who pushed for checked exceptions and finally won (it's a woman, I don't remember her name) saying something to the effect of the world seeing the light again... The sad reality is, I think error handling is misunderstood and misused in Java more than in any other language. Well, thank you ma'am, whatever your name is...

    No, RTEs are not out there to blow things up. NO, no, no! They absolutely must be caught and handled, one way or the other. No exception should be left unhandled. (Just think of mission-critical applications that run on systems where human lives are involved! Should a space shuttle blow up if a NPE occurs?) Sometimes, handling may involve as little as logging and exiting gracefully - at the very least. But nothing less than that! Also, even when you think that you can swallow the exception, you must log it, at least. That information may later be extremely useful in indicating that something unusual did happen, and you eventually need to keep track of such things.

    goodness, what a learning experience. thanks guys!
    You are welcome. I hope this helps. One person at a time... Help us spread the word.
    Last edited by constv; Mar 3rd, 2009 at 07:32 PM.

  9. #19
    Join Date
    Mar 2009
    Posts
    15

    Default

    - In regards to RTE's thrown at transaction commit times, ie. the db unique constraint is violated etc.

    - Since generally my service methods are marked with @Transactional, should I proxy these calls and catch the RTE's then wrap them and return back a valid msg to the user?

    ie.
    Code:
    public User createUser(String email, String name){
         try{
               createUserTransaction(email, name);
        }catch(/* runtime error */){
         //throw back meaningful RTE wrapping the exception
        }
    
    }
    
    @Transactional
    private User createUserTransaction(String email, String name){
       //construct domain object, dao/persist logic
    }
    I can't see another way to catch these RTE's and return back a meaningful RTE to the client. (And even if I did an explicit check to see if the email is unique, it's not guaranteed until the transaction commits)

  10. #20
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    It is not exactly so - for the most databases such kind of checks are performed at the moment of actual operation (insert/update/delete) and not on commit. In some databases (e.g. Oracle) constraints may exist in immediate (default) and deferred mode, but latter is used rather rarely.

    If use use Hibernate (or something similar) operations may seem to be deferred till transaction end, but in reality they are delayed not till commit, but till session flush, commit triggers flush, but you may as well call flush manually before leaving method.

    So in the most circumstances you are able to catch and process (e.g. wrap and rethrow) exceptions inside your method.

    In rare cases when you have to deal with deferred constraints there are several possibilities.
    • You may use some wrapper with non-transactional methods which you expose to the clients of your service and then delegate calls to the real service and then dials with exceptions (if any).
    • You may use AOP to apply after-throwing advice to your service methods. But I'm not sure how to place before transactional aspect. As far as I can remember (not 100% sure) Spring has some problems with this. You may try a search a AOP forum for appropriate discussions,
    • Depending on your application (e.g. if you are using Spring MVC and/or Spring WebServices) it may be enough to configure handler exception resolvers in an appropriate way.

    There may be other possibilities that I can not remember right now.

    Regards,
    Oleksandr


    Quote Originally Posted by ramoq View Post
    - In regards to RTE's thrown at transaction commit times, ie. the db unique constraint is violated etc.

    - Since generally my service methods are marked with @Transactional, should I proxy these calls and catch the RTE's then wrap them and return back a valid msg to the user?

    ie.
    Code:
    public User createUser(String email, String name){
         try{
               createUserTransaction(email, name);
        }catch(/* runtime error */){
         //throw back meaningful RTE wrapping the exception
        }
    
    }
    
    @Transactional
    private User createUserTransaction(String email, String name){
       //construct domain object, dao/persist logic
    }
    I can't see another way to catch these RTE's and return back a meaningful RTE to the client. (And even if I did an explicit check to see if the email is unique, it's not guaranteed until the transaction commits)

Posting Permissions

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