4 Replies Latest reply on Jun 26, 2008 7:15 PM by kragoth

    Design: HtmlUnit and our RichFacesClient

    ssilvert

      It's clear that we will need a new version of the RichFacesClient that uses the new HtmlUnit-based JSFClientSession. The good news is, this should be MUCH simpler than the old HttpUnit based RichFacesClient.

      I have a few goals in mind for this:
      1) It should be easy to use
      2) It should be complete. That is, it should provide support for all RichFaces components.
      3) It should serve as a model by which someone can create a client for other JSF component libraries.

      Feel free to add other goals if I've left something out.

      I want to discuss the design of the new RichFacesClient. The old one uses simple delegation when it needs access to the core JSFClientSession. So you create the RichFacesClient like this:

      RichFacesClient richClient = new RichFacesClient(jsfClientSession);

      RichFacesClient just holds onto an instance of JSFClientSession.

      Of course, the alternative to delegation is inheritance. So RichFacesClient could just inherit from JSFClientSession.
      public class RichFacesClient extends JSFClientSession

      Then the JSFSession becomes a factory. You would get a RichFacesClient like this:
      JSFSession jsfSession = new JSFSession("/index.jsf");
      RichFacesClient client = (RichFacesClient)jsfSession.getJSFClientSession();

      And we would have some way to configure JSFSession as a factory that creates whatever client you are expecting. This approach is nice because you have one object that you use for all client-side services.

      The downside comes in when you want to mix in a second component library. For instance, what if you want to use both RichFaces components and the Seam components like <s:link>? Since java doesn't have multiple inheritance, this gets a little tricky.

      One solution is to use interfaces for the mixin:
      public interface JSFClientSession { // Today's JSFClientSession methods }
      public class JSFClientSessionImpl implements JSFClientSession {}
      
      public interface RichFacesClient { // RichFacesClient methods }
      public class RichFacesClientImpl implements RichFacesClient {}
      
      public interfaces SeamClient { // SeamClient methods }
      public class SeamClientImpl implements SeamClient {}
      
      public class MyJSFUnitClient implements JSFClientSession, RichFacesClient, SeamClient {
      
       // delegates
       private JSFClientSession jsfClientSession;
       private RichFacesClient richFacesClient;
       private SeamClient seamClient;
      
       public MyJSFUnitClient(JSFServerSession jsfServerSession, Page initialPage)
       {
       this.jsfClientSession = new JSFClientSession(jsfServerSession, initialPage);
       this.richFacesClient = new RichFacesClient(this.jsfClientSession);
       this.seamClient = new SeamClient(this.jsfClientSession);
       }
      
       // JSFClientSession delegation methods
       public void click(String componentID) throws IOException
       {
       this.jsfClientSession.click(componentID);
       }
      
       // etc.
      
       // RichFacesClient delegation methods
      
       // SeamClient delegation methods
      
      }

      Then the developer would use his class like this:
      JSFSession jsfSession = new JSFSession("/index.jsf");
      MyJSFUnitClient client = (MyJSFUnitClient)jsfSession.getJSFClientSession();

      The nice thing about it is that now the developer is free to call any method he needs from that one MyJSFUnitClient instance.

      The downside is that the developer has to write the MyJSFUnitClient class. For the RichFaces/Seam combo, we could provide an implementation in JSFUnit proper. But for someone who wants to mix in something like TomahawkClient and SeamClient, they would have to write it themselves.

      Thoughts?

      Stan







        • 1. Re: Design: HtmlUnit and our RichFacesClient

          Initial comments:

          goals = good, I think that pretty much covers it.

          It seems to me the delegation pattern promotes a very clean separation between component support libraries and allows the user to instantiate whatever they need.

          I don't see much difference between delegation and inheritance (doesn't much simplify the code for the user) except that it has the undesirable side-effect of making it difficult to use multiple component libraries at once. This just seems a too little rigid.

          The interface driven method seems a little complex and counter-intuitive for a user. Although, the "average" user (read: users that use richfaces) wouldn't need to implement their own client class.


          Just a thought: The client side support for richfaces, and other component libraries seems like it should be an add-on for HtmlUnit, or at least at that level. Does HtmlUnit already have (or planned) support like this? (It seems like a natural extension)


          Brian

          • 2. Re: Design: HtmlUnit and our RichFacesClient
            ssilvert

             

            "bgregory" wrote:

            It seems to me the delegation pattern promotes a very clean separation between component support libraries and allows the user to instantiate whatever they need.

            In general, I also prefer delegation. I rarely design for inheritance. Looking back at what I wrote, you are right that we don't get much bang for the buck by getting fancy with it.

            "bgregory" wrote:

            Just a thought: The client side support for richfaces, and other component libraries seems like it should be an add-on for HtmlUnit, or at least at that level. Does HtmlUnit already have (or planned) support like this? (It seems like a natural extension)

            I don't know if it would work. Right now, RichFacesClient delegates to JSFClientSession. And JSFClientSession sometimes needs access to the component tree through an instance of JSFServerSession.

            So the added value of JSFClientSession over plain HtmlUnit is that JSFClientSession knows that it's in-container and that it's using JSF. This is true for component library clients like RichFacesClient as well. That's something that can be leveraged by JSFUnit, but not by HtmlUnit.

            Stan

            • 3. Re: Design: HtmlUnit and our RichFacesClient

              I'm still learning "what's inside" but I do remember seeing code in the client that uses the server component tree to look up what is needed. I'm going to have to work my way through and think a little bit.

              I've been primarily thinking about this in terms of testing a JSF application, where you would be "poking" the user interface and then inspecting the server side to ensure the correct state information is produced, but I can see a case where the "unit under test" is a JSF/AJAX component, and you would need to be able to inspect the layers in between.

              Brian

              • 4. Re: Design: HtmlUnit and our RichFacesClient
                kragoth

                Hey guys,

                I aggree with bgregory on this one. I think the delegation solution is more intuitive and plugable.