Designing a WebLogic EJB application
This is a collection of design tips and debugging points to keep in mind when building applications with EJB (Enterprise JavaBeans). A number of these tips apply to remote object models (RMI and CORBA) as much as they do to EJB. You should also read and be familiar with:
Design TipsBack to basicsWhen beginning a development project that involves using Enterprise JavaBeans, it is useful to remember these basic points as you formulate your design.
Design the user interface first!Don't fall into the trap of developing the object-oriented design (the bean) first, and then producing a user interface that's little more than a view of the object model.Intuitive behavior, support for expert typists and power users, inter-screen communication, information-rich screens that aren't just pretty icons are just a few of the features that should be given attention before you start designing the beans. If the user interface is not fast enough, go directly to the database if you have to. This may sound heretical, but the user doesn't care how it happens -- they just want to get their job done! Why do you never touch the bean directly?A common question and point of confusion: why do you never touch the bean directly?Because if you didn't have the Container, then either the client (and all the different clients that might work with this bean) or the bean itself would need to know how to provide all the object services. Instead, the ejbObject provides an interface, and through that interface you work with the bean. The container acts as an intercessor between the client and the bean. Though this looks like it is adding an entire layer of complexity, what it means is:
If these services were provided by the bean, there would be problems of duplication of code, lack of portability from one implementation to another, and the benefits of the component model would be lost. Also, remember the difference between CORBA and EJB, and where transactions fit in:
Components should be coarse-grainedA common reasoning seems to be as follows: "Objects are good, objects that are remotely accessible are better, and remote objects with declarative transaction and security attributes are even better". Challenge this idiom as much as possible.User-time and network-time are not free. Don't make all objects of your system available remotely. Line items in an invoice, cells of a spreadsheet... these are fine-grained objects. They shouldn't be accessed frequently over a wire. Instead, have their containers provide a bulk-CRUD (Create, Read, Update, and Delete) API to access anything at a lower level. Using inheritance and EJBeansInheritance works when building groups of related beans that share common code, but you should be aware of issues that the EJB specification does not address.Consider ejbCreate() and ejbFind(). ejbCreate() for a bean-managed EJBean is expected to return a primary key. Any class inheriting from this bean class cannot have an ejbCreate() returning a different primary key class, even if this new class is derived from the base classes' primary key. ejbFind() has exactly the same issue. Now, let's look at what happens if this process of implementation inheritance changes the interface: perhaps the derived bean adds a new method that is meant to be accessible remotely. The situation would be:
Because AHome.create() and BHome.create() return different remote interfaces, you cannot have the BHome interface inherit from the AHome interface. You can still use inheritance to have methods in the beans that are unique to a particular class, that inherit from a superclass or that are overloaded in the subclass. See the example Enterprise JavaBean subclass Child example packages and classes. EJB ContainersWhat is an EJB Container?Though the Enterprise JavaBean 1.0 specification talks about "containers", it does not say exactly what a container is, beyond speaking of it as the place "where an enterprise Bean object lives, just as a record lives in a database, and a file or directory lives in a file system."In the WebLogic Server, the container is the environment, the cocoon around the bean that implements EJB semantics. In other words, the container is the entire EJB service. There are third-party products that are described as "EJB containers", but they typically provide a specialized service such as persistence; they do not provide EJB semantics (such as when to call ejbActivate, ejbLoad, ejbStore, etc.) or when to start and stop transactions. With that context, many services that are described as requiring containers -- such as encapsulation of another system's API for their business logic -- actually require a set of EJBeans rather than a container. It's not required nor a good idea to re-write lower level services merely to provide a face to an existing public interface. Support for multiple containers in WebLogicOnce WebLogic provides an interface to the transaction service (which will be part of our completion of JTA), anyone would be able to implement another EJB service under a different package hierarchy. Our EJB implementation is built on standard APIs (RMI, JNDI, JDBC and JTA); these APIs are our "container-to-server interface". Note that simply that supporting multiple EJB home implementations does not constitute multiple container support.When to use a custom containerThere is a place for a custom container -- such as Versant's "Enterprise Container", providing access to the Versant ODBMS. But before embarking on such an endeavor, define what the container is, what it is to do, and determine if you can't achieve the same goals by writing EJBeans instead.Dynamic classloadingFor information on EJBeans and dynamic classloading, see the Tech Support Guide Troubleshooting your WebLogic EJB application.Using Entity BeansBeans == rows in a databaseTreat beans just as you would rows in a database. Note that a bean doesn't necessarily correspond to a single row (or any row, for that matter); this advice is merely to treat it with the same caution you'd apply for accessing rows in a database. That means:
Don't fight database optimizationsRelational databases are optimized for bulk access. To avoid having to go to the database for each object, prime the EJB cache using a suitable finder to bring in a group of relevant entity beans with as few queries as possible -- ideally a single query.Don't distrust database optimization and cachingSome of the biggest/meanest/fastest systems in deployment today have been done with the help of TP monitors such as Tuxedo running on top of relational databases. A TP monitor provides stateless services. Consequently, the application goes back to the database for every single request, without caching anything. Guess what? It works well. Don't underestimate database caching capabilities.Avoid distributed transactionsAvoid distributing a transaction as much as possible, especially across disparate systems. This is especially true of beans wrapping legacy systems; do not try them in the same transaction. It's better to have a shared database, workflow or queued messaging setup between the two.Always write a custom primary key class for entity beansFor container-managed persistence, the EJB 1.0 specification requires you supply an application-defined primary key class. But for bean-managed persistence, the class can be any non-primitive type (such as java.lang.String).However, since the signature of findByPrimaryKey() requires you to supply the primary key class as the argument, if you ever change the implementation from bean-managed to container-managed persistence, you will be forced to change the home interface and consequently affect all current users of that bean.
Moral of the story: always write a custom primary key class
unless you are certain that you are never going to change the type of persistence.
Of course, the bean must set the isModified() flag to true in any place where the state changes. What's not obvious is that methods such as ejbRemove(), ejbActivate(), and ejbPassivate() must also set this flag to true, because the bean could be pooled and reused. Using isModified() is prone to errors. In the case of container-managed persistence, you will need to set the flag to false in the ejbLoad() and ejbStore(), as shown in the container-managed example, if you want the container to take advantage of this optimization. Though ejbStore() is called before the EJBean is actually written to the database, in the event an exception is raised while writing to the database, the framework will assume that the EJBean has been modified, regardless of the setting of the flag. Using Session Beans'Model' provided by session beansIn the Model-View-Controller pattern, the view is the GUI form and the model is the piece of code that supplies data to the screen.In a client-server setup, the model typically lives on the same server as the view and talks to the server. It's preferential to have the model on the server, in the form of a session bean; a smart Java stored procedure, if you will. (This is similar to having a servlet providing support for an HTML form, save for the fact that the model session bean does not worry about the actual presentation.) There should be one model session bean instance for each GUI form instance, acting as the form's representative on the server. For example, if you have a list of 100 network nodes to display in a form, you might have a method called getNetworkNodes() on the corresponding bean which returns an array of values relevant to that list. In contrast, imagine an approach where the GUI form calls a custom Finder method on the NetworkHome, and for each of the 100 EJBObject references returned, it goes back to the corresponding bean to fetch more data. You might as well go out for a cappuccino while the list gets populated. To put this another way: never expose your entity beans directly to the client. (Though we show examples such as the beanManaged example where an entity bean is exposed to the client, this is only for the purpose of illustrating accessing entity beans only.) The model session bean should be accessing the entity beans. This keeps the transaction time short while the transaction itself is more localized and requires considerably less network bandwidth. Network transparency is a mythThe EJB/RMI synchronous calling mechanism is convenient, but it is not network transparent; you have to deal with various flavors of java.rmi.RemoteException.It is not cheap either, considering that it consumes a thread on the server and having it block during a transaction eats up a precious resource. Having a session bean act as a model eases the load on the system. By keeping the overall transaction short and local to the server, it reduces the possibility of other threads locked up due to contention. |
|
Copyright © 2000 BEA Systems, Inc. All rights reserved.
|