Chapter 9: Practical Applications


In this chapter we would like to consider some topics that are dealt with only superficially or not at all in the EJB specification. Often, it is precisely these points that are particularly useful in the development of practical systems. That is not to say that we consider the specification incomplete or flawed. The goal of this chapter is to give suggestions and examples for implementations on selected topics and to discuss these suggestions.

Performance

As already mentioned in Chapter 2, the programming language Java suffers from a reputation for poor performance. Particularly in the domain of business applications, which is without doubt the main focus of Enterprise JavaBeans, this is a critical point. Applications that run too slowly represent costs to an enterprise, since it means that employees are able to work less efficiently. System-dependent wait times are not conducive to motivation in the workplace. Employee work flow is impaired, and employees are thereby less able to focus on their tasks. This diminishes users' acceptance of slowly running applications.

This section has set itself the task of introducing to the reader once again the peculiarities of the architecture of Enterprise JavaBeans, in order to make the reader more conscious in a performance-related way of how they are used. To this end we would like to consider more carefully than we did in Chapter 3 the processes that take place inside the EJB container.

An abstract example of a simple session bean and a simple entity bean should clarify the nature of the objects involved as well as the processes and the interrelationships among all of these things. We shall assume that both beans support the remote client view. The following implementations make no attempt to be applicable in detail to every server or container implementation, but the principle, which is what we are interested in here, is the same for all implementations.

Example: Currency Exchange

The entity bean (we are going to assume container-managed persistence) is to represent the exchange rate of a particular currency with respect to the euro. Given the exchange rate, the session bean should be able to convert an amount in euros to another currency. Both beans, which the bean provider develops, declare both a home and remote interface (ExchangerateHome, ExchangerateRemote, ConversionHome, and ConversionRemote) and provide a bean class with the actual implementation (ExchangerateBean and ConversionBean). Both beans use declarative transactions. The entity bean (ExchangerateBean) has two attributes: the currency as a String variable and the exchange rate with respect to the euro as a Float variable. The currency serves as the primary key (represented by the class ExchangeratePK). The bean offers the method exchangerateAsFloat(), to permit access to the exchange rate as a Float value for calculations. The session bean (ConversionBean) has the method convert() in its remote interface. This method expects as parameter the amount in euros and the exchange rate in the form of a Float value and returns as result the amount in the target currency.

During the installation of the two beans in an EJB container, the deployment tools of the container provider will be used to generate additional classes. Implementation classes are generated for the home and remote interfaces, based on the instructions in the deployment descriptor (which we name ExchangerateHomeImpl, ExchangerateRemoteImpl, ConversionHomeImpl, and ConversionRemoteImpl, and which we met as the EJBHome and EJBObject classes in Chapter 3). Since the implementation classes of the home and remote interfaces must be addressable at run time as remote objects via RMI, the corresponding RMI classes must be generated (for each implementation class a stub class and a skeleton class). Moreover, for the abstract bean class of the entity bean an implementation class is generated that extends the code of the bean with the code of the persistence manager for access to the database. In order to install these two primitive beans in a server, we thus need sixteen classes and four interfaces. The four interfaces, the two bean classes, and the primary key class are created by the bean provider, while the remaining classes are generated using tools of the container and the persistence manager provider (where the actual number of generated classes will ultimately depend on the implementation of the container and that of the persistence manager).

The client code for using both beans might in simplified form look like that in Listing 9-1 (a fixed amount of 100 euros is to be converted to US dollars).

Listing 9-1: Client code for currency conversion.

start example
 ... InitialContext ctx = new InitialContext(); /////////// //Step 1 : The client ascertains the current Exchangerate. Object o = ctx.lookup("Exchangerate"); ExchangerateHome wh = (ExchangerateHome)        PortableRemoteObject.narrow(o, ExchangerateHome.class); //search for the Exchangerate ExchangeratePK pk = new ExchangeratePK("US-Dollar"); ExchangerateRemote wr = (ExchangerateRemote) wh.findByPrimaryKey(pk); //read the Exchangerate Float exchangerate = wr.exchangerateAsFloat(); //Step 2 : Convert the amount into the desired currency Object o1 = ctx.lookup("Conversion"); ConversionHome uh = (ConversionHome)        PortableRemoteObject.narrow(o1, ConversionHome.class); //generate the session bean ConversionRemote ur = (ConversionRemote)uh.create(); //convert 100 euros into the target currency float result = ur.convert(100, exchangerate); ... 
