Discussions

J2EE patterns: Session Adapter

  1. Session Adapter (12 messages)

    Problem: Sometimes there is a need to interact with a business logic, which has different implementations that should be developed as methods of one or more SessionBean(s). So you need an abstract interface for those business logic methods in SessionBean(s). May be sub-classing of the two or more SessionBeans from a super class does not solve your problem well, due to the ambiguities and difficulties of object-oriented relations among EJBs. In inheritance relationship between SessionBeans it is not clear whether considering the super class as another SessionBean or not. In addition, you need to create a super interface for their Remote/Local interfaces, which enforces extra work. However you can’t do any thing for their Home interfaces except for using reflection to call the create method – since the create method signature depends on the Home interface. Also if the different implementations of the business logic are in the same SessionBean, inheritance is not the solution anyway.

    Solution: To achieve this goal, use SessionAdapter classes that convert a SessionBean interface to the required interface of its client. An adapter is a simple java class that wraps a SessionBean and adapts its methods to a proper interface. The rationale of designing SessionAdapter classes is in the similar business logic that exists among a group of methods. However each SessionAdapter encapsulates a set of business operations related to a single logic.

    A SessionAdapter implementation can act as an adapter even for two or more SessionBeans. For example suppose that one operation of a business is supported by one SessionBean and the other operation is provided by another SessionBean. Also, each SessionBean may be adapted to its clients by different SessionAdapters. This case depends on your business and the degree of granularity of your SessionBeans. If they are very coarse-grained modules and they provide a group of business logics, you may need more than one adapter for a SessionBean for different parts of business. However it is not a rule and is the decision of designer.


    Participants:
    • SessionAdapter: the common interface which client needs to communicate with
    • ConcreteSessionAdapter: the implementation of SessionAdapter interface
    • BusinessService: the provider of business logic e.g. Remote/Local interface of a SessionBean
    • SessionAdapterFactory: the factory for creating SessionAdapter instances
    • Client: the caller of the service
    Benefits:
    • Facilitate a generic design for logics with different implementations
    • Reuse the client of business logic operations
    • Avoid the ambiguities of OO relationships among EJBs
    • Applicable on methods of one SessionBean
    Example:

    package examples.pattern.sessionadapter;
    public class SessionAdapterPattern {
      public static void main(String[] args) {
        Client client = new Client();

        Long groupId1 = new Long(10);
        Long groupId2 = new Long(20);

        Long personId1 = new Long(110);
        Long personId2 = new Long(120);

        Long companyId1 = new Long(210);
        Long companyId2 = new Long(220);

        client.addMemberToGroup(groupId1,personId1, "person");
        client.addMemberToGroup(groupId2,personId2, "person");
        client.addMemberToGroup(groupId1,companyId1, "company");
        client.addMemberToGroup(groupId1,companyId2, "company");

        client.getGroupMembers(groupId1,"company");
        client.getGroupMembers(groupId2,"person");
      }
    }

    package examples.pattern.sessionadapter;
    import java.util.List;
    import java.rmi.*;

    public class Client {

      public void addMemberToGroup(Long groupId, Long memberId, String memberType){
        try {
          Groupable groupable = AdapterFactory.createGroupableAdapter(memberType);
          groupable.addMemberToGroup(groupId, memberId);
        }
        catch (RemoteException ex) {
          ex.printStackTrace();
        }
      }

      public List getGroupMembers(Long groupId, String memberType) {
        try {
          Groupable groupable = AdapterFactory.createGroupableAdapter(memberType);
          return groupable.getGroupMembers(groupId);
        }
        catch (RemoteException ex) {
          ex.printStackTrace();
          return null;
        }
      }
    }

    package examples.pattern.sessionadapter;
    public class AdapterFactory {

      public static Groupable createGroupableAdapter(String adapterType) {
        if (adapterType.equalsIgnoreCase("person")){
          return new PersonAdapter();
        }
        if (adapterType.equalsIgnoreCase("company")){
          return new CompanyAdapter();
        }
        throw new RuntimeException("Unknown Adapter");
      }
    }


    package examples.pattern.sessionadapter;
    import java.util.List;
    import java.rmi.RemoteException;
    /**
    * This is the SessionAdapter interface
    */

    public interface Groupable {

      public void addMemberToGroup(Long groupId, Long memberId)throws RemoteException;
      public List getGroupMembers(Long groupId)throws RemoteException;
    }


    package examples.pattern.sessionadapter;
    import java.util.List;
    import examples.pattern.sessionadapter.person.*;
    import java.rmi.RemoteException;

    public class PersonAdapter implements Groupable {
      private PersonFacade personFacade;

      public PersonAdapter() {
        //here is the code for creating PersonFacade
      }

      public void addMemberToGroup(Long groupId, Long memberId)throws RemoteException {
        personFacade.addPersonToGroup(groupId,memberId);
      }
      public List getGroupMembers(Long groupId) throws RemoteException{
        return personFacade.getGroupPersons(groupId);
      }
    }

    package examples.pattern.sessionadapter;
    import java.util.List;
    import examples.pattern.sessionadapter.company.*;
    import java.rmi.RemoteException;

    public class CompanyAdapter implements Groupable {
      private CompanyFacade companyFacade;

      public CompanyAdapter() {
        //here is the code for creating CompanyFacade
      }
      public void addMemberToGroup(Long groupId, Long memberId)throws RemoteException {
        companyFacade.addCompanyToGroup(groupId,memberId);
      }
      public List getGroupMembers(Long groupId)throws RemoteException {
        return companyFacade.getGroupCompanies(groupId);
      }
    }


    package examples.pattern.sessionadapter.person;
    import javax.ejb.*;
    import java.util.*;
    import java.rmi.*;

    public interface PersonFacade extends javax.ejb.EJBObject {
      public void addPersonToGroup(Long groupId, Long personId) throws RemoteException;
      public List getGroupPersons(Long groupId) throws RemoteException;
    }

    package examples.pattern.sessionadapter.person;
    import javax.ejb.*;
    import java.util.List;

    public class PersonFacadeBean implements SessionBean {

      public void addPersonToGroup(Long groupId, Long personId) {
        System.out.println("Add Person "+personId + " to Group "+groupId);
      }
      public List getGroupPersons(Long groupId) {
        System.out.println("Return list of Persons in Group "+groupId);
        return null;
      }
    }


    package examples.pattern.sessionadapter.company;
    import javax.ejb.*;
    import java.util.*;
    import java.rmi.*;

    public interface CompanyFacade extends javax.ejb.EJBObject {
      public void addCompanyToGroup(Long groupId, Long companyId) throws RemoteException;
      public List getGroupCompanies(Long groupId) throws RemoteException;
    }

    package examples.pattern.sessionadapter.company;
    import javax.ejb.*;
    import java.util.List;
    import java.util.ArrayList;

    public class CompanyFacadeBean implements SessionBean {
      public void addCompanyToGroup(Long groupId, Long companyId) {
        System.out.println("Add Company "+companyId + " to Group " +groupId);
      }
      public List getGroupCompanies(Long groupId) {
        System.out.println("Return list of Companies in Group "+groupId);
        return null;
      }
    }

    Threaded Messages (12)

  2. ain't this a SessionFacade?[ Go to top ]

    I wonder if this is not in fact exactly a SessionFacade?

    ./pope
  3. ain't this a SessionFacade?[ Go to top ]

    No, it is not a facade but an adapter before the SessionFacade. The SessionAdapter acts between the facade client and the facade itself.
    It is used for situations that different facade layers should be used by the same client. The code example shows two different SessionFacades (PersonFacade and CompanyFacade) that are used by the Client with Groupable (SessionAdaptor) interface.

    I can send you the class diagram of this j2ee pattern to you if I have your email.
  4. Change the interfaces[ Go to top ]

    If I understand this description right, your "Session Adapter" is a "switch" that switches to ONE implementation if you provide memberType="person", and to the ALTERNATIVE implementation for memberType="company".

    I think a "cleaner way" (from an object oriented point of view) to handle all of this would be to:

    * change the interfaces and provide some data transfer objects instead of ID and TYPE as Strings (like client.addMemberToGroup(groupId1, new PersonDTO(123)); and client.addMemberToGroup(groupId1, new CompanyDTO(234)); ) --- and you can still use inheritance for these DTOs, although you are using EJBs...
     
    * As this solution would result in "instanceof" cascades, you should use the visitor pattern to dispatch to the right implementation

    Of course this implies that you are ABLE/ALLOWED to change the interfaces...
  5. RE: Change the interfaces[ Go to top ]

    SessionAdapter pattern applies "GOF Adapter" on SessionBeans which include your business logic. The pattern introduces a java bean named SessionAdapter inside of your J2EE system in order to make communication with SessionBeans approperiate to their clients. Many developers don't think of any other objects except for EJBs in their business tier while they are designing a system and by this pattern I wanted to introduce SessionAdapter to be used in between SessionBeans.

    The interface of Client object is not matter of discussion here and it can be in any form that you need. The only point is that the Client should use the SessionAdapterFactory to get a proper instance of SessionAdapter for its business.

    Also the "GOF Visitor" pattern can not be applied here for two reasons:
    1. In Visitor pattern the ConcreteVisitor should knows all kinds of "Element" objects to invoke them. But in the case mentioned here no object knows different kinds of objects that implemented SessionAdapter. For example nobody knows that there is a CompanyAdapter as well as PersonAdapter.
    2. In Visitor pattern the ConcreteVisitor delegates the execution of business to the "Element" objects. While in a J2EE system the business is implemented in SessionBeans rather than DTOs.
  6. RE: Change the interfaces[ Go to top ]

    I will try to rephrase my last statement:
    as I pointed out, the sample interfaces you used in your samples have some "strange smell" to me. And I think that only these interfaces made your SessionAdapter necessary.

    To put in other words: I am not saying, that the SessionAdapter itself is bad. You just don't have me persuaded that the SessionAdapter is necessary, except if you have some awkward interfaces.

    As for the Visitor Pattern:

    1. You are absolutely right about the Visitor having to know about all the classes in the Visited class's hierarchy. If you don't want that, don't use "Visitor".

    2. In the Visitor Pattern, the first (possible) place to put your impementation logic is in the Visitor's "visit(Element elem)" method, not in the Element itself (which just does something like "visitor.visit(this)"). So, of course, the DTOs would still be pure data stores (without business logic).
  7. RE: Change the interfaces[ Go to top ]

    As you know, a pattern is well understandable when you encounter the situation/problem the pattern solves, and sometimes before facing that problem its a little bit difficult to get its real idea. However, I don't want to say that my pattern is something different but it is important to see the problem it solves in practise.
    Recently, I have been involved in some systems that this case was very common in their businesses. Differnet entities with their own special businesses take part in another business wich is the same as that of others. For example Person and Company are two seperate entities with lots of business methods for each one, but both are one participant of another business which is Grouping. In another sub-system we had a concept of "Rule", and there were different Rules with different attributes so they had different database tables, but all of them had to be managed by the same interface their clients wanted to see them the same, so we used "Ruleable" interface as their SessionAdapter and each rule had its own implementation for it.
    Nevertheless, by applying this pattern, when you look at your business interfaces (in SessionBeans) you have designed very clear and underatndable interfaces and it facilitates unification of clients of your business methods.

    Thank you for your interest on this pattern.
  8. Session Adapter[ Go to top ]

    This is variably known as the Boundary pattern, the System Facade pattern, and so forth (see, for instance, "A System of Patterns" by Stal et. al.)

    You fail to mention liabilities/consequences; these include:

    1. Lower throughput in distributed scenarious / Call overhead
    2. Added complexity
    3. The pattern makes it difficult to use EJB tools such as Xdoclet because the idiomatic interface is wrapped
  9. This pattern is NOT Boundary or System Facade pattern at all. As I described in previous replies, this pattern doesn't act as a Facade, it is only an Adapter (See GOF Adapter).
    This pattern should be used when needed and it is not supposed to be used everywhere. Only a few methods of one or more SessionBeans that require adaptation to their clients will be adapted by a SessionAdapter. In the provided example, the PersonFacade and CompanyFacade both have more methods than what you see but just the methods that wrapped by an adapter are shown.

    About the consequences I disagree:
    1. There is no call overhead since the SessionAdapter is a simple java bean.
    2. All patterns if used inappropriately will add complexity but at right places they solve a problem well.
    3. You don't need to do any changes on your SessionBeans or make any consideration at design or development. Just create them as before and if it is required add a SessionAdapter behind them. And there is no conflict with any tools.
  10. There is no call overhead since the SessionAdapter is a simple java bean.
    Since when didn't simple java bean have call overhead? There's no free lunch in the interpreted world. Call-through-interface is 10x slower than direct calls and when applying your pattern to local EJB calls, this is definitly measureable (which has consequences, because local session beans are quite often used with finer granularity these days.)
    All patterns if used inappropriately will add complexity but at right places they solve a problem well.
    Oh, come on. Of course it adds complexity. There is more "stuff" to maintain. Yes, it may solve a problem (although I'm not sure what problem you are really trying to solve) but it still adds complexity.

    Most patterns adds to the overall complexity and most pattern writers "admit" that in their pattern descriptions.
  11. Every java program is full of interfaces and it is so common to use interfaces insted of direct calls. Almost all of j2ee and many of jdk classes are interface. Server side java programs interact with the application server via interfaces (Servlet & EJB), JDBC is interface,..., but nobody think like you about them. Call via interface is the same as call via a super class. These days nobody does care of call an object via a wrapper or interface, we have also passed those times.


    About the complexity issue:
    I recommand you to understand the problem before presenting your opinion about it.
    In first view, it seems that the pattern adds some complexity, but don't forget that you'll have more complexity if you don't use it. The patterns are used when without them a difficulty exists and it should be solved generally all over that application, so using a patten is inevitable otherwise you'll have a dirty code with weak maintainability.
  12. Reza, I'm in general sceptical about any pattern description having only benefits and no liabilities. Unless you list some of the obvious liabilities, nobody is going to take your pattern seriously.
  13. Can you explain the point of redocumenting the GOF adaptor pattern. All you have done from what i understand is apply it to session beans. If that is all then it is a big waste as most of the "classic" patterns can be applied to many different architectures so why fill more space. Lets have a CORBA Adaptor and a Web Service Adaptor.

    Also on another note your interfaces use "id" arguments. We are now in an object world. Why not pass in objects ? Entity beans or POJOs work nicely for database access and remove the underlying implementation from your interfaces making them more reusable. What if group id becomes a string ? Whereas if I pass a group object we are "safer".

    Bruce