Big design decision: what to return to clients of session beans?


EJB design: Big design decision: what to return to clients of session beans?

  1. This message was posted to jdo-listserv at techtrader dot com, but I thought it was appropriate and would get more airplay here.

    -----Original Message-----
    From: james dot webster at cardsetc dot com [mailto:james dot webster at cardsetc dot com]
    Sent: Thursday, June 21, 2001 9:10 PM
    To: jdo-listserv at techtrader dot com
    Subject: JDO design patterns


    This list seems a little quiet but I thought I'd attempt to provoke a
    discussion :-)

    JDO is being touted by some as a replacement for Entity Beans in J2EE. For
    a web-based application the architecture would roughly look like this...

    Browser -- [HTTP/POST] --> Servlet -- [RMI] --> Business Services (Session
    Beans) -- [Direct Invocation] -->
         Business Data Objects (JDO Instances) -- [JDBC] --> Datastore

    Lets start by assuming that the Servlets and Session Beans are running on
    separate machines (ie. a 'web' cluster and an 'object' cluster).

    What do the Session Beans return to the Servlets?

    My initial thought is to return the Business Data Objects directly. On the
    object cluster, the Business Data Objects will be JDO enhanced, on the web
    cluster they won't be - but it will be the same code.

    But perhaps there need to be Value Objects that are created from Business
    Data Objects. The problem is, writing a set of classes that just act as
    containers for
    all of the Business Data Objects seems to be a little wasteful. But I guess
    generic solutions are possible...

    James W.
  2. I've seen a lot of discussion on value objects, and I agree with you, having value objects along with your persistence capable objects seems to be a duplication of a lot of stuff.

    Since I see session beans as usually representing a request-response paradigm, I define XML Schemas for requests and responses, then use an XML Schema-to-Java code generator (until JAXB implementations become available) to create serializable Java request and response objects. You could define one request and response object per session bean method, or you could consolidate the request & response objects as you see fit. This way, you achieve several benefits:

    1. You can support clients that can speak XML (over RMI, RMI-IIOP, COM via JIntegra, etc)
    2. You are not returning more data than the client needs. For example, if a session bean client only needs two properties of a persistence capable business object, your response object can be defined to hold only those two values, whereas a value object would probably return all of the business object's properties, sucking up more bandwidth than really is necessary. The tradeoff? More classes, of course, but when they're generated code, the impact is not quite as severe as hand coding request and response classes.

    It is important to note that I let the problem domain drive the discovery of our persistence capable business objects, and let use cases drive the discovery of the coarse-grained session bean methods, as well as temper how far you go during the analysis of the problem domain.

    For example, if you had a persistence capable business object called com.yourcompany.Corporation, there might be session bean methods to perform operations on Corporation objects, like:

    // begin file com/yourcompany/service/

    package com.yourcompany.service;

    import javax.ejb.EJBObject;
    import java.rmi.RemoteException;

    public interface CorporationServiceEJBRemote extends EJBObject {

        String execute(String request) throws RemoteException;
        CreateCorporationResponse createCorporation(CreateCorporationRequest request) throws RemoteException;
        DeleteCorporationResponse deleteCorporation(DeleteCorporationRequest request) throws RemoteException;
        GetCorporateGroupsResponse getCorporateGroups(GetCorporateGroupsRequest request) throws RemoteException;
        SpamCorporationsResponse spamCorporations(SpamCorporationsRequest request) throws RemoteException;
    // end file com/yourcompany/service/

    The "String execute(String request)" method would take an XML request, parse it enough to know which method to call, marshal the XML's request part into the appropriate Java request object, call the appropriate method, capture the return value of the method in the appropriate Java response object, marshall the response object back into the XML response, and return the response to the client.

    The other methods could also be called directly from Java clients -- the String-based methods are there to help with non-Java clients. Since the Java request and response objects are serializable, Java clients could bypass the overhead of XML marshalling.

    In this example, you can see an explosion in the number of *Request & *Response objects. Since they're generated, there's not much to worry about on that front, but you do have to be careful of the code that uses these generated classes -- changes may have a ripple effect.

    Anyway, just my $0.02.

  3. This reply was originally sent to jdo-listserve at techtrader dot com, so please excuse some of the TechTrader-specific hype :)

    >What do the Session Beans return to the Servlets?
    >My initial thought is to return the Business Data Objects

    This is a great question. If your data objects are serializable, they can certainly be returned directly from the session beans. There is one gotcha when doing this, though: when a JDO instance is serialized, the entire object graph of related instances is serialized as well. While this is exactly how 'normal' java objects act when serialized, some people are surprised when serializing one JDO object that happens to have a lot of persistent relations ends up sucking in almost the entire DB! This problem is easily surmountable by either:
    a) Making sure to only return instances with few relations (it's generally good to minimize inter-object dependencies anyway).
    b) Making instances 'transient' before returning them and nulling relations that aren't needed by the web tier. (pm.makeTransient (obj))

    Of course, one doesn't have to return jdo instances at all. Other options include packing only the needed data into special containers (tedious, but possibly automated in some generic way through reflection or otherwise), or simply using the session tier to perform transactional operations only.

    In this latter case, very little data would have to be returned from EJBs (only the result of the update/insert/whatever or the calculation made). The web tier could use a PersistenceManager with 'nontransactionalRead' set to true to gather the data it needs itself, without the overhead of EJBs. If you're concerned that the business logic -- including how to find the right instances -- should all be performed in Session Beans, have them return one or more Query instances (which are serializable and can be re-constituted through the PersistenceManager.newQuery (Object otherQuery) method) that the web tier can then execute to get the data it needs. Either way, having the web tier do the work in 'read-only' situations and having Session Beans perform transactions gives you the best of both worlds and is amazingly efficient.

    A final option is to use JDO and entity beans together, where any JDO instance that is to be returned to the client is first proxied with an entity bean wrapper. TechTrader has plans to work on this in the future, but not in the near term.

    >On the
    >object cluster, the Business Data Objects will be JDO
    >enhanced, on the web
    >cluster they won't be - but it will be the same code.

    This doesn't matter. The deserialized instances -- even if enhanced -- will lose their association with the PersistenceManager during the transfer process and will therefore act exactly like 'normal' object instances.

    If anyone else has some ideas on possible usage patters, please speak up! JDO is a new technology to all of us.

    -- Abe White

    p.s. Plug for new and somewhat relevant product: Within 2 weeks, TechTrader is going to release a new product that uses bytecode enhancement to transform normal java classes into stateful/stateless Session EJBs, with no change to either the class code or any code that used the original class. So using the above Session Bean patterns will get a whole lot easier.
  4. James,

       If the servlet needs the Customer's name and address, then it would
    make sense to return a Customer (business data object, I'm assuming)
    directly to the servlet. The servlet could deserialize into a
    non-enhanced class providing that care is taken to maintain the
    serialization ID between the two versions. However, the enhance class
    could also be used on the servlet as well, if you don't mind the code
    bloat, although none of the PM stuff is available since JDO is not on
    the web cluster. The deserialized business object is non-transactional
    and non-persistent -- in other words, a JDO transient object, and by
    definition it should act just like an object of the unenhanced class.
    So yes, as I see it, value objects will be returned to the client of the
    remote service that itself uses JDO.

       On the other hand, if the servlet needs to verify that the client has
    at least a net wealth of 100K in all brokerage accounts before serving
    them up the page that allows them to apply for options trading, then
    this would be just another service that the business service offers with
    no business objects being returned.

       The return trip from the first case is interesting as well. The
    servlet may accept changes from the client to the name and address, and
    now wants to send the modified Customer object back to the business
    service to be validated and persisted. The business service like the
    servlet receives a JDO transient object upon deserialization. It must
    now find the JDO persistent object that matches. If primary key JDO
    identity is being used, this is straightforward, since the application
    can construct the JDO identity from the transient instance. If
    datastore JDO identity is being used, then its a bit trickier. Last I
    heard, Draft Two of the Proposed Final Draft which hasn't yet been
    released will allow extracting a text key from the toString method
    of JDO identity object, which can then be used with a TBD factory method
    to recreate the key. So this text key could be sent down with the
    Customer object and then, when it's sent back, used to recreate the
    datastore JDO identity and find the persistent object. Having found the
    matching persistent object, the class should probably have a utility
    method to update the persistent object with the value object.
    Thereafter, the code performs validation and either commit or rollback.

       It is required to match the returned value object to the persistent
    object on the return trip, since just calling makePersistent() on the
    returned value objects will either lead to another distinct object in
    the datastore or a duplicate primary key exception.

       I don't think there is any general purpose need to write wrapper or
    container classes.

       Does that sound reasonable?

    David Ezzio
    Yankee Software