In each column, The EJB Advocate presents the gist of a typical back-and-forth dialogue exchange with actual customers and developers in the course of recommending a solution to an interesting design issue. Any identifying details have been obscured, and no "innovative" or proprietary architectures are presented. For more information, see Introducing the EJB Advocate.
Dear EJB Advocate,
I am writing you in the hopes that this dialogue will fuel change within the IBM® WebSphere® Application Server product concerning a problem that we encountered with the current implementation of EJB References.
Some background: Our application development team is scattered across the globe with over 1,000 developers in four countries. Our current architecture uses a session EJB with a remote interface as a facade to various services implemented in a POJO (plain old Java™ object). Each service can be thought of as executing a workflow of one or more tasks that are themselves implemented in a separate POJO for reuse (a given task may be reused in multiple workflows and services). Further, tasks can use other services (such as would happen in a complex workflow).
In order to fully leverage this reuse and the skills of our development organization, we generally do not want our programmers to know the session facade exists as an execution context for their POJOs. The problem is that each session facade needs to have an EJB-REF entry in the deployment descriptor for each session facade that it accesses. Practically speaking, this requirement means knowing all the services called by the tasks called by a given service implementation. And since we do not really want to break the encapsulation on the tasks called by a given service, this becomes more than just a maintenance headache, requiring us to duplicate lists of references over and over again.
Any help would be appreciated, especially if you know of a tool that can examine our code and automatically generate the EJB references for us.
Cross with EJB References
The EJB Advocate himself got a headache just by reading about having to know "all the services called by all the tasks called by a given service implementation". This recursion pointed out an inconsistency in the way services and tasks were implemented and used -- even though both were developed separately and possibly by different people. However, The EJB Advocate wanted to make sure of the details:
Digging deeper to reveal an architectural inconsistency
Your architecture seems very similar to a number of others I have encountered before where workflow-like behavior is handled by session beans and helper classes. A solution to your problem probably does not require code-understanding tools, but before proposing anything specific, I'd like to make sure that I have all the details right. Figure 1 is what I understand to be the high level architecture from your description.
Figure 1. High level architecture
A number of questions (more or less from left to right in the architecture above):
- Does the Client (which could be a task as shown by the diagram above) know it is using a remote session EJB, or is that detail hidden behind some sort of POJO itself (like an Access bean)? The reason I ask is that it seems like you are trying to completely hide the use of EJBs in the architecture, and I'd like to see how far you have gone in this regard.
- Is a Task POJO truly a subtype of Client as I show above? That is, I need to make sure that your answer to #1 above applies to the Task POJO implementation. If not, I need to know how a Task POJO will access "sub" services in its method implementations.
- What is the granularity of the Session facade? Is it a single method? Is it multiple methods? The reason I ask is to try to get a feel for how services get exposed to the Client, regardless of how they are implemented.
- What is the granularity of a Service POJO? Are its methods one-to-one with the Session facade? This answer will give me an idea as to the organizing principles of your services, and how much parallel development activity we can expect.
- How does the Service POJO locate a Task POJO? The new operator? A factory? A singleton? The answer to this question will be important with respect to migration of the existing code, if any is needed.
- What is the granularity of a Task POJO? Is it a single method? Is it multiple methods?
- Is the Task POJO stateful or stateless? The proposal I
make will depend on whether you are following a "command"
pattern, such as the following:
- create a task (or get it from a pool)
- set properties representing the inputs
- execute it
- get the properties representing the outputs
- remove it (or put it back in the pool).
Figure 2. Sequence diagram for a stateful command
Your EJB Advocate.
The following was the reply:
Dear EJB Advocate,
Thanks for the prompt reply. Here are the answers to your questions:
- Clients use the new operator to create a POJO that we call a "business delegate" (or BD) to hide the details of getting the Home and creating the Session. Like the Session, the BD is generated from the Service POJO. You are basically right in that we are trying to completely hide the use of EJBs from our programmers. Using EJBs in the first place has been a hard sell in our organization, but we tolerate the use of sessions to get container managed remotability, security, and transactionality.
- The implementations of the Task POJO methods also use a BD, just like any other Client, as you show.
- A Session interface has multiple methods that are related in some meaningful way.
- The methods on a Service POJO are one-to-one with the Session Bean facade.
- The methods on a Service POJO simply use the new() operator to create a Task POJO. The idea is for the Service and Task POJO methods to be as "pure" Java and as simple as possible. And by the way, under certain circumstances, a Service POJO method will new up a BD to another Service POJO directly. We like the way that WebSphere will "short circuit" the ORB call if the two Session facades are co-located.
- There can be multiple public methods on a Task POJO, but more often than not there is only one. There may be some private methods.
- The Task POJO methods are not stateful, as you show in the sequence diagram. But I would like to see the difference it makes in the architecture in case we were to use the command style programming model you showed.
Hope that these answers help you propose a solution.
Cross with EJB References
A modest proposal: Use session EJBs for the tasks, too
The answers were almost as The EJB Advocate expected, except for tasks not being stateful. Oh, well. In any event, the problem seemed to arise from not treating tasks like the full-fledged components they are (the adage "if it walks, quacks, and looks like a duck..." came to mind).
Here was the proposal:
Thanks for your answers, because they do give me enough detail to propose as solution. Here is the modified class diagram capturing the extra information you provided to use for comparison purposes from those that I will recommend below.
Figure 3. Corrected high level architecture
A rule of thumb I like to apply is that when code is developed and tested separately, it should be packaged as a component. Let's pretend for a minute that your team was not opposed to EJBs. In this case, I would recommend that both Services and Tasks be implemented as session beans. Why? Because a major benefit of session EJBs that your team seems to overlook is maintainability and reliability.
One distinction you seem to make between Services and Tasks is that Services are accessible remotely, and Tasks are accessible locally. Therefore, I would expose and use a Remote interface for Services, and a Local interface for Tasks, as shown in Figure 4.
Figure 4. Proposed high level architecture with remote
services and local tasks
If the Tasks had exposed a stateful programming model to the services (like I first assumed they did), they would be declared as stateful instead of stateless in the EJB deployment descriptor. The overhead of a local session EJB is relatively low, so performance should not be an issue, even if they are stateful. (As an aside, some might argue that stateful session beans should never be used. However, we never say never. We are happy to recommend them in this case, since they are deployed locally to a Service; therefore, the statefulness occurs within the context of a transaction started on the outermost session bean representing a Service. In short, if you are willing to accept local entity EJBs behind a session facade, you should be willing to accept a local stateful session EJB behind one as well.) In any event, there are benefits that make it worth the tradeoff for Tasks to be session EJBs, whether stateful or not.
Of course, the most important benefit is that this architecture solves your reuse and EJB-REF problems. Programmers that build Services can use Tasks (and Services, under certain circumstances as you described). Programmers that build Tasks can use Services. In either case, the deployment descriptor for each session bean (Task or Service) simply includes an EJB-REF for each session bean that it actually uses. Encapsulation is maintained, which is a key to maintainability.
Another benefit of making each Task an EJB is that you can unit test the Tasks independently of the Services that may call them (which includes testing the EJB-REFs that they use). Anything that makes a component easier to test is good for reliability.
Also, an often overlooked benefit of an explicitly EJB-based approach is that your business logic can take advantage of the EJB context to get access to information, such as the user making the call, and the transaction currently active. This ability simplifies the signatures of the Service and Task methods (while I did not ask, I suspect that user IDs and other information available in the session context is explicitly passed on the Service and Task POJO method signatures -- but we can save that for a separate discussion).
But even with all this said, I suspect that you still could not yet sell the explicit use of EJBs to the team. A compromise is to generate a "business delegate" and associated session EJB components from the Task POJOs as shown in Figure 5.
Figure 5. Generate business delegate and associated EJB
components for services and tasks
The Service POJO code will need to be changed to new up a Task Delegate rather than a Task POJO. But a good IDE will simplify the migration. And the change will be worth it, since this approach will help make the architecture much more service oriented and flexible with respect to the deployment options.
Good luck in your future endeavors.
Your EJB Advocate
Through this exchange, we saw applications of Session EJBs -- both remote and local, both stateless and stateful. Hopefully, we added maintainability and reliability to the list of benefits that architects will consider when choosing EJBs, beyond distribution, security, and transactions.
The rule of thumb to remember is that service oriented architectures require functional decomposition: decomposition results in components that ultimately need to be managed, and components in Java are best implemented with session EJBs (with or without a helper class, which makes the session EJB a facade). Trying to hide the use of EJBs is one thing (usually leading to some justifiable tradeoffs), but trying to avoid using components is another (usually leading to problems).
In the next column, we will examine an exchange that gets into the details of how to design EJB method signatures. We will expand on uses of the EJB context information, as well as the stateful programming model introduced here, and show how a stateful programming model can always be converted to a stateless one -- if you are willing to make the tradeoffs.
- Introducing the EJB Advocate
- Java 2 Platform, Enterprise Edition (J2EE) specification, the definitive source
- Enterprise Java Programming with IBM WebSphere, Second Edition, by Kyle Brown, Gary Craig, Greg Hester, Russell Stinehour, W. David Pitt, Mark Weitzel, JimAmsden, Peter M. Jakab, Daniel Berg. Foreword by Martin Fowler.
- Browse for books on these and other technical topics.