Application Design Issues FAQ

From Support
Jump to: navigation, search

Design Issues

Can a bean's remote interface extend an existing interface?

Absolutely! The bean's remote interface can certainly extend an existing interface. The only restriction here is that all interfaces that it extends must have all methods throw java.rmi.RemoteException.

1.Thus the Sort example can be modified by renaming the bean's remote interface to SortRemote (modifying other files as needed). Then define a new interface called Sort and move the two business methods from SortRemote to Sort:

public interface SortRemote extends Sort, javax.ejb.EJBObject {
}

public interface Sort {
    Vector sort(Vector v, Compare c) throws java.rmi.RemoteException;
    Vector merge(Vector a, Vector b, Compare c) throws
java.rmi.RemoteException;
}

2. An alternative design is to have all your root classes to extend java.rmi.Remote, or some sub-class thereof (e.g., javax.ejb.EJBObject). So, you could change Sort to:

public interface Sort extends java.rmi.Remote {
  Vector sort(Vector v, Compare c) throws java.rmi.RemoteException;
  Vector merge(Vector a, Vector b, Compare c) throws java.rmi.RemoteException;
}

and then SortRemote could remain:

public interface SortRemote extends Sort, javax.ejb.EJBObject {
}

This probably is close to the previous design, and does a better job of expressing the fact that Sort is actually a remote interface (although not necessarily an EJB interface). It was already implied that Sort was a remote interface, by the fact that all its methods throw RemoteException, so why not make it explicit?

3. An argument to not have Sort extend java.rmi.Remote is that one may want the client to use Sort and be unaware whether the object behind Sort is remote or not. This should allow one to switch between an Object By Value and a remote object mechanism without changing the client.

Can I use inheritance for the Home interfaces?

Let's look at an example: Let's assume we want to have a default create for the Person bean. The Home must have the following signature:

public interface PersonHome extends EJBHome {
  public Person create() throws ...;
}

Now let's say we want a default create for the Employee 
bean too.  We would like to have:

public interface EmployeeHome extends PersonHome {
  public Employee create() throws ...;
}

This is not allowed in Java. The overriding method cannot differ only in the return type.

So, one may then try to avoid the overload on the return type, by simply inheriting the method in the base class:

public interface EmployeeHome extends PersonHome {
}

Oops! Now you have broken EJB compliance, which states that the return type of a create must be the remote interface.

Obviously, something has to give. What needs to give is the EJB specification, which needs to be enhanced to support inheritance of home interfaces properly. This is probably slated for EJB 2.0. For now, you cannot inherit your home interfaces.

Having said that, note that you can always inherit your remote interfaces (as in the above). Your homes still must be:

public interface PersonHome extends EJBHome {
  public Person create() throws ...;
}

and:

public interface EmployeeHome extends EJBHome {
  public Employee create() throws ...;
}

This is really the way to go until the EJB specification is updated.

If there are a number of shared methods between the two intefaces, you can put the shared methods into a common base class, of course.

How should I design my current CORBA apps with an eye to migrating easily to EJBs later?

Use the following guidelines:

1) Break all service IDL into a factory and the actual implementation. This will map to Home/Remote interfaces nicely.

2) Store all your factories in CosNaming. This will map nicely to JNDI.

3) Use design patterns which mimic the EJB types: stateless session objects, stateful session objects, and entities. The corollary of this is to avoid building services which are not supportable by EJB. For example, avoid singleton objects, which are not easily supported in EJB.

Following these basic guide-lines should allow most of your services to migrate to EJB with minimal modification.

Note, however, that there is nothing wrong with using a hybrid architecture. It is reasonable to expect that some services will be easily ported to EJB, and some will be hard to port. Any EJB server built on IIOP should support hybrid deployments easily, allowing you to migrate your services over time, or in some cases, not at all.

What should I do if I need a singleton?