end example

In the following we will look at the mechanisms that these few lines of code of the client program set in motion at run time on the server (or in the container) and which objects are involved.

When the server is started, the implementations of the home interfaces are instantiated for the installed beans, and the associated stub objects are made available in the naming service.

Step 1: The Client Ascertains the Appropriate Exchange Rate

Table 9-1 provides an explanation of the steps in Figure 9-1. The client uses a JNDI lookup to obtain a stub object of the home interface implementation in its process space. To find the desired exchange rate, the client instantiates and initializes a PrimaryKey object and passes it as parameter in the call to the findByPrimaryKey method. The PrimaryKey object is serialized in the stub object and sent with the RMI protocol over the network to the associated skeleton object. The skeleton object deserializes the data, evaluates it, and carries out an appropriate method call on the object of the home interface implementation.

Table 9-1: Explanation of the steps in Figure 9-1.

Execution Step

Explanation

1

JNDI lookup on the home interface of the exchange rate bean (network access required).

2

Generation of a PrimaryKey object and call to the findByPrimaryKey method (network access required).

3

Call to the findByPrimaryKey method on the instance of the home interface implementation belonging to the skeleton.

4

Search the data via the persistence manager, generate the bean instance and the remote interface skeleton, or use pooled instance, initialization of instances.

5

Remote interface stub is returned to the client as result of findByPrimaryKey call.

6

Client calls the method exchangerateAsFloat() to ascertain the exchange rate (network access required).

7

Call to exchangerateAsFloat() on the remote interface skeleton.

8

The implementation of the remote interface skeleton opens a transaction. The persistence manager obtains persistent data and places the data on the bean instance.

9

Call to ejbLoad() and ejbActivate() on the bean instance.

10

Call to the method exchangerateAsFloat() on the bean instance and return of the result. In the implementation of the remote interface skeleton, after the end of the method call on the bean instance, the transaction is closed and the result is returned to the client.

click to expand
Figure 9-1: Example— ascertaining the exchange rate.

The home interface implementation checks with the help of the persistence manager whether a data record with the appropriate primary key is to be found. This requires access to the persistence layer. Then it is checked whether an available instance of the bean class exists in the pool. If not, a new instance is generated. Then a skeleton object of the remote interface is instantiated. The bean instance is supplied with an EntityContext object. As result of the operation, the RemoteInterface instance is returned to the skeleton object of the exchange rate home interface implementation. There the associated stub object is serialized and is delivered as return value to the client with the RMI protocol over the network.

To obtain the exchange rate in the form of a Float variable, the client calls a corresponding method, exchangerateAsFloat(), of the newly received remote interface stub of the exchange rate bean. This call goes over the network via the RMI protocol from the stub to the associated skeleton object on the server, which in turn transmits the call to the remote interface implementation. Before the call from the remote interface implementation can be further transmitted to a bean instance, it must be determined whether a bean instance with the correct bean identity has already been activated. If so, the EJB container simply uses this instance. If no bean instance with the proper identity is available in the state Ready, then an instance from the pool is used and initialized with the requisite bean identity.

Then the EJB container instructs the persistence manager to fetch the persistent data from the persistence layer and to initialize the attributes of the bean instance. Then the EJB container calls the method ejbActivate of the bean instance in order to inform it of the change into the state Ready. If there is no free instance available in the pool, then an active bean instance must be passivated, or the EJB container must generate a new instance. Only when a bean instance with the correct bean identity is present in the state Ready can the EJB container relay the method call to the instance.

In considering aspects of performance we shall assume that the transaction attributes and transaction context of the client are such that the EJB container has to start a new transaction and must terminate it after running the method. Normally, the EJB container always uses global transactions. This means that a transaction service always coordinates the transactions. The EJB container accesses the transaction service over the interface UserTransaction and thereby manages the transaction. Communication between the EJB container and the transaction service can take a variety of forms. A typical constellation is that the transaction service is provided by the application server and runs on the same computer in a separate process. In the case of a large system, however, issues of scaling come to the fore. If several EJB containers access the same data, then they require a central transaction service. In this case, the transaction service usually runs on its own server. Each access by an EJB container to the transaction service then goes over a network path.

