Better Performing Data Marshalling & Interoperability

Discussions

J2EE patterns: Better Performing Data Marshalling & Interoperability

  1. Java Serialization presents a major overhead in the marshalling of objective and structural types in EJB. With the advent of RMI over IIOP, compatible application servers can provide a significant performance boost through the marshalling of IDL types rather then serialized types. The marshalling of an IDL type over IIOP does not require the significant overhead of building an object map to Serialize the data to a byte[]. Secondly these types are portable to other CORBA based systems, even running on other platforms or languages like C++.

    The patter includes the idea that all remote interfaces include only IDL stub types. These types can be viewed as the Model in an MVC design. At the server and client side, a Controller is responsible for the conversion of these structural IDL types to more "Java" like types. These Controllers can both convert inputted data into an IDL type for marshalling, or iterate over the type to de-marshall.

    Example:

    Lets say we want to pass an Account Balance. Normally in EJB we would pass a Collection of a Serializable type. Instead well work with an IDL type in the Remote itnerface. So in IDL we might define

    struct AccountInfo
    {
       string accountId;
       long balance
    }

    The Java language binding of this would be

    public final class AccountInfo implements org.omg.CORBA.portable.IDLEntity
    {
       public String accountId;
       public int balance;

       public AccountInfo(String p0, int p1)
       {
          accountId = p0;
          balance = p1;
       }
    }

    The interface org.omg.CORBA.idl.IDLEntity is simply a marker which itslef implements java.io.Serializable. In other words, the IDL stub is also a Serializable type. This means that this will work for those application servers who do not implement the RMI over IIOP completely as they will pass the AccountInfo object as a serialized type properly.

    The Remote interface would then be designed as

    public AccountInfo[] getInfo() throws java.rmi.RemoteException

    However you may find it unresonale to work with such a simple structural type, where your developers are used to more objective like and more Java like datatypes. The use of a Controller class to "package" the data into the IDL type can be used. A class implementing the Collection interface would be the best choice for a Controller. In this way developers simply create an AccountInfo for each element in the collection and add it to the Collection. You can then convert the Collection to an array for marshalling.


    For instance:

    // Assuming we have a valid ResultSet

    Vector v = new Vector();
    while(rs.next())
    {
       v.add(new AccountInfo(rs.getString(1),rs.getString(2));
    }

    AccountInfo[] array = new AccountInfo[v.size];
    v.copyInto(array);
    return array;


    On the client side, you can also place the passed array into a Collection type for manipulation as a controller.

    Conclusion

    Using IDL types rather then Serializable types represents a significant performance improvement in EJB systems, while at the same time allowing for interoperability across CORBA ORB implementations. Application servers properly implementing RMI over IIOP will realize these improvements while non compatible application servers can still marshall via serialization.


    Threaded Messages (24)

  2. Isn't the disadvantage of using idl structs(compiled via. idltojava) type java classes is that there is no polymorphism. You would have to pass some kind of indicator to tell the receiving class what type of child java class to create and fill in with the final java class data sent over the wire-marshaled over the wire. Also, there is some processing required to convert the idl final java class into the regular java class.

    Are you saying that the benefits outweigh the disadvantages?
  3. If you look at the IDL type as the Model and the converter as the controller, then the controller handles the polymorphism. The is some basic packaging that need be done to convert the data in the controller into the IDL type. Basically this is because developers would prefer to work with a "standard" Java datatype like a Collection rather then a simple structural type.

    Also dont forget these IDL types are actually Java classes generated by your IDL to java compiler so its not like there is real "conversion" just abstraction over the simple type to a richer type.

    For instance, we provide an IDL implementation of a java.sql.ResultSet. You code simply looks like

    ResultSet rs = stmt.executeQuery();
    // Convert to IDL type
    TabularResults.ResultSet trs = IDL.getResultSet(rs);
    return trs;

    The client then does

    java.sql.ResultSet = SQL.getResultSet(remote.doQuery());

    The client side implementation of java.sql.ResultSet is a simple iterator that scrolls over the structural IDL type.

    The performance boost is MAJOR here. The marshllers do not have to serialize the entire object to marshall it. Rather the standard GIOP serialization is used which is MUCH better performing then Java serialization. The secondary advantage is that the IDL type is now useable by non-java clients. Or, clients running outside of the world of EJB, such as CORBA.

    I believe the advantages much outweigh the disadvantages.

    Dave Wolf
    Internet Applications Division
    Sybase
  4. What kind of performance improvements have you seen? Percent improvement? How much faster is it to construct a idl java type class from a regular java type class on the client side, then send this to the bean(via. RMI/IIOP), then have the bean class convert this back into a user friendly java class and then process like normal vs. having Serialization do this for you?
  5. I ran a simple test and will post the source here. I created two methods in a stateless session bean, both passig similar structural data one using a Java mapping of an IDL struct the other using a Serializable object. My results showed depending ont he size of the object being passed, the IDL type being 25%-50% faster to marshall then the Serializable type. This is a HUGE marshalling improvement by simply switching datatypes. The test does include a Vector as a simple controller converting to the IDL array. I do not create a Controller on the client side, but if you use an Iterator as the client side controller there is basically no overhead in creating it anyway and there is then no real conversion being done.

    This is 25% to 50% boost im marshalling time basically for free by using a container which properly implements RMI/IIOP and using an IDL datatype. On top of this you get interoperability to other CORBA based systems.

    All tests were run using Sybase EAServer 3.6 and EJB 1.1, JDK 1.2.

    struct PersonModelIDL
    {
        string fname;
        string lname;
        long age;
    };

    This mapped to

    final public class PersonModelIDL extends java.lang.Object implements org.omg.CORBA.portable.IDLEntity
    {
        public String fname;
        public String lname;
        public int age;

        public PersonModelIDL(String fname, String lname, int age)
        {
    fname = fname;
    lname = lname;
    age = age;
        }

        public PersonModelIDL()
        {
    super();
        }
    }

    The Serialized verion of the same structural data was:

    public class PersonModelSerial extends java.lang.Object implements java.io.Serializable
    {

        // add your data members here
        private String fname;
        private String lname;
        private int age;

        public PersonModelSerial(String fname, String lname, int age)
        {
    fname = fname;
    lname = lname;
    age = age;
        }

        public int getAge()
        {
    return age;
        }

        public void setAge(int a)
        {
    age = a;
        }

        public String getFirst()
        {
    return fname;
        }

        public void setFirst(String f)
        {
         fname = f;
        }

        public String getLast()
        {
       return lname;
        }

        public void setLast(String l)
        {
    lname = l;
        }
    }


    The business method code of the Bean was:


    /**
    * getPersonBySerial Method
    */

    public java.util.Collection getPersonBySerial() throws javax.ejb.EJBException
    {
        Vector v = new Vector();
        for(int i = 0; i < 500; i++)
        {
            v.add(new PersonModelSerial("Dave","Wolf",29));
        }
        return v;
    }

    /**
    * getPersonIDL Method
    */

    public PersonModelIDL[] getPersonIDL() throws javax.ejb.EJBException
    {
        Vector v = new Vector();
        for(int i = 0; i < 500; i++)
        {
    v.add(new PersonModelIDL("Dave","Wolf",29));
        }
        PersonModelIDL[] array = new PersonModelIDL[v.size()];
        v.copyInto(array);
        return array;
    }


    Dave Wolf
    Internet Applications Division
    Sybase


  6. Hi....
      if understood correctly, this article recommends using the Structs wherever we just need the data and do not want to operate on that data. It sounds great but my concern is from the development perspective. because in order to use it we need an IDL2JAVA compiler and which is specific for a secific product for example Visigenic has it's own. But say i wanna write an EJB using the suggested pattern and deploy it in Weblogic then i probably need an idl2java compiler from Weblogic which it does not provide. so what one might wanna do. Please correct me if i am wrong.
    regards
    aseem
  7. The fact that BEA doesnt ship with an IDL2Java compiler or even properly marshall IDL types via RMI/IIOP doesnt mean the pattern is lacking. I have shown above a 50% improvement in datatype marshalling performance. I would suggest aquiring a more capable container.

    You can use the IDL2Java compiler which is free with the Java2 platform.

    Structs however are very easy mapping and can be done by hand. See above.

    Dave Wolf
    Internet Applications Division
    Sybase
  8. Think this is a good pattern to keep in mind. It doesn't hurt to try. Also, it is easy to make a idl type java object, just copy the normal java object and make all the variables public and also implement:

                  org.omg.CORBA.portable.IDLEntity

    Then have a constructor accept all the needed variable. I am sure this pattern would work under Inprise. Are we saying that Weblogic can not handle this type of object- all parameters have to implement java.io.Serializable?
  9. org.omg.CORBA.portable.IDLEntity itself extends java.io.Serializable so in effect the IDL types are also serializable. But a good container properly implementing RMI/IIOP completely would realize this was an IDL type and use the more effecient GIOP marshalling.

    Ive found some containers, like BEA always marshall using Java Serialization. There is no porting issue, but they miss out on the performance boost.

    Dave Wolf
    Internet Applications Division
    Sybase
  10. Thanks for the info. !!!
  11. I just want to throw another voice into the discussion pointing out that:

    a) This pattern is completely portable (meaning the code you write will run on any Java2 VM)

    b) This pattern will work with our products (VisiBroker, Borland AppServer)

    c) This pattern should work with any product that correctly implements RMI-over-IIOP.

    d) An IDL compiler is NOT required to use this pattern. Even if you were using our product, you would not use our IDL compiler to use this pattern. This pattern is realized by implement a Java class using a particular design pattern. A CORBA 2.3 based compiler should detect that you are using this particular design pattern in your Java code, and generate optimized mashaling code. Again, IDL is not involved.

    e) The performance benefits of this pattern will only be realized on marshaling engines that correctly implement RMI-over-IIOP. That is, there will be no performance benefit (nor performance loss) if you are currently running on RMI-over-T3, or some other proprietary protocol.

    In summary, there is quite a bit to gain by using this pattern, and nothing to lose.

    Also, I want to list exactly the requirements for implementing a Java class which can be marshaled most efficiently by an RMI-over-IIOP engine:

    i) The class must be "final" (that is, it cannot have subclasses).

    ii) The class "extends" nothing (that is, it must implicitly extend java.lang.Object, only).

    iii) The class "inplements" org.omg.CORBA.portable.IDLEntity (which is a marker interface provided in Java2).

    iv) All data fields are markes as "public".

    v) Two constructors are provided, one with no parameters (that is, a default constructor), and one taking a parameter for each field (that is, a setting constructor).

    -Jonathan K. Weedon
     Architect: VisiBroker, Borland AppServer
  12. Great clarifications Jonathan. Ill be publishing an article on this subject in the upcoming Java Developers Journal as well.

    Dave Wolf
    Internet Applications Division
    Sybase
  13. Dave,

    Very interesting results!

    I am very interested in seeing your article when it comes out in JDJ. I'm hoping you can answer some other questions about this pattern, though. Questions that relate to the bigger picture.

    ============================================
    Question #1
    ============================================

    You are proposing a 'Controller', but really this becomes a class that is doing a lot of instantiations to convert the structs into "meaningful" objects.

    Let me give a common scenario a lot of us can relate to. Let's say I query the database and get 1000 results back (customers -- for instance), I have to do the following under this pattern :

    1) Query the database and get back 1000 row resultset.
    2) Instantiate 1000 "meaningful" data objects and populate them with resultset data.
    3) At the Impl level, break these data objects apart into structs. Again... 1000 more instantiations for the structs.
    4) Send structs across CORBA.
    5) Receive structs within the "Controller" class and instantiate 1000 "meaningful" data objects using the structs as parameters.

    Are you saying that all of these extra instantiations as well as the "Controller" class overhead is still faster than simply using Java serialization? I'd like to see your benchmark results to back that up. You might get 25%-50% increase in the transport, but are we talking 10 milliseconds as opposed to 20 milliseconds? What is the unit of measure? Are you certain that the savings in step #4 is enough to offset the overhead of step #3 and step #5?

    Mind you, we're not just talking about the time it takes to pass the object over CORBA. That's a part of it, but not the whole pie. Taking into account all of the instantiations (which are expensive in Java), is this really faster than Java serialization -- in the long run? If so, what exactly are the time savings?

    ============================================
    Question #2
    ============================================

    Another person in this thread mentioned the lack of polymorphism/inheritance in this approach. Obviously, these characteristics are necessary for any good object-oriented design.

    In order to achieve a good OO system design, we would have to write the extra code for these "controller" classes as well as the code for instantiating/populating our "true" data objects from structs.

    In a medium to large scale system, do you think the overhead in the development effort and code maintenance is worth using this approach? This approach would require writing many additional controller/converter classes for each data object/struct that was needed.

    ============================================

    Lastly, I want to say I'm not trying to be a spoiler here. There are dozens of design patterns out there which tout their benefits compared to other design patterns. This is another one which touts its own benefit of marshalling/transmission speed (which I've honestly never known to be a bottleneck in EJB -- we use Jaguar EAS 3.5).

    I am a huge fan of "the big picture". Savings in time and money over the course of a project. I'm also a huge fan of good OO design techniques. Because I know there are quanitifable benefits of reuse and extensibility.

    Looking at this pattern, I see that more code has to be written (and therefore maintained) along with the expense of more instantiations. Not only that, but the object model becomes 'flattened' at the transport layer.

    A huge benefit to Java serialization is that we were able to maintain the integrity (states) of our objects in a distributed system with very little code... and with good performance.

    I'm trying to quantify your benefits/costs. Any supporting data you have would be greatly appreciated, Dave!

    Thanks!

    Rick Grashel
  14. Hi Rick,

    Let me try to address your points,

    1) The controller Im talking about here is a simple packager/iterator. I assume that the final Data Object people would work with is the struct type. If you look in my sample code above, you will see what I mean. On the "producer" side, the structs are instantiated. This same cost would occur with a serialized type as well. They are then added to a Collection. The same would also happen in a serialized type. The only seperate work is rather then the collection being returned, the array is generated by the collection. This can actually be a basic 0 net operation if the controller itself holds the data in an array. See my code above, but I do all this work in my benchmark so my numbers include this overhead for both options.

    As for my numbers, they include total round trip from request of the method, to all data marshalled back. This includes packaging on the server and unpacking on the client. For 100 records my numbers we like 80ms vs 120ms. My point is however this is FREE boost. And add on now that the system is fully interoperable across any CORBA compliant system.


    2) The issue with Polymprhism is that the structs themselves cannot extend another class. However I would consider this less of an issue with a smart controller which could handle any subclass of Object.

    I understand your points, and this is why we have our patterns discussed and not just supplied. My questions back are

    1) Why is this more code then a serialized object? See my code above. I see no more code.

    2) You may feel there is not bottleneck in serialization, but have you tested the impact of serialization?

    3) Cant a state be represented in a data object format?

    Dave Wolf
    Internet Applications Division
    Sybase
  15. Thanks for the comments, Dave!

    I'll address your questions, first.

    1) Its more code because you are assuming that we use the structs themselves as our application data objects. IMHO, the limitations of the structs really defeat the purpose of good OO design. So to compensate, we as developers would have to create "friendly" data objects (as kevin buhr pointed out previously in this thread) which are polymorphic and benefit from the use of inheritance and encapsulation.

    2) No. We haven't tested the impact, but that's probably because we haven't had any issues with performance where data transmission is concerned. Likewise, we don't optimize database queries which are performing acceptably. Same goes with any other part of the system. If we're happy with 120ms for 100 records, why would we do the extra work to get 80ms -- while creating other overheads -- development/maintenance cost of additional code.

    3) A state can be represented in a struct, but only in a 'flat' fashion. Without encapsulation and inheritance... really... that kind of limits a state. Are we saying that all members of data objects should always be visible to other data objects? Are there cases where we might want a data object to have a 'protected' member? Or private? How about getters/setters? I think these factors are extremely important.

    Good OO design would say that we design our objects in a way that models the business system :

    public class Person extends ApplicationObject
    {
        private String firstName;
        private String lastName;
        private int genderCode;

        // Accessor methods
        public String getFirstName() { return firstName; }
        public String getLastName() { return lastName; }
        public int getGenderCode() { return genderCode; }

        // Mutator methods
        public void setFirstName(String value) { firstName = aValue); }
        public void setLastName(String value) { lastName = aValue); }
        public void setGenderCode(int value) { genderCode = aValue); }
    }

    public class SalesPerson extends Person
    {
        private long salespersonNumber;
        private String salesRegion;

        // Accessor methods
        public long getSalespersonNumber() { return salespersonNumber; }
        public String getSalesRegion() { return salesRegion; }

        // Mutator methods
        public void setSalespersonNumber(long value) { salespersonNumber = value; }
        public void setSalesRegion(String value) { salesRegion = value; }
    }

    *These* are the types of objects that I am talking about passing over CORBA. They are application/user friendly. Very friendly to front-end GUIs. They are a more accurate representation of the system being modeled.

    If we use this pattern, we would undoubtedly have to write additional code to break apart and rebuild these objects as they pass (as structs) across the transport layer. But with serialization, we don't have to do this at all. We can just pass this object as 'Person' (or even as 'ApplicationObject'). I think it is hugely beneficial... from a design point of view.

    Maybe it is a matter of preference. And you know what they say about opinions... everyone has one. I think some people just prefer a more OO approach to design instead of a more flattened architecture. But again, I'm still not seeing the big gain. 80ms versus 120ms for 100 records. 80ms is very good. 120ms is very good. If we extrapolate those results in a linear fashion, 10000 records would take 8000ms (8 secs) versus 12000ms (12 seconds). Noticable, but still very minimal... 4 seconds difference for 10000 records... that is extremely tolerable for that volume of data. To me, only at the very high records volume ranges would you see a truly unacceptable delay.

    In closing, I have this personal guideline I try to always follow: "Never treat the exception like the rule." If a system had a situation where CORBA marshalling performance was an issue in a certain part... then I say... fine... use this technique to get the needed performance gain. But leave the rest of the system alone if it performs well, and is of good OO design.

    I'm really coming to believe that no one design pattern fits all needs(especially in larger distributed applications). Sometimes you have to go outside of the 'common' application design pattern to accomodate performance problems or other 'X-factor' issues. But treat those exceptions as such. But IMHO, people shouldn't model their entire system around a pattern to address performance problems that don't really exist for them. All the while sacrificing the great gains achieved through good OO design.

    Use this pattern for a small sub-system if you need the gain in performance. But if you don't need it, don't use it. Use more widely accepted and validated OO patterns for the majority of the system (Person/Role patterns, Command patterns, Factory patterns, Iterator patterns... etc).

    That is where Java serialization is of great benefit. We can really use any pattern we like without any extra code or limitations (aside from the 40ms delay for 100 records).

    I dunno... that's how I'm seeing it, Dave. I still look forward to seeing your article. I've read a lot of your posts in the newsgroups, and I truly appreciate how much you help out the Sybase community, myself included. I'm sure you'll have several new golden nuggets for us to chew on in your article!

    So many other container-vendors do not give near the level or quality of service that you guys as Team Sybase do. You guys are to be commended for that!

    Thanks again!

    -- Rick Grashel
  16. Rick,

    I would like to add to Dave's comments a few of my own as
    well as responding to some of your comments.

    (1) The pattern forces you to think about portability of
        data. When you use a serializable Java type with
        private fields, you should be aware that the Java RMI
        to IDL mapping does not require the mapping of methods.
        A client who has to access your type through a
        generated value type with no methods will not
        necessarily appreciate the "OO" design.

    (2) Clients written in any other language than Java will
        almost certainly be extremely difficult (if not next
        to impossible) to write.

    (3) How would you handle 'generic' objects such as row sets?
        I suggest in this case an IDL-based type is far
        preferable. (see (5))

    (4) I have seen numerous cases where inheritance is used
        and then the programer writes code like:

        void myMethod(Person person)
        {
            if (person instanceof SalesPerson)
            {
                SalesPerson sp = (SalesPerson)person;
                ...
            }
            else
            {
                ...
            }
        }

        This is not polymorphism in any useful sense, and it
        defeats the purpose of using inheritance in the first
        place. Such code is likely to be subject to change
        whenever you introduce a new sub-class of Person, in
        which case a more 'generic' type is often no less
        approriate. Generic types can be modelled quite well
        in IDL even without valuetypes.

    (5) When dealing with large data sets, performance can
        quickly become a major issue, e.g. serialized row sets
        can easily use five or more times more marshalled space
        than IDL types accomodating the same state.

    (6) Just because you are careful about the use of
        inheritance/polymorhism in parameter types does not
        mean that your remote interfaces cannot benefit from
        the same.

    (7) Remember overloading :-)

        void myMethod(Person person)
        {
            ...
        }

        void myMethod(SalesPerson person)
        {
            ...
        }
    ___________________________________________________________

    Evan Ireland
    EAServer Architect
    Sybase, Inc.
  17. Thanks for responding, Evan. I'll address your points, as well.

    1) I am talking only about Java to Java implementations. Dave was comparing speeds between this IDL entity pattern and standard Java serialization. So that was the playing field I was going by. Obviously Java-based serialization does no good to where a C++/COBOL client is concerned.

    2) See #1. Of course. Again, I'm assuming Java to Java. It is not the requirement of all systems out there that they talk to different types of clients. Designing a system that way when isn't necessary is a mistake in my opinion. If we're saying use this pattern when you have to talk to different languages, then I agree. Use this pattern. But if not... then don't use it. The right tool for the right job, as the saying goes.

    3) All I would (and do) return to clients is java.util.List(s). Lists can contain anything. And as long as what the List contains is serializable, you get all of that for free through Java-based serialization. It is the same thing you guys are talking about except without the limitation of struct-types.

    4) I agree. The code you wrote is pretty bad, and I wouldn't agree with that approach at all. Polymorphism with a huge IF/ELSE-IF block is almost as dumb as passing structs around and then recreating 'friendly' objects based on the contents of these structs.

    Assume that the 'Person' is abstract. And that is has an abstract method 'doYourJob()'. Every type of person must 'do their job' in a different way. Instead of the code you wrote, I like this code much better to demonstrate just ONE of the benefits of polymorphic behavior (excuse the crudeness of the example, only for demonstration purposes) :

    public class Salesperson extends Person
    {
       public void doYourJob()
       {
          System.out.println("I am doing my SALES job.");
       }
    }

    public class AccountingPerson extends Person
    {
       public void doYourJob()
       {
          System.out.println("I am doing my ACCOUNTING job.");
       }
    }

    public void tellPersonToDoTheirJob(Person person)
    {
        person.doYourJob();
    }

    Obviously, the outcome would be different depending on the type of person passed in. That is the big benefit of passing things around as their superclasses/interfaces. You don't have to KNOW what they are in a specific sense. You only need to have an 'abstract' idea of their 'implementation' (interface). That is my idea of polymorphism.

    5) There is no question about that. I agree completely. See my previous post. But again, I think a common mistake in this industry is the make a RULE out of an EXCEPTION case.

    Do we bother optimizing database SQL queries that already perform well? Of course we don't. Why not? Because it doesn't make sense. If we are experiencing bad performance in a particular situation, we can deal with that separately. There is no one-size-fits-all, cookie-cutter solution to designing medium/large-scale distributed systems. I think the best that people can hope for is one-size-fits-ALMOST-ALL solutions. That is what I shoot for. Treat the exception cases as exceptions. But don't throw out a good design just because you have a couple of aberrant JSP/screens that are returning a large result sets.

    6) I agree completely. Every part of a system can greatly benefit from the careful application of inheritance and polymorphism.

    7) Overloading is definitely has its place. I think its probably misused a lot... really due to laziness. When someone has to overload, I begin to wonder if the design of the 'method being overloaded' was right to begin with. But again... one-size-fits-MANY. So sometimes you have to overload. Honestly... I wish Java had operator-overloading more than anything. Ah well... maybe next version... or the next after that. ;)
  18. I just see that what you want is provided by the Controller classes which take the flat data being passed around and construct good OO views of this flat data. In the end, the data itself truly is flat.

    And I disagree that Java serialization is "free". Easy yes, but far from free, its actually damned expensive.

    Evan and I are damned big OO biggots, but at some level OO is abstracting data. All this pattern proposes is you only ever pass raw data models, and use controllers to abstract that data.

    I think spending all your time insisting everything be passed as an OO type is like the data modellers who insist every database be 100% normalized, even at the expense of performance and portability.

    We'll agree to disagree :)

    Dave Wolf
    Internet Applications Division
    Sybase

  19. Rick,

    My comments on your comments on my comments (on Dave's
    comments) :-)

    (1) Even in the Java to Java case, the issue about having
        no methods in the mapped value-type can apply.

    (3) I can define a List type in IDL using a recursive union
        and a couple of struct and sequence types. It too can
        store anything (at least in the respect of arbitrarily
        complex structured types).

        It is no harder to work with the resulting Java classes
        than with java.util.List (perhaps even easier). And it
        is interoperable and portable and performs well!

    (4) If the only difference between Person and SalesPerson
        is due to polymorphic implementation of some methods,
        how is a client programmer going to make meaningful
        use of any aspects of the data content of a SalesPerson
        that is not held by a Person (i.e. extracting or
        inserting fields values even if it is via getter/setter
        methods)?

        In your new example, I would suggest that doYourJob
        probably belongs in a remote interface.

        And don't get me wrong, I love polymorphism. It can
        however be used in the wrong places.

    (5) This pattern is neither a rule nor is it an exception.
        That is evidenced by the pattern name. If performance
        and interoperability don't matter, then the pattern
        doesn't apply.

    I would caution all EJB programmers from assuming that
    an all-Java design (Java at client and server) is
    appropriate for a distributed system that is expected to
    have a medium-to-long-term production phase (and we've all
    heard of those stop gap, run for at most one month projects
    that stretch out to be many years. Who knows, in 3 years we
    might all be writing C# clients!

    I think Java seralization is like a rope, great for tying
    things together, but be careful, you can also hang yourself
    with it!
  20. Well, we're getting into pretty dogmatic discussions now.

    The only thing that I will say is that your point stated in #5 is all I was trying to impart. If people don't have to worry about interoperability and their systems are performing well, then don't use this pattern. To me, its overkill. A bandaid without a wound. That's the only message I wanted to impart.

    I won't get into the discussion as to whether or not Java will be around in three years and we'll all be writing C# clients for our EJB projects. I also won't get into the issue of whether or not data objects should have getters and setters and use inheritance/polymorphism... or whether we should just pass the value-types/structs around. Again... its really up to the designers.

    People can go read their object-oriented books and derive their own conclusions about what characteristics make a system design more or less object-oriented. Then choose the approach that best fits their needs and their budgets.

    What you stated in item #5 is the only message I wanted to impart. So on that point, we are in agreement.

    I'll leave the religious arguments alone. I'm sure we all have enough of those on our existing projects as it is. Heheh. It makes for good discussion by the water cooler, but probably isn't appropriate for this thread. Heheh.

    Thanks for the good discussion!
  21. I tend to agree with Rick. I think it doesn't make sense to force IIOP communication style within pure Java environment. As Rick mentioned, it adds development and maintenance overhead that I found unexceptable for pure Java development on medium/large projects.

    Following the same line, if interoperability with non-Java clients was a requirement, I would use the following approach:

    1. Develop an EJB "as usual", assuming having only Java clients. That will allow Java clients to use the bean "as usual".

    2. Use the pattern proposed here to implement methods to be used by IIOP clients.

    Step 1 becomes then the main development thread, which remains in line with already established development practices, and we add IIOP communication style only to EJBs that need it.

    Using IIOP communication style within pure Java system in order to improve data marshaling performance is, perhaps, a good candidate for an antipattern. Of course, whether this is a patern or an antipattern can be established with 100% certainty only by extensive usage of this approach in real development.
  22. Let me add small correction to my previous post:

    Using IIOP communication style within pure Java system in order to improve data marshaling performance is, in my opinion, an antipattern, if applied to all EJBs without discrimination.

    However, it could be a usefull pattern if applied to performance sensitive EJB methods. In this context, a performance sensitive method has to be defined as the one which gains significantly by using IIOP communication style. For example, 33% gain (120ms -> 80ms) is usualy not sufficient to qualify a method as performance sensitive.
  23. It is interesting to note that while IDL Entity Types are
    valid RMI/IDL types as per the Java Language to IDL Mapping
    (OMG document 00-01-06), and thereby according to the EJB
    specification they are valid as parameter or return types,
    when mapped back to IDL they must be wrapped up within a
    boxed value type (it's not hard to imagine a better
    reverse mapping, but those Java folks seem to just love
    the idea of propagating null values and it's too late to
    change it now anyway). That is,

        module M { struct S { long value }; };

    maps to Java:

        package M;
        public final class S
            implements org.omg.CORBA.portable.IDLEntity
        {
            ...
        }

    which then maps back to a boxed value type in module
    org::omg::boxedIDL containing the original struct. So an
    EJB server which supports RMI-IIOP must marshal CORBA
    struct types as value types containing structs. This adds a
    little overhead (for marshalling the value type wrapper) but
    at least the embedded struct data is marshalled as usual.
    (Similar comments apply to enum, union, exception types).
  24. The pattern you suggest may not be needed, in some cases you
    can get acceptable performance improvments by implementing your own java serialization methods for

    private void writeObject(java.io.ObjectOutputStream out)
         throws IOException
    private void readObject(java.io.ObjectInputStream in)
         throws IOException, ClassNotFoundException;


    In my experience this improvment is very simple to implement and is also transparent.

  25. Ummm but why write your own when RMI IIOP gives this to you?

    Dave Wolf
    Internet Applications Division
    Sybase