Say you want a stateful, shared, server-side object, i.e., a singleton. EJB does not provide this design pattern explicitly. You could use an entity bean, but these are really intended to be stored in either a database or some (other) legacy backend. Writing a stand-alone entity bean (which provides full transactional semantics, as entities should) is overkill for a simple singleton. Or, you could use a stateful session bean, but these are intended to be single-user. (In reality, it is not possible for a container to enforce this rule, and thus it could be ignored, but again we find ourselves doing something quite unnatural in EJB.)

So we reach the inevitable conclusion that EJB provides a useful set of server-side design patterns, but does not provide ALL the useful patterns. Developers will find that jumping out of EJB (presumably into CORBA) will be useful in a number of cases. And of course, our product makes this transition painless, in that it preserves the transactional semantics, and security, when crossing the EJB-CORBA boundary. This preservation of distributed system semantics is not easily doable (and is not being done by competitors' products) if the EJB container is not built on top of IIOP (e.g., CORBA).

Sessions Wrap Entities Pattern

A common observation goes like this ...

If I execute several setXXX() methods against an entity bean as a group, I get some rather intense database activity. I was wondering if something could be done to reduce the number of SQL UPDATE calls (and perhaps their associated ejbLoads) to 1 total, as opposed to 1 per setXXX().

You are missing two key concepts: (1) session beans, and (2) transactions.

It is a very bad idea to make individual set or get calls on an entity bean within its own transaction. You really want to bundle a bunch of accesses to one or more entity beans into a single transaction, thereby reducing the number of DB accesses.

The "right" way to do this is to never access your entity beans directly from you client, but always to access them though a session bean. Basically, you want to use a session bean which says "get me a bunch of information", and then this session bean can do all the getting and setting on the entity beans.

Why this design pattern? Because then you can specify "Required" as the transaction attribute on the session bean, and all the entity accesses will run in the session bean's transaction. Since each entity bean will be loaded only once per transaction (or not at all, if using caching) this will have a huge impact on your overall performance, by drastically reducing your DB access.

Another approach is to use client demarcated transactions. Here, you start a transaction on the client, access all the beans you want, and then commit the transaction. Again, since each bean is loaded and stored at most only once per transaction, this will have the same effect. However, generally client demarcated transactions are frowned upon, because it is much harder to control a client than to control the container.

One may suggest making the updates asynchronous, which is a very bad idea, because the whole idea of transactions is that you know it worked (e.g., committed) or you know it failed (e.g., rolled back). Saying "we'll try to do it, maybe later, don't worry about the results" breaks the whole model!

Of course, you could always spawn off a client thread which does the client- demarcated transaction commit asynchronously, if you are in a hurry, and come back to poll the results later.

How should I create/refer to another bean from within a bean implementation?

Getting a reference to a server bean from another server bean is just like getting the reference from the client. Also, the EJB 1.1 spec states that your deployment descriptor must indicate all ejb's that your ejb will lookup. You should be using the EJB-ref mechanism to hook your EJBs together. You can use direct JNDI names but this is inflexible and does not leave any room for the Container to optimize access to collocated beans. For example, say I have a deployment where beans A and B are both replicated in two different containers, for performance and/or availability reasons. Clearly, you want every instance of A in container 1 to use instances of B also in container 1.Likewise for container 2. An EJB container can enforce this locality constraint using ejb-links, but not always using direct JNDI names.

Can I pass bean references across beans?

You may, but the more heinous issue involves process threads. If two threads of control have a reference to the same session bean a head on collision with transaction contexts can occur. The spec sums it up in this statement : "If a session bean instance is participating in a transaction, it is an error for a client to invoke a method on the session object such that the transaction attribute in the deployment descriptor would cause the container to execute the method in a different transaction context or in an unspecified transaction context. The container throws the java.rmi.RemoteException to the client in such a case."

You could prevent two transactions occuring at the same time by carefully specifying the transaction contexts, but this seems like its destined to be a nightmare to maintain or debug.

Also one should never pass the EJB implementation's "this" pointer as a parameter. To obtain a valid reference to an EJB, you need to call:

        context.getEJBObject();

where context is either a SessionContext, or an EntityContext.

Article originally contributed by Borland Developer Support


Tags: AppServer