First, the EJB container launches a global transaction with a call to the method begin in the interface UserTransaction. The thread now possesses a transaction context. Each system that takes part in the transaction must be informed about this context so that it can register itself with the transaction service. The context will be automatically transmitted only for calls within the thread. If the transaction context is to be transmitted in the case of calls across the network, then a special stub must manage this task.

If, for example, a database is accessed from within a transaction, then the JDBC driver (also a type of stub) transmits the transaction context to the database service. The database service registers itself with the transaction service using the transaction ID from the context. Then a local transaction associated with the transaction context is begun in the database. If, for example, a global transaction is later terminated with commit, the database service is notified of this by the transaction service. The two-phase commit that is used requires at least two calls (see Chapter 7, on transactions).

This process is identical for all global transactions, independent of whether the bean or the persistence manager accesses the database. If we assume that application server, database, and transaction service run on their own machines, a transaction results in at least four additional calls (begin, registration, and two commits) over a network path.

In our example the actual method call is transmitted to the bean instance after the begin. After the execution of the method, the EJB ends the transaction with commit or rollback, depending on the occurrence of any errors in the method. Then the result of the method is passed in the form of the return value to the skeleton. There, as with findByPrimaryKey, the result is serialized for network transmission and sent back to the client stub, which deserializes the data and makes it available to the calling object in the form of the return value.

Step 2: Converting the Amount into the Desired Currency

Table 9-2 clarifies the steps in Figure 9-2. This step is essentially a repetition of what was shown in detail in the first step. Instead of a findByPrimaryKey call, now we have a create call (since we are dealing with a session bean). Moreover, the persistence-relation operations are lacking. It could happen, depending on the server implementation, that the handling of entity and session beans is different with regard to pooling.

Table 9-2: Explanation of the steps in Figure 9-2.

Execution Step

Explanation

11

JNDI lookup on the home interface of the conversion bean.

12

Call to the create method (RMI).

13

Call to the create method on the home interface skeleton of the conversion bean.

14

Generation of the bean instance and remote interface skeleton or use of instances in the pool, initialization of instances.

15

Remote interface stub returned to the client as result of create call.

16

Client calls the method convert() to convert the amount into the target currency (RMI).

17

Call to convert() on the remote interface skeleton.

18

Possible opening of transaction or use of bean instance in the pool.

19

Call to the method convert() on the bean instance and return of the result to the client, possible termination of transaction.

click to expand
Figure 9-2: Example— conversion to the target currency.

The rather detailed description of the processes in the container classes in our example should have suggested that operations on Enterprise Beans are relatively expensive. With a component model like Enterprise JavaBeans a certain amount of overhead cannot be avoided.

Communication over RMI is, to be sure, simple and convenient for the developer, but this convenience results in a decrease in execution speed. The resulting container code for every bean type cannot—or at least only to a small extent—be divided among the various bean types, since the code is generated from the specific instructions of the deployment descriptor of an Enterprise JavaBean. At run time a skeleton object of the home interface is required for each bean type (this holds for entity and session beans). With entity beans a skeleton object of the remote interface is necessary for each bean in the state Ready. With session beans what is required is one object for each bean type and bound client.

The extent of the container overhead (that is, the scope of the container objects present at run time) is directly proportional to the number of installed and employed beans (with session beans the number of clients bound to the server plays a role as well). The size of the bean itself has relatively little to do with the generated container code. A bean with a small home or remote interface does not have significantly less container code and fewer run-time objects than a bean with a large home or remote interface.

Conclusions

An important consideration in the use of Enterprise JavaBeans with respect to good performance is the design of the network interface. By this we mean the bean's home and, most of all, remote interfaces. The goal is to offer as few methods as possible containing as much functionality as possible and allowing the exchange of the greatest possible quantities of data. As the implementations presented show, calls to bean methods are expensive operations. Thus for the sake of optimization it is a good idea to consider processing speed as early as the design phase. Fine-grained operations (such as ascertaining the exchange rate in our example) are marked by a certain disparity between the scope of the functionality and the container overhead. They have a negative effect on the performance of the application system.

No less important a role in the performance of EJB applications is played by the design of the data model. Entity beans are heavyweight conglomerates of run-time objects. Fine-grained entities, that is, data records with few data fields that appear in large quantity, are to be avoided if possible. A negative example is the entity Exchangerate as used in this section. As with the network interface, it is a good idea to define coarse-grained entities. The goal is to keep the number of entity-bean and container-class instances as small as possible. The definition of coarse-grained entities and the modeling of methods that offer a great deal of functionality and permit the transfer of large quantities of data also lowers the number of necessary network accesses.

