May 13, 2007, 4:36 p.m.
posted by oxy
Item 11: Recognize the cost of vendor neutralityJust as Java holds portability (remember "Write Once, Run Anywhere"?) as a high priority, J2EE similarly holds vendor neutrality, or the ability to take a compiled J2EE application and run it within any vendor's J2EE container, as a high priority. Three of the eight stated goals in the front of the EJB Specification, for example, center on vendor neutrality. The Servlet Specification, the JSP Specification, JDBC, JMS—all explicitly mention the ability to take a compiled application and run it under any compliant vendor's container without modification as a design goal. The question is, do you care? Certainly, J2EE applications intended for resale as third-party components or standalone applications will want to embrace portability and vendor neutrality as much as possible, in order to maximize the potential client market. There's no sense in restricting an application to using the Oracle database, for example, if the application can be made database-neutral by avoiding Oracle-specific extensions and using only SQL-92 syntax. Doing so means clients who aren't currently using Oracle don't have to be convinced to buy it when you pitch them your application. The fact remains, however, that far more often the J2EE application is an in-house system developed exclusively for use by a single company and/or subsidiaries and maybe a few business partners. The target platform—the hardware, the operating system, the database, and so on—is usually decided well in advance of the first line of code. As a result, there is usually little need for the system to be portable across platforms/containers/databases. More importantly, standards exist as a baseline of functionality to be expected on any platform. If that were the only consideration, what point would there be to multiple vendors selling the exact same product? You could throw a dart at a dartboard to pick your vendor. The fact is, however, that vendors would prefer that your money go into their pockets rather than their competitors' and so will try to come up with reasons you should purchase their product instead of the other guys'. In the business world, this is called value-add, and it plays a key role in this question of portability because vendor value-add is what Sun is banking on to keep J2EE alive in the face of that Large Software Company in the Pacific Northwest. The idea is this: you write your system to the specifications, and then, at the time of deployment, you pick which vendor you wish to use based on price, scalability, clustering capabilities, and so forth. Each vendor will add a host of features that justify the additional cost. You pick the vendor product that best fits what you want to do, and everybody lives happily ever after—because you write code to the specification, the vendor can work its magic under the hood to make your system faster/better/cheaper. But vendors don't stop with just optimizations under the hood. For example, consider Item 17: when a transaction involving an EJB entity bean is begun from within an EJB container against the data store, data must be synchronized between the EJB container holding the bean instance and the data store holding the bean's persisted state on disk. This is necessary because, by default, the specification assumes that the container cannot have exclusive access to the actual data storage layer (the database). Therefore, it's necessary to "refresh" the container's view of the data at the start of the transaction via a call to ejbLoad on the entity bean. Whether this results in an actual trip to the database or not is entirely up to the bean—a CMP bean will, whereas a BMP bean's behavior depends, as always, on how the implementation is written. Most EJB container vendors, however, see an opportunity here to provide some value-added flavor to their implementation and offer some kind of "exclusivity" flag that programmers can flip on when there are no other systems accessing the database concurrently. This allows the EJB container to assume that it is the only gateway to the database and therefore make all kinds of caching optimizations, such as not forcing the refresh on an entity bean at the start of a transaction. Because of the improved performance this brings, many programmers use that flag. Doing so, however, immediately renders the bean nonportable—not so much because the bean won't compile or run in another container but because the executional semantics surrounding the bean have changed. The rules of the environment in which the bean is coded, developed, and tested shape the ultimate direction the bean's implementation takes, and if those rules are suddenly changed, redundant code is executed or, worse, bugs are introduced. For many developers, the argument for portability hinges on the idea that "management can always change its mind at a moment's notice" and that the tools and/or platform used at the start of the project can suddenly shift underneath you. This could happen for a variety of causes: merger, buyout, a change in strategic partnerships, or a nice game of golf courtesy of the vendor's sales rep. The actual reason itself is irrelevant—you're suddenly stuck with an entirely different platform, and if you coded to that platform's specific features, you're facing a major rewrite. Other developers argue that this is a red herring, that such shifts in direction can never be planned for and never completely accounted for within a given system. Just as the company may decide to change its strategic partnership from Oracle to IBM, the company could just as easily make its choice to go from Oracle to Microsoft, implying a shift from Java to .NET. This is a situation that no J2EE container or specification can accommodate, so writing specification-compliant servlets and EJBs has zero effect. There's more to a shift in platform than just recoding the system. Truth be told, rewriting the code is often one of the smaller tasks compared with everything else that needs to be done: porting the database schema, porting the database data, retraining the support staff, integrating the new platform into the overall support structure, and so on. For existing applications, particularly if the system has been in use for some time, these noncoding costs can run well into the millions of dollars, not even considering the cost of purchasing the new platform or hardware (if shifting hardware/operating systems). And if historical precedent teaches us anything, it's that standards ultimately won't matter as much in the future as they do now, for the simple reason that the market will tend to centralize behind three or four major vendors, each of which will claim conformance, present their own slight "quirks" in that conformance, and provide a host of value-added features that will slowly come to be recognized as a de facto part of the standard. One example that comes to mind is the ANSI SQL standard (SQL-89, SQL-92, and SQL-99). While the standard exists and continues to refine itself, the major database players continue to provide specific extensions to SQL, and relational databases in general, that aren't portable to other vendors' products. Each vendor supports the majority of the SQL standard, but none are 100% compliant with the SQL-92 Specification. Thus far, for the majority of J2EE applications, however, this hasn't presented a problem—the differences between Oracle's variant of SQL and Microsoft's variant are well-known and therefore less of a "porting issue" when attempting to adapt schema from one to the other. In the end, the decision regarding portability versus vendor value-added features is ultimately a value judgment, a personal and cultural choice. Make that choice consciously, not out of habit. For those systems that need to maximize portability, stay away from any sort of vendor value-added feature until deployment. Make sure to test against multiple vendor containers, as well as the reference implementation, to find any possible scenarios in which you've accidentally made use of a vendor feature without realizing it—for example, one vendor may do different remote stub generation than the specification calls for, allowing you to get away with a simple cast in your client code instead of the specification-required form using PortableRemoteObject.narrow. For the rest of us, however, portability quite often takes a back seat to two other concerns, performance and scalability. Decide on your container vendor as early as possible, and take every vendor value-added feature offered to you. Flip on that exclusivity flag. Use the vendor's distribution protocol instead of RMI/IIOP. Run the container inside the database, where entity beans can set and retrieve data directly against the table without having to make a SQL call. Or, if the vendor offers it, write entity beans against stored procedures instead of allowing the container to generate the SQL. (This provides you with another hook point to optimize the data-retrieval layer of your system.) Set whatever configuration options the container requires to use local transactions against the database instead of distributed ones. In short, take every single optimization the vendor can give you. |
- Comment