The Chain of Responsibility pattern's pitfalls and improvements

Discussions

News: The Chain of Responsibility pattern's pitfalls and improvements

  1. The classic Chain of Responsibility (CoR) pattern requires the individual chain node classes to decide whether the next node should be called to fulfill the chain's responsibility.

    This is a design flaw because, in practice, such a call can be easily forgotten. In this article, CoR implementations of Microsoft Windows global hook and Java servlet filter framework are used as examples to demonstrate that flaw.

    A solution is proposed, and an action chain that allows multiple actions to handle an HTTP request in the Struts framework is discussed.
    The buzzwords for software design are "decouple, decouple, and decouple." The fundamental flaw of the classic CoR implementation suggested by GoF is that the chain execution decision-making, which is not the business of subclasses, is coupled with request-handling in the subclasses. That violates a principle of object-oriented design: an object should mind only its own business. The solution is to decouple the chain execution decision-making and the request-handling by moving the next node call to the base class. Let the base class make the decision, and let subclasses handle the request only. By steering clear of chain execution decision-making, subclasses can completely focus on their own business, thus avoiding stopping the chain by accident.
    The Chain of Responsibility pattern's pitfalls and improvements

    Threaded Messages (20)

  2. i agree![ Go to top ]

    I also never use the chain of responsibility in the "classis" manner. I always create my "handlers" so that they return a boolean. A value of true means to continue. A value of false means to stop. I usually ignore the distinction of stopping because the situation was "handled" or stopping becuase the "handler" merely wants it to stop. This usually does not matter. If handlers need to communicate then they can in order to stop. I can always log what happened and my code is happy that the situation was "handled".

    I have also had debugging nightmares because the "handleNext" call was not made. I first started using boolean returns after working with ATG Dynamo's chain of responsibility implementation (which is "classic") and had to debug it for all of the newbie developers on the project. They were given a two edged sword which is dangerous!

    Regards,

    Bruce
  3. Struts users who are interested in applying the Chain of Responsibility pattern will likely be interested in a fleshed out implementation of the CoR concept, which is currently available in the Struts CVS repository under directory "contrib/struts-chain". The original motivation was to decompose the standard RequestProcessor class that implements the Struts request processing lifecycle (and supports extension only via subclassing), and the CVS sources include a nearly complete implementation of that concept.

    Under the covers, this module uses Commons Chain (not yet released as a 1.0, but robust and reasonably stable, see http://jakarta.apache.org/commons/chain/). In the article author's terminology, this implementation uses the "non-classic CoR1" style, where the request is passed down the chain until one of the handlers (called a Command in this library) says "stop". Compared to the implementation examples in the article, however, there are a couple of twists on the suggested approaches:

    * The linkage of Command instances into a chain
      is externally stored in a Chain instance, rather
      than having "next" links stored inside the Command
      instances themselves. Among other things, this
      allows Command instances to be reused in more than
      one Chain, if appropriate.

    * Chain extends Command, so that chains can be
      composed out of mixtures of individual commands
      and sub-chains without doing anything special.

    * The state information passed between commands is
      gathered together in a Context object that can
      either be application specific, or used as a generic
      container (since it extends java.util.Map). This
      is helpful in avoiding things like dependency on
      web tier APIs (such as is seen in the calling sequence
      to a Struts Action), and assists in creation of unit
      test environments for individual Command instances
      (all a Command really does is modify state in the
      Context, so tests that monitor those changes are
      very easy to construct).

    Craig McClanahan
  4. I have been using IoC (Hivemind) but switched to CoR (commons-chain) based on the Struts v2.0? example in CVS.

    I found it just a bit simpler and just a bit more powerfull then IoC.

    .V

    ps: Struts v1.21 (and upcoming v1.22) is still using the old request procesor so you have to go to CVS to see it in action. So Spring is going AOP + IoC, but Struts is just OO + CoR, quite interesting.
  5. I've been working on a new ActionListener for JSF called, Strux. It uses Ant like dependencies to develop chains as the article describes, but uses JSF's definition of an Action Method. This allows you to include Actions Methods that actually handle things like Exceptions:

    ActionContext extends FacesContext;

    OrderControllerSessionBean
    public String handleOrderException() throws Exception
    {
       String result = "fail";
       try
       {
          ActionContext actionCtx = ActionContext.currentInstance();
          actionCtx.getActionScope().put("requestId","someVar");
          result = actionCtx.executeNext();
       }
       catch (OrderException oe)
       {
         // handle error
       }
       return result;
    }

    I'm still fleshing out some ideas, feel free to email me jacob at hookom.net with suggestions.
  6. HiveMind has pipelines that are easy to create and use and accomplis the same chain-of-command. So use what's best. Tapestry 3.1 will use many, many pipelines, since they act as a rich series of extension points for applications to contribute into. Aspect oriented configuration, anyone?
  7. Struts users who are interested in applying the Chain of Responsibility pattern will likely be interested in a fleshed out implementation of the CoR concept, which is currently available in the Struts CVS repository under directory "contrib/struts-chain".
    Great, this is definitely needed. Any search for "chaining struts actions" inevitably leads to a comment that it's against the struts philosophy, should be handled by business code, dastardly, evil, etc. (oh and there's a bit of chaining support in there, but it's evil, so even if you find out how to do it, don't). Yet, frameworks like webwork happily support it.

    The struts attitude to chaining has never made sense to me. I've found it extremely useful to maintain Actions:JSPs in a 1:many relationship - each action ships out to any number of JSPs it "owns", all of which report back to their daddy. Commonality among JSPs and actions can still be handled by all the usual OO/web techniques of subclassing, jsp:includes, tags, etc.. But as for concrete classes and "concrete JSPs", the relationship is one-to-many.

    Any time the user wants to start any other "module" (e.g. another area of the site), the request goes back to its "daddy Action", which follows the CoR pattern and says "I can't handle this - over to you Action X". Action X picks up the request and sends it to one of its JSPs. Chaining at its finest, and I can't see any reason why that approach is evil.

    Furthermore, chaining allows you to reuse actions. I can create a "Colour Chooser Action" for example. Any time an action needs a colour from the user, it just delegates to the "Colour Chooser", which goes back and forth to the user until a decision is made, and then calls back to the original user. This has been possible in Struts, but only by pleading with it and morphing it into something it's not.

    So it's great to hear that chaining will happen. CoR has proven to be a valuable pattern in web apps.
  8. Struts users who are interested in applying the Chain of Responsibility pattern will likely be interested in a fleshed out implementation of the CoR concept, which is currently available in the Struts CVS repository under directory "contrib/struts-chain".
    Great, this is definitely needed. Any search for "chaining struts actions" inevitably leads to a comment that it's against the struts philosophy, should be handled by business code, dastardly, evil, etc. (oh and there's a bit of chaining support in there, but it's evil, so even if you find out how to do it, don't). Yet, frameworks like webwork happily support it.
    I've read these opinions too, but I use Struts chaining and it works like a charm. Granted, what I use them for is simple input action/output action pairs, but it works and it makes my code much cleaner.
    The struts attitude to chaining has never made sense to me. I've found it extremely useful to maintain Actions:JSPs in a 1:many relationship - each action ships out to any number of JSPs it "owns"
    Totally agree with this, each JSP must have a... well, an owner.
    all of [JSPs] report back to their daddy.
    I do not like this "looping" back to "daddy" action. The similar approach is used in .NET, enhanced with IsPostBack, code-behind and other stuff. I do not like this scheme, I prefer an action class to receive the request and to display the result using JSP. Whatever is entered on that page, goes to a different action, not to the "daddy". Action receives input, generates output. It does not need to know where the input came from, it just data.
    Any time the user wants to start any other "module" (e.g. another area of the site), the request goes back to its "daddy Action", which follows the CoR pattern and says "I can't handle this - over to you Action X". Action X picks up the request and sends it to one of its JSPs.
    Right, but in my case the idea is more down to earth: "I can handle and validate input only, but for generating output I am redirecting you to Action X".
    Chaining at its finest, and I can't see any reason why that approach is evil.Furthermore, chaining allows you to reuse actions.
    Absolutely. One can have input, business and output actions and chain from one to another, defining the chain in struts-config.xml. Business actions can be totally reused, but output actions can be reused too. And if you use redirection, it helps to fight double submit problem.
  9. Swing event handling is an example of JavaWorld's proposal. So the pattern has already been exhaustively validated in the field. And why did Servlets get such a crappy behavioral design in the first place? Was Servlet filtering invented by Sun or JCP?
  10. ATG (www.atg.com) used a similar design for the process pipelines in the Dynamo Suite.
  11. *Grumble*[ Go to top ]

    While the goals of the JavaWorld author are laudable, there are good reasons why the Chain of Responsibility pattern works the way it does. Here are some things you wouldn't be able to do with the JavaWorld alternatives to the Chain of Responsibility:

    long startTime = System.currentTimeMillis();
    chain.proceed(request, response);
    log.info("children took " + (System.currentTimeMillis() - startTime) + "ms to complete");

    or

    MDC.put("SessionId", session.getId())
    chain.proceed()
    MDC.remove("SessionId")

    or even (simplistic example):

    String cachedPage = cache.get(request.getRequestURL());
    if(cachedPage == null) {
      CachingResponseWrapper cachingResponse = new CachingResponseWrapper(response);
      chain.proceed(request, cachingResponse);
      cache.put(cachingResponse.getContent());
    } else {
      response.getWriter().write(cachedPage);
    }

    *sigh*
  12. *Grumble*[ Go to top ]

    While the goals of the JavaWorld author are laudable, there are good reasons why the Chain of Responsibility pattern works the way it does. Here are some things you wouldn't be able to do with the JavaWorld alternatives to the Chain of Responsibility: [snip]
    I have to agree with this. The classic Chain of Responsibility is not just about performing operations in sequence, but is also about performance transforms and substitutions on the objects you are operating with. Think CoR + Decorator.

    Besides, it is a trivial matter to provide dumbed-down CoR utility classes to handle simple cases. Here is a Servlet filter example:

    public abstract class PreprocessingFilter implements Filter {
      public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        handle(request, response);
        chain.doFilter(request, response);
      }

      public abstract void handle(ServletRequest request, ServletResponse response) throws IOException, ServletException;
    }

    Voila, problem solved. You can even spice it up if you like (provide defaults for other Filter interface methods, cast to HttpServletRequest/Response, etc.).
  13. *Grumble*[ Go to top ]

    One more thing to say here..

    If some one wants to do some post processing after invoking the successive filter, this one can not be solved by having boolean to the method signature.
    ( kind of Interceptor pattern.. :) )

    public abstract class PreprocessingFilter implements Filter {
      public final void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        handle(request, response);
        chain.doFilter(request, response);
         postProcess();
      }

      public abstract void handle(ServletRequest request, ServletResponse response) throws IOException, ServletException;
     
      public abstract void postProcess();

    }

    ------
    Sato.
  14. Release to grumble[ Go to top ]

    Generally speeking,the post process call in a concrete chain node should be avoided. Look at the code sampele above: postProcess() call occrurs right after chain.doFilter(request, response) call. That means the post process is performed AFTER ENTIRE CHAIN EXECUTION. In that case why call post process in a chain node? It should be outside of the chain like this:

    chain.handle();
    postProcess();

    Of course,in the servlet filter environment, that can't be done, because the filter chain is started by servlet container. The alternative is that call postProcess() in the last chain node that will return false boolean value to tall container that it wants to stop chain, right beafore the boolean be returned.
  15. Release to grumble[ Go to top ]

    Generally speeking,the post process call in a concrete chain node should be avoided. Look at the code sampele above: postProcess() call occrurs right after chain.doFilter(request, response) call. That means the post process is performed AFTER ENTIRE CHAIN EXECUTION.
    That's not necessary true. What if you have more than one post-processing filter in the chain?

    For example, here are two possible examples of post-processing Servlet Filters:

    1. A "print-friendly" filter, that omits sidebars ads.

    2. A custom error handler, that catches exceptions generated by any lower filter, logs the message and routes to an error page.

    You could easily want both of these filters in the same filter chain, in combination with various pre-processing filters (such as a security filter).

    These filters might even be "stacked" as follows:

    1. Error Handling
    2. Security
    3. Print Friendly
    4. The page (chain end's)

    On the way "down", the security filter rejects bad logins, and the error handling and print friendly filters do nothing. The page is executed, then on the way "up", the print friendly filter alters the page's content, the error handling filter deals with any errors, and the security filter does nothing.

    To be even more slick, the security filter could do nothing more than throw a SecurityException, and rely on the error handling filter to log the security failure and route to the appropriate alternative, by making the login page the "error page" for a SecurityException.
  16. answer[ Go to top ]

    Add three more motheds in the chain base class:

    protected abstract void before();
    protected abstract void after();
    protected abstract void onAException(AException e);

    in start():
    public final void start(ARequest request)
    {
                this.before();
                try{
                  this.handle(request);
                }catch(AException e){
                   this.onAException(e);
                   //loop through prevousNodes, call onAException() on them.
                }
                this.after();
                if (next != null)
                   next.start(request);
    }

    This way, the individual nodes get chance to do whatever they want to do before and after handle(). In addition, when an exception hanppens, all executed nodes get notified.
  17. *Grumble*[ Go to top ]

    While the goals of the JavaWorld author are laudable, there are good reasons why the Chain of Responsibility pattern works the way it does. Here are some things you wouldn't be able to do with the JavaWorld alternatives to the Chain of Responsibility...
    Yep, absolutely right.

    Xwork interceptors do this, but, to make it easy for developers to create new Interceptors, we've got a base class you can extend that does this:

        public String intercept(ActionInvocation invocation) throws Exception {
            String result = null;

            before(invocation);
            result = invocation.invoke();
            after(invocation, result);

            return result;
        }

    then you can just implement before() and after(), and, if you need to change anything or maintain state around the rest of the call, you can just directly implement intercept().
  18. I saw the code. Isn't it CoR pattern with Strategy pattern.
    I mean in base class you have algoritm, this algoritm has call for next node.
    Subclasses just redifine some step of algoritm.
  19. I saw the code. Isn't it CoR pattern with Strategy pattern.
    I mean in base class you have algoritm, this algoritm has call for next node.
    Subclasses just redifine some step of algoritm.
    IMHO its CoR + TemplateMethod. Yah but conceptually we both think alike on this topic. shrini
  20. What is the difference?[ Go to top ]

    Isn't the proposed solution logically the same as the "classical" CoR pattern? In both approaches, the sub-class implementation must decide whether to continue the chain. The programmer still has to follow a documented convention. Return true if you handled it, returned false if you didn't. Also, as someone else already pointed out. The proposed version of the CoR pattern seems to disallow the scenario where all handlers are executed in the chain.

    public void handle() {

        // do stuff

        if( notHandled ) {
            next.handle();
        }
    }

    public boolean handle() {

        // do stuff

        return !notHandled;
    }
  21. What is the difference?[ Go to top ]

    I prefer/use the second approach is my stuff "next.handle(message)" because :
    * the component could transform the message send to the next
    * the component could send the message to several next(s) or a selected subset
    * the component could split the message
    * ...

    If you follow those rules you could (I did) create a simple (as complexe) event/message system (like SEDA).

    I create an Open Source framework based on this approach.
    (javadoc draft at http://www.dwayne.freesurf.fr/my-lib/api/my/yaep/package-summary.html).