The implementations that we have shown relativize themselves when an Enterprise Bean is addressed via the local client view. Since the client and the Enterprise Bean are deliberately located in the same process, there is no network communication overhead. With the lack of serialization of parameters and return values and the lack of network latency, the local client view has a significant performance advantage over the remote client view. Whether the interface of the Enterprise Bean is fine-grained or coarse-grained plays a secondary role in this case. What remains with the local client view are container overhead, overhead of global transactions, and in the case of entity beans, the transport of data to and from the database.

The local client view gives the design of EJB applications a new dimension. Through the optimal combination of Enterprise Beans with remote client view (coarse-grained interfaces for remote clients) and local client view (local interfaces that are primarily addressed by other Enterprise Beans) one can achieve a significant influence at the design stage on the performance of the future system.

The performance behavior is generally a function of the type of bean (independent of whether the remote or local client view comes into play). With session beans, from the point of view of performance one would favor stateless over stateful session beans. The EJB container can manage stateless session beans much more efficiently through pooling than it can stateful session beans. With the latter type, object serialization through the activation and passivation mechanisms has an additional negative effect on performance. In the case of entity beans the form of persistence mechanism chosen plays a role. With the use of container-managed persistence one is dependent on the implementation of the persistence manager, which affects the performance of data storage routines. The persistence manager provider must make available generic routines, which are always less efficient (in the case of bean-managed persistence) than algorithms designed especially for a particular data model. Thus in particular cases it can be necessary to do without the convenience of container-managed persistence for the sake of speed of execution.

Message-driven beans hold a special position to the extent that they have no public interface. They cannot be addressed directly by the client. Moreover, message-driven beans are not persistent. Thus for this bean type the overhead of the EJB container is significantly less than with entity and session beans. Message-driven beans can be managed by the EJB container with a similar efficiency as for stateless session beans.

On account of the possibility of parallel processing, message-driven beans have a tendency to increase the performance of a system. For parallel processing to be possible, several threads must be available. The EJB container takes over management of the threads and the assigning to instances of message-driven beans. The greater the number of different types of message-driven beans that are deployed, the greater the number of threads that are needed by the EJB container to guarantee the parallel processing of the various types of message-driven beans.

Threads are a valuable resource of the operating system, but they bring with them considerable management overhead. The use of message-driven beans to improve performance should therefore not be excessive, since the effect of increased performance by the increased effort of the operating system for the management of threads can be impaired. Moreover, not every type of message-driven bean can make use of its own pool of threads. Within an application server (the EJB container is, together with other containers, a component of such a server) there is generally a central thread pool available, which is divided among the various services and containers. The greater the number of different types of message-driven beans used, the more easily the resource Thread can become in short supply, leading to a bottleneck.

To parallelize a process through the use of several threads does not automatically mean that better performance can be attained. Whether the processing runs more quickly or more slowly depends on a number of factors. One of these factors is the hardware on which the system is run. A computer with one CPU can always have only one thread running while the other threads wait until they receive a share of CPU time. To parallelize a CPU-intensive process over several threads on a computer with a single CPU can result in worse performance overall. In addition to the time that the CPU needs for processing the instructions there is also the time necessary for managing the CPU time for several threads and the switching between threads. However, if the processing is of the form, say, where an e-mail message must be fetched, data read from a database, and a file fetched from an FTP server, then the division into several threads can be profitable even on a computer with a single CPU. While one thread is waiting for the answer to the e-mail (thus is using no CPU time), the other thread can already begin to take data from the database.

Transactions are also a burden on the performance of an application system. If one cannot do without transactions, then it is advisable to avoid the use of global transactions. Since global transactions use a great deal of communication over the network, local transactions show much better performance. Global transactions can be avoided by not including several entity beans in disparate locations in a single transaction. A global transaction is also necessary, for example, when an entity bean accesses several databases.

Finally, we note that the examples in this book do not always follow the principles stated in this section. The goal of the examples in the other sections and chapters is to foster an understanding of EJB-related matters, not to achieve optimal performance.




Enterprise JavaBeans 2.1
Enterprise JavaBeans 2.1
ISBN: 1590590880
EAN: 2147483647
Year: 2006
Pages: 103

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net