Technical Publications-Whitepapers-A Detailed Comparison of Enterprise JavaBeans (EJB) & The Microsoft Transaction Server (MTS) Models (with specific code examples)
14 May 1999Publications, Container-Managed-Models
Before you read this article, please be fore-warned that this was written with the developer in mind. I assume that you are familiar with MTS and EJB and have a working knowledge of at least one of these technologies.
This Comparison is based on the following Criteria |
|
A Note on the Terminology
Let us now look at some of the terminology that will be used in this article.
Resource Dispenser (RD) | The ODBC Resource Dispenser manages a pool of database connections. The Resource Dispenser can also reclaim connections for use by other clients. When a client gets a connection from the Dispenser, it is automatically enlisted in the object's transaction. Resource Dispensers are about making efficient use of a limited pool of resources. |
Resource Manager (RM) | Resource Managers are the services that provide the resources to be used in transactions. Resource Managers are about managing the effect of transactions on resources by making sure that the effects of a transaction are either committed or rolled back accordingly. |
Microsoft Distributed Transaction Coordinator (MS-DTC) | A transaction is an atomic unit of work: either all the actions in a transaction are committed or none of them are. Work can be committed as an atomic transaction even if it spans multiple resource managers, potentially on separate computers. A Transaction Coordinator is a transaction manager that coordinates transactions which span multiple resource managers. In the MTS environment, a transaction is coordinated by the Distributed Transaction Coordinator (DTC). |
Shared Property Manager (SPM) | Shared Properties are properties global to the middleware MTS component. Since these properties may be shared with multiple clients, there is a distinct problem of Synchronization that has to be handled. The Shared Property Manager synchronizes access to shared properties. |
Microsoft Message Queue (MSMQ) | The Microsoft Message Queue Server (MSMQ) guarantees a simple, reliable and scalable means of asynchronous communication freeing up client apps to do other tasks without waiting for a response from the other end. It provides loosely-coupled and reliable network communications services based on a messaging queuing model. MSMQ makes it easy to integrate applications, implement a push-style business event delivery environment between applications, and build reliable applications that work over unreliable but cost-effective networks. |
COM Transaction Integrator (COMTI) | COM Transaction Integrator (COMTI) enables Microsoft Transaction Server to execute applications running under CICS or IMS on an IBM MVS system. COMTI ships with SNA Server version 4.0 |
Java Naming and Directory Interface (JNDI) | An API for naming-service-independent resource location. This provides Java applications with a unified interface to multiple naming and directory services on the enterprise. JNDI enables seamless connectivity to heterogeneous enterprise naming and directory services. Developers can now build powerful and portable directory-enabled applications using this industry standard. |
Java Transaction Service (JTS) | This is an API to ensure data integrity across several systems and databases using two-phased commits and rollbacks. The API is compatible with the OMG's Object Transaction Service (OTS). The Java Transaction API (JTA) specifies standard Java interfaces between a transaction manager and the parties involved in a distributed transaction system: the resource manager, the application server, and the transactional applications. |
Java Message Service (JMS) | JMS provides a reliable, flexible service for the asynchronous exchange of critical business data and events throughout an enterprise. The JMS API adds to this a common API and provider framework that enables the development of portable, message based applications in the Java programming language.The JMS API improves programmer productivity by defining a common set of messaging concepts and programming strategies that will be supported by all JMS technology-compliant messaging systems. |
Introduction
A while back, I had written a Detailed Comparison on the CORBA, DCOM and Java/RMI models. In that article, I had promised that as soon as OMG announced their middleware CORBA Component Model (CCM), I would write another article comparing MTS, EJB and CCM. In a press release on April 6, 1999, the OMG officially announced that the JavaSoft's Enterprise JavaBeans (EJB) model would also serve as a subset to CORBA's Component Oriented Middleware platform.
Middleware Component Models take a high level approach to building distributed systems. They free the application developer to concentrate on programming only the business logic, while removing the need to write all the "plumbing" code that is required in any enterprise application development scenario. For example, the enterprise developer no longer needs to write code that handles transactional behavior, security, database connection pooling or threading, because the architecture delegates this task to the server vendor.
When it comes to Middleware Component Models there are several 'de-jure' and 'de-facto' Standards in the industry today. In this article, we will look at two of them namely, The Microsoft Transaction Server (MTS) Architecture from Microsoft and JavaSoft's Enterprise JavaBeans (EJB). Let us examine the differences between these models from a programmer's standpoint and an architectural standpoint. At the end of this article, you will be able to better appreciate the merits and innards of each of these Middleware Component Architectures.
MTS, based on the Component Object Model (COM) which is the middleware component model for Windows NT, is used for creating scalable, transactional, multi-user and secure enterprise-level server side components. MTS provides a surrogate for in-process server-side components. MTS can also be defined as a component-based programming model. An MTS component is a type of COM component that executes in the MTS run-time environment. MTS supports building enterprise applications using ready-made MTS components and allows you to "plug and work" with off-the shelf MTS components developed by component developers. Just as a COM component can be modelled on the basis of interfaces and their implementation, MTS enforces modelling based on the component's state and behavior. MTS, through the COM infrastructure, handles communication between components using DCOM. This allows MTS to expose its components to Windows applications from anywhere on the net or the web. As long as you are only running on Windows NT, MTS components can be deployed on top of existing transaction processing systems including traditional transaction processing monitors, web servers, database servers, application servers, etc. Legacy system integration with non-Windows systems and MTS is achieved using the COM Transaction Integrator (COM-TI) technology. Also, it is important to realize that MTS is a stateless component model, whose components are always packaged as an in-proc DLL. Since they are composed of COM objects, MTS components can be implemented in a variety of different languages including C++, Java, Object Pascal (Delphi), Visual Basic and even COBOL!
EJB is a middleware component model for Java and CORBA and is a specification for creating server-side, scalable, transactional, multi-user and secure enterprise-level applications. It defines a set of specifications and a consistent component architecture framework for creating distributed n-tier middleware. It would be fair to call a bean written to EJB spec a Server Enterprise Bean. Most importantly, EJBs can be deployed on top of existing transaction processing systems including traditional transaction processing monitors, web servers, database servers, application servers, etc. Since these components are written using Java, EJBs containing the business logic are platform-independent and can be moved to a different, more scalable platform should the need arise. If you are hosting a mission-critical application and need to move your EJBs from one platform to the other, you can do it without any change in the business-logic code. This allows you to "plug and work" with off-the-shelf EJBs without having to develop them or have any knowledge of their inner workings. EJB provides both a stateless and a stateful model and are packaged as jar files. Since EJB is built on top of Java technology, EJB components can only be implemented using the Java Language.
Caveat Emptor
The term "Server" has different connotations in different contexts. There are Web Servers, Application Servers, EJB Servers, Transaction Servers, etc. or, in some casses, even a piece of hardware can be construed as a Server. Unless explicitly mentioned otherwise, when I talk about a Server, I am refering to either an EJB Server, or the MTS runtime host process (mtx.exe) that acts as a Server.
A "Resource" can be a anything from a Database, to a File, to a Socket, to an OS Semaphore. So when I refer to a Resource in the write-up, they can mean any of the above, not necessarily only Databases.
MTS the acronym - coined more than three years ago - means a lot of different things in different contexts, just as EJB the acronym can at times refer to EJB the packaged component, EJB the enterprise bean class, and EJB the architecture - it all depends on the context. Similarly, MTS the architecture is different from an MTS component or the MTS runtime host process (which is what these MTS components are deployed on).
Given that MTS is also an implementation, comparisons must be made with the EJB spec and real EJB implementations. However, since most of these details are hard to come by, and may vary from vendor to vendor, I have tried to point out as much of implementation related stuff in EJBs as I possibly could.
An EJB is entirely different from a plain 'vanilla' JavaBean. A JavaBean is more of a client-side component (more akin to ActiveX controls) which can be visually manipulated in a builder tool. However, an EJB resides entirely on the Server-side (just as an MTS or a DCOM component would).
Application Sample - The Checking Account Server and Client
The Checking Account Server component has methods for managing a typical Bank Checking Account. Consequently, it has methods to create an account, and credit & debit money from the Account.
I have selected Java as the implementation language for these examples here for three reasons :
- EJBs can only be implemented using Java.
- Since I am comparing EJB with MTS, I should also implement the MTS components in Java for ease of comparison.
- The Java Language keeps the implementation simple, easy to understand, and is very elegant.
The Background
Middleware components are always deployed on the server side. Each Server will probably service tens to many thousands of client requests at any point in time. The server should be able to scale well and service these calls efficiently from multiple clients. Hence the technology used to build these server components has to provide the features and a framework that will help the developer build scalable middleware server components.
Most client applications spend a long portion of their time waiting for a user to key in some data. If the middleware component on the server were to wait for this data from the user to come in, it would continue to tie up valuble server resources like network connections, processes, threads, memory, and database connections. This means at some point in time sooner than later, the server may not be able to service any more clients since all its resources may be tied down by middleware components waiting for input from their clients.
Both EJB and MTS solve this problem by requiring that the actual middleware components run within a controlled runtime environment. While EJB Components run inside an EJB Container, MTS components run in an MTS Executive. Instance Management Algorithms are used to make sure that these middleware components are not idle and are working to their maximum potential.
Lifecycle and Instance Management
The MTS runtime (mtxex.dll) manages creation, management, and destruction of MTS components. An EJB container creates, manages, and destroys EJB components.
MTS Client - The MTS client code shown below calls into the MTS server object's methods by first acquiring a pointer to the server object. Though it looks simple once implemented, there are a lot of things that happen internal to the Microsoft Java VM. The new keyword here instantiates the bank.Checking MTS Server object. This leads the Microsoft JVM to use the CLSID (obtained from the COM specific class file attributes) to internally make a CoCreateInstance() call. The IUnknown pointer returned by CoCreateInstance() is then cast to bank.IChecking, as shown below. All these are handled internally and automagically by the Microsoft VM and is transparent to the user.
Realize that the object created in the MTS case is a regular Java object, that doesn't require any transaction specific interfaces. In fact, to anyone using the object they don't necessarily even know that it uses transactions.
EJB Client - The EJB client shown below calls into the EJB server object's methods by first acquiring a pointer to the server object. The client first uses the Java Naming and Directory Interface (JNDI) to get a reference to the EJB component's home interface. It then uses this reference to create an instance of the EJB component and holds on to this reference when calling into the component methods. The home interface also allows you to get an object reference that already exists.
MTS - Component Creation & usage | EJB - Component Creation & usage |
try { // create the MTS component bank.IChecking server= (bank.IChecking) new bank.Checking (); // invoke business methods on the component server.createAccount (1234, "Athul", 1000671.54d); } catch (Exception ex) { ex.printStackTrace (); } |
try
{ // get the JNDI naming context Context initialCtx = new InitialContext (); // use the context to lookup the home interface CheckingHome home = (CheckingHome) initialCtx.lookup ("checking"); // use the home interface to create the enterprise Bean Checking server = home.create (); // invoke business methods on the bean server.createAccount (1234, "Athul", 1000671.54d); } catch (Exception ex) { ex.printStackTrace (); } |
Client code to create MTS component | Client code to create EJB component |
In essence, both MTS and EJB work by intercepting method calls and inserting services based on a set of attributes defined at deployment time. MTS uses class factory wrappers and object wrappers to intercept the method calls (the MTS executive is called automatically by the COM runtime). Similarly, EJB requires wrappers for each component type (EJBHome) and each component instance (EJBObject).
Instance Management is about giving an impression to the client that a dedicated middleware component is waiting to service its request. The client has no idea whether such a component instance really exists or not. When a client makes a call to the middle tier component, this call is intercepted by the container. The container now kicks in its Instance Management Algorithm. The Figure 1 below shows the generic architecture of both the EJB and MTS models.
Figure 1: The Generic Architecture
While what happens till now is the same both in the EJB and MTS architectures, the next steps are different. MTS uses an Instance Management Algorithm called Just In Time Activation (JITA). EJB uses an Instance Management Algorithm called Instance Pooling (IP).
MTS - Just In Time Activation (JITA) | EJB - Instance Pooling (IP) |
In MTS, the actual middle-tier MTS component is not created until the call from a client reaches the container. Since the component is not running all the time, it does not use up a lot of system resources (even though an object wrapper and skeleton for the component are still hanging around for the component). As soon as the call comes in from the client, the MTS wrapper process activates its Instance Management algorithm called JITA. The actual MTS component is created "Just In Time" to service the request from the wrapper. And when the request is serviced and the reply is sent back to the client, and the component either calls SetComplete()/SetAbort(), or the transaction that it's part of ends, or the client calls Release() on the component, the actual MTS component is destroyed. In short, MTS is a stateless component model. Generally, this is what happens on the Server when a client requests services from a typical MTS component:
It is thus possible to implement high latency resources as asynchronous resource pools, which should take advantage of the stateless JIT activation afforded by the middleware server. |
In EJB, the
actual middle-tier EJB component instance may be pooled.
Since the component may not be running all the time, it
does not use up a lot of system resources. As soon as the call comes in from the client, the EJB container may use an EJB component instance from a pool of shared instances. And as soon as the request is serviced and the reply is sent back to the client, the actual EJB component is returned back to the pool. It may not be destroyed. Generally, this is what happens on the Server when a client requests services from a typical stateless EJB component:
EJB may or may not be not be stateless between calls from the client, while MTS is. For stateful beans the above scenario is true if and only if a call to the component coincides one to one with transaction boundaries. You can easily have the following scenario:
|
Location Transparency
Both MTS and EJB application components are location transparent. A client, at best, may only need to know the DNS name of a Server machine from which to get a reference to the component. It is unnecessary for the client of a component to know the physical location of the component in the file system or on the network. It doesn't matter where a component is - on the local machine or a remote machine, inprocess or out of process and this is a feature of the underlying transport mechanism. If this knowledge is unavailable to the client, it forces a consistent approach for interaction with components. The same exact components can be deployed so that all users can share them. This means that only one set of code needs to be supported for multiple configurations. This lowers the cost associated with maintaining code and it increases deployment choices.
Database Connection Pooling
Generally, a lot of time is wasted establishing a database connection. This means, acquiring a database connection and initializing it - verifying user against system using authentication schemes, logging on to database, etc...- is an expensive operation. So it is always a good idea to create a pool of database connections that can be shared amongst multiple clients.
MTS-Connection Pooling | EJB-Connection Pooling |
Database connection pooling is built into the ODBC Resource Dispenser. Any application using ODBC and running on Windows can make use of this facility to reuse database connections. MTS uses ODBC for database access and hence, reuses database connections by making use of this facility built in Windows. Without this database connection pooling, the Just In time Activation (JITA) algorithm used by MTS for Instance Management will not work efficiently. Definitely, the initial creation of the database connection will be slow, but this happens only once in a while. |
EJB uses JDBC for database access.
Support for Database connection pooling is not available
in JDBC until version 2.0. EJB vendors may either need to
get JDBC 2.0 compliant or else will be forced to
implement their own proprietary database connection
pooling algorithms in their products. Although the EJB specification
does not formally prescribe it, almost all EJB vendors
provide JDBC connection pooling, and this will become
standardized at JDBC 2.0. Further, the access to these |
The Architecture
The MTS model
A basic MTS architecture is shown in the Figure 2 below and is made up of:
Figure 2: The MTS Model
The EJB model
A basic EJB architecture is shown in the Figure 3 below and consists of:
- An EJB server
- EJB containers that run within the server
- Home objects, Remote EJBObjects and Enterprise Beans that run within containers
- EJB clients
- Auxiliary systems like the Java Naming and Directory Interface (JNDI), Java Transaction Service (JTS), Security services, etc.
Figure 3: The EJB Model
MTS - Architecture | EJB - Architecture |
COM components that run under the
control of the MTS Executive are called MTS components.
MTS components are all developed as in-proc DLLs and are
implemented as one or more COM components. These
components are deployed and run in the MTS Executive
which manages them. As is usual with COM components, the
object implementing the IClassFactory
serves as a Factory Object to create new instances of
these components. MTS inserts a Factory Wrapper Object and a Object Wrapper between the actual MTS component that MTS manages, and it's Client. Therefore, whenever the client makes a call to the MTS component, the Wrappers (Factory and Object) intercept the call and inject their own instance management algorithm called the Just In Time Activation (JITA) into the call. The wrapper then makes this call on the actual MTS component. In addition to this, based on the information from the component's deployment properties, transaction logic and security checks are also done in these wrapper objects. For every MTS component, there also exists a Context Object which implements the IObjectContext interface. The Context Object maintains specific information about that component such as it's transactional information, security information and deployment information. The MTS component calls into the Context Object through it's IObjectContext interface. |
Java components that run under
the control of the EJB container are called EJB
components. EJB components are all developed according to
EJB specs and are implemented as one or more EJB
components. These components are deployed in the EJB
Server and run in a Container which manages them. The
object implementing the javax.ejb.EJBHome interface serves as a Factory
Object to create new instances of these components (and
to find existing components in the case of entity beans).
EJB inserts a Factory Wrapper Object called the HomeObject and a Remote Wrapper Object called the EJBObject between the actual EJB component that the EJB container manages, and it's Client. Therefore, whenever the client makes a call to the EJB component, the Wrappers (Home and EJBObject) intercept the call and inject their own instance management algorithm called Instance Pooling(IP) into the call. The wrapper then makes this call on the actual EJB component. In addition to this, based on the information from the EJB component's Deployment Descriptor, transaction logic and security checks are also done by these wrapper objects. For every EJB component, there also exists a Context Object which implements either the javax.ejb.SessionContext or the javax.ejb.EntityContext interface. The context object is the component's handle on the container, through which the component can get transaction information, security information and information from the component's deployment descriptor. The EJB component calls into the Context Object through the SessionContext or EntityContext interface. |
Distributed Transaction Support
Transactional Model in MTS
In the MTS model, each MTS component declaratively declares its transactional primitives. The MTS runtime environment automatically initiates transactions. For every component, MTS creates a corresponding Context Object that contains information like the transaction identity, the current activity identifier, the security ID of the caller, etc. The Context Object is loaded as part of the MTS Executive (mtxex.dll). In addition to holding the Context Object, the MTS Executive takes part in transaction processing on behalf of the component. The components and the MTS Executive typically execute in a separate host process provided by MTS (mtx.exe). MTS uses OLE Transaction technology to carry out two-phase commit transactions. This model defines three entities in transaction processing:
- Transaction Client,
- Transaction Manager - the Microsoft Distributed Transaction Coordinator (MS DTC for short), and
- Resource Manager - anything that manages persistent, durable resources such as a databases or a files and allows transactional access to the underlying data (e.g., SQL Server).
The Figure 4 below illustrates the transaction model in MTS.
Figure 4: Transactional model in MTS
MTS does a good job of isolating MTS components from the details of how transactions are carried out. The MTS Executive, the Dispenser Manager, and the MS DTC do most of the work required to perform two-phase commit, distributed transactions behind the scenes. To achive this, MTS takes on all the major responsibilities of a Transaction Client and perform such steps as:
- Creating transactions and maintaining the transaction context,
- Instructing MS DTC to commit or abort the current transaction,
- Propagating the transaction context to all participating objects and enlisting the appropriate resources.
Transactional Model in EJB
EJB uses the Java Transaction Service (JTS) and the Java Transaction API (JTA) for transactional support. JTA provides the exceptions and interfaces that the components and clients see. However, it can be expected that many Servers will use JTS for the underlying server provider. However, JTS is actually not a transaction service, but an interface to an underlying transaction service provider. JTS just has one interface and several exceptions. It is clear by looking at its list of exceptions that it was modeled to work with OTS, even though it could be used as an interface to other transaction services as well. The transactional model used in EJB is very similar to OTS. Therefore, understanding how OTS works helps us to understand how transactions work in EJB.
The components that are involved in OTS transactions are:
- Control
- Resource
- Synchronization
- Terminator
- Coordinator
These OTS components can be mapped almost directly to EJB transaction components. The Figure 5 below, shows all the objects participating in a transaction. Commits and Rollbacks are applied to all Resource objects in the group.
Figure 5: Transactional model in OTS and EJB
The Control object represents the transaction. From this object, the Terminator and the Coordinator can be obtained. The Control object is used by the EJB Container to manage transactions on behalf of the bean and is transparent to the EJB developer.
The Resource object has transactional state (since it may represent a connection to a database). Calling commit() on this object will will force the updates to be applied to the database. Similarly, calling rollback() on this object will revert all changes to the database since the start of the transaction, to be reverted. Once the commit() or rollback() id done, the corresponding rows in the database will be unlocked. It participates in the two-phase commit protocol and has a vote as to whether the transaction has to be committed or rolled back.
The Synchronization object needs to be notified upon completion of the transaction whether the transaction was committed or rolled back. It is a passive participant in the two-phase protocol and has no vote on the commit or rollback of transactions.
The Terminator object may be used by the EJB container to commit or rollback the transaction when a thread returns from a bean method. When a commit or rollback is requested, all objects in the transaction will committed or rolled back as appropriate.
The Transaction Coordinator object is what co-ordinates all this and makes it all work seamlessly. Both the Resource and Synchronization objects are registered into the transaction using this object. The bean may not get access to this object directly. Transaction-aware objects that are intended for use with the EJB will automatically obtain a reference to the current Transaction Coordinator to register themselves.
In General...,
Distributed transaction management involves two possible layers of distribution: multiple application participants and multiple data resource managers. Each layer must be managed separately. A Transaction Coordinator is responsible for ensuring support for distributed transactions. The director of a transaction is called the transaction manager or Transaction Coordinator. The participants in the transaction that implement transaction-protected resources such as relational database drivers are called resource managers. When an application begins a transaction, it creates a transaction object that represents the transaction. The application then invokes the resource managers to perform the work of the transaction. As the transaction progresses, the transaction manager keeps track of each of the resource managers enlisted in the transaction.
The application's first call to each resource manager identifies the current transaction. Thereafter, all calls made over that connection are performed on behalf of the database transaction until it ends. If the application fails, the Transaction Manager aborts the transaction. When the application successfully completes the transaction's work, it calls the Transaction Manager to commit the transaction. The Transaction Manager then goes through a two-phase commit protocol to get all of the enlisted resource managers to commit.
In a distributed object environment, a single transaction may involve a number of different objects. One object starts the transaction, then it calls some number of methods in other objects to execute the work, then when everything is complete, the object commits the transaction. The transaction context, keeps track of all the object participants in the transaction. When the transaction commits, the commit request is forwarded to a transaction coordinator (TC) to manage the data resource managers. Transaction Coordination can also be delegated to a third party - either the transaction coordinator in a database or a separate distributed transaction coordinator (such as Microsoft's DTC or an XA-compliant transaction coordinator like Encina or Tuxedo). While JTS/OTS focuses on managing the multiple application participants, XA and DTC focus on managing the multiple data resource managers.
MTS - Distributed Transaction Support | EJB - Distributed Transaction Support |
In Windows NT, a separate service called The Microsoft Distributed Transaction Coordinator (MSDTC) performs the role of a Transaction Coordinator and helps ensure that distributed transactional operations happen successfully. The MTS Executive communicates with the MSDTC to start transactions, join existing transactions, commit transactions, and roll-back transactions. | Most EJB implementations (e.g., WebLogic, Bluestone, Novera, Persistence, Oracle AS, Oracle8i) use the database delegation approach to transactions, so they don't support heterogeneous transactions. As of now, GemStone, Inprise, Secant, OrchidSoft and IBM WebSphere are the only EJB servers that provide an integrated distributed Transaction Coordination service. |
The Distributed Transaction Service
The piece in the MTS architecture that is similar to JTS (or OTS) is the MSDTC. The MSDTC provides distributed transaction support in MTS just as OTS does in the CORBA architecture.
In MTS - MSDTC | In EJB - JTA and JTS |
The Microsoft Distributed Transaction Coordinator (MSDTC) performs the role of a Transaction Coordinator and helps ensure that distributed transactional operations happen successfully. The MTS Executive communicates with the MSDTC using OLE Transactions which has a COM based interface. | EJB provides a transactional
framework for EJB components. Transactional resources (e.g.
JDBC connections) are automatically registered with the
EJB server's transaction coordinator as necessary by the
EJB container. EJB components can interact with the EJB
transaction frame work via JTA (Java Transaction API).
JTA allows components to begin, commit and vote for
rollback of EJB transactions. JTA is just an interface. It's completely devoid of any implementation. It defines an interface to a transaction service, which may or may not actually be JTS. |
Support for Transactions
In MTS, when a client references an MTS component, a Context Object is created. This Context Object provides information to MTS, the client, the MSDTC and other data source drivers involved. The information includes the "Transaction Settings" for the component, which depends on the properties set either when it was deployed on the Server or set programmatically. In addition to calling SetComplete() and SetAbort() methods, a component can always exert control over how transactions work. For example, it can call the DisableCommit() method to prevent the MSDTC from allowing a SetComplete() or SetAbort() method to commit open data store transactions on all resource managers until further notice. When it is ready to commit, it can always call the EnableCommit() method. MTS automatically provides transaction support to applications running on the server, providing a reliable failure isolation and recovery mechanism. Transaction support is transparent to the application programmer. This is a considerable improvement compared to previous generations of transaction processing systems, where developers had to explicitly program low-level transaction control primitives into their applications.
In essence, in MTS, the developer implements the business methods, but doesn't have to implement any callback methods. The developer must code transaction commit and rollback demarcation statements. If everything goes right, the developer must call SetComplete() on the transaction context object. If anything goes wrong, the developer must call SetAbort() on the transaction context object. MTS supports four automatic transaction semantics which may be assigned declaratively. MTS does not support manual transactions.
Similarly, in EJB, the transaction semantics for an enterprise bean are defined declaratively rather than programmatically. At runtime, the EJB container automatically implements transaction services according to the TransactionAttribute specified in the deployment descriptor. In EJB, the developer must define the remote interface, and the developer must implement the business methods and the EJB callback methods (although in many cases they can be left empty). The EJB container deployment tools automatically generate the EJB wrapper objects. Optionally, an EJB developer may deploy a component with "Bean Managed Transaction" specified. In this case the component uses the JTS to manually control its transaction behavior.
MTS - Support for Transactions | EJB - Support for Transactions | |
Client-managed transactions | Client can determine explicitly when a transaction should begin and end. This feature is built on top of Automatic Transactions. | TX_MANDATORY - Caller must start transaction. The bean is always invoked in the scope of the client’s transaction. If the client does not have one, a javax.transaction.TransactionRequiredException exception is raised. |
Container-managed transactions (in
EJB) - or - Automatic Transactions (in MTS) |
Not Supported- Caller’s transaction will be suspended before calling the MTS component. The component is invoked without transactional scope | TX_NOT_SUPPORTED - Caller’s transaction will be suspended before calling the EJBean. The Bean is invoked without transactional scope |
Requires New- MTS component requires a new transaction be created for each method call | TX_REQUIRES_NEW - EJBean requires a new transaction be created for each method call | |
Required - the MTS component requires a transaction. If the client is associated with a transaction context, the component is invoked in the same context otherwise, the container starts a new transaction before invoking methods on the component and commits the transaction before returning from them. | TX_REQUIRED - EJBean requires a transaction. If the client is associated with a transaction context, the bean is invoked in the same context otherwise, the container starts a new transaction before invoking methods on the EJBean and commits the transaction before returning from them. | |
Supported - The caller’s transaction is simply passed on by the MTS wrapper. If the caller does not have a transaction context, the MTS component methods are invoked without a transaction context. | TX_SUPPORTS - The caller’s transaction is simply passed on by the EJB Container. If the caller does not have a transaction context, the bean methods are invoked without a transaction context. | |
Component-managed transactions | The MTS component starts and ends a transaction. It makes direct calls to MSDTC using OLE transactions | TX_BEAN_MANAGED - the enterprise bean starts and ends a transaction. It may use the javax.transaction.UserTransaction interface to demarcate transaction boundaries |
Component Types
MTS components have the following characterestics:
- MTS components typically execute on behalf of a single client.
- MTS components may be transaction-aware.
- They can update data in an underlying database.
- They are relatively short-lived, since their lifetime is limited to that of a client call.
- They may be destroyed as soon as they are done processing a client request and the component either calls SetComplete()/SetAbort(), or the transaction that it's part of ends, or the client calls Release() on the component.
- MTS components do not represent data that has to be stored in a database.
EJB components are of two types - Session Beans and Entity Beans:
The characteristics of a Session Bean can be summarized as follows:
- Session EJBs execute on behalf of a single client. A Session Bean instance is an extension of the client that creates it.
- Session beans may be transaction-aware.
- They can update data in an underlying database.
- They are relatively short-lived, since their lifetime is limited to that of their client.
- They may be destroyed when the EJB server crashes. The client has to establish connection with a new Session Bean object to resume any computation.
- Session beans do not represent data that has to be stored in a database.
Every Entity Bean has the following characteristics:
- Entity beans can share access from multiple users.
- They can participate in transactions.
- Entity beans represent data in a domain model.
- They are persistent. They live as long as the data lives in the domain model.
- Entity beans can survive EJB server crashes. Any EJB server crash is always transparent to the client.
- Entity beans have a persistent object reference. The object reference encapsulates the Persistent key for this Bean.
EJB also supports the notion of a component Handle which can be retrieved from a component (session or entity), stored in a data store, and later retrieved and re-hydrated to re-establish the connection to the component.
MTS - Component Types | EJB - Component Types |
MTS components are essentially stateless components. They may or may not be transactional. They are typically accessed by one client at a time. | EJB components
are of two major types - Session Beans and Entity Beans
Session
Beans may or may not be transactional. They
are typically accessed by one client at a time. They are
of two types - Stateless Session Beans and Stateful
Session Beans. Entity Beans represent persistent data stored in the database. They are stateful and transactional. They are typically shared between several clients. |
Portability
MTS components only interoperate on Windows NT, since as I said earlier, it is a middleware model for Windows NT. That acknowledged, however, since the MTS Executive is developed by Microsoft and there are no other servers which can run MTS components, there are no portability issues for MTS components when they are moved between servers (Every Server is from the same vendor). However, COMTI technology allows some third party transactions to participate with MTS and vice versa.
EJB portability can be discussed by dividing the discussion into two -
- platform level portability and
- portability between the different EJB Servers/Containers.
Since EJB components are developed using Java, there is no platform level portability issues (as long as you are using a JVM that supports JDK1.1 and above and its extensions) since Java programs are supposed to run seamlessly from one platform to the other.
However, the same cannot be said about portability of EJB components between EJB Servers. For one, the interfaces between the Containers and Servers have been left open in the EJB Specification. Portability between EJB servers is higher for bean managed persistence than container managed. In container managed persistence, the mapping between the components and the datastore is done through a vendor provided provisioning tool, and this aspect of the specification is open. Deployment to another EJB server will certainly require re-provisioning, but the component's implementation code is probably ok.
Similarly, until the EJB 1.1 specification came in (also called the Moscone release of the EJB spec), there was no common way of defining deployment descriptors and each vendor had their own proprietary way of defining them thus making deployment on multiple different Servers a hassle since the deployment descriptors had to be modified to suit each Server. If vendors do'nt move on to support EJB 1.1, CMP entity beans may not be portable and their servers will be non-EJB 1.1 compliant.
A reference implementation and a compatibility test suite from JavaSoft will go a long way in mitigating this problem.
MTS - Portability | EJB - Portability |
Since MTS is a middleware component model for Windows NT, we can only talk about portability on NT. Since there is only one Server vendor (Microsoft) for MTS, the packaged components are portable across these Servers seamlessly. However, COMTI technology allows some third party transactions to participate with MTS and vice versa. | Since EJB components
are developed in Java, they are portable across multiple
platforms. However, since the interfaces between the EJB Server and Container have been left open in the EJB spec, there is no guaranteed portability for packaged Container Managed Persistent Entity Beans across different EJB Servers from multiple vendors even though the component's implementation code is probably ok. Similarly, pre-EJB 1.1 compliant servers have proprietary ways of defining deployment descriptors. Hence pre-EJB1.1 packaged components have to be modified to use XML so that they can be deployed on different EJB 1.1 Servers and still work seamlessly. |
Interoperability
MTS components can be invoked from other machines by clients using DCOM or through local COM if they reside on the same machine. For communication through HTTP, clients can always talk to MTS servers through the Microsoft Internet Information Server (IIS) which is the Web Server built into Windows NT. Clients wishing to use the CORBA/IIOP protocol communicate with the server component through a COM-CORBA bridge. COMTI technology allows some third party transactions to participate with MTS and vice versa. Similarly the integration of MTS and MSMQ helps to provide asynchronous messaging in MTS.
EJB components can be invoked from other machines by clients using RMI/JRMP or RMI/IIOP. While the EJB spec allows the EJB server implementors to use any communication protocol between the client and server, the EJB/CORBA mapping document is prescriptive with respect to what goes on the wire. This allows both system-level and application-level interoperability between products from vendors who choose to implement the EJB/CORBA protocol as the underlying communication protocol.
EJB clients will optionally communicate with server components using IIOP. Java EJB clients will have a choice of APIs – either the Java RMI or the Java mapping of the CORBA IDL interface. Non-Java clients communicate with the server components using IIOP and the appropriate language mapping. Clients wishing to use the COM+ protocol communicate with the server component through a COM-CORBA bridge. Also realize that the client of an EJB can itself be a server component (eg., a servlet), so an HTTP-only web client can use a servlet to make EJB invocations.
RMI/IIOP is trivially easy to do. However, RMI/JRMP is a simple protocol to do simple things. It needs to change and look an awful lot like IIOP and CORBA when it's finally mature. It thus comes down to what is missing from JRMP that is present in IIOP. In JRMP for example, it is impossible to transparently add support for distributed transactions, security, etc. The protocol simply doesn't support the addition of information to the message on the client end, and the support for retrieving this information on the server side. This means that any system built using JRMP has to deal with transactions and security as part of the message, rather than as part of the context of the message, which is added, modified and interpreted by services that are neither the client nor the server.
MTS - Interoperability | EJB - Interoperability |
MTS components can be invoked from other machines by clients using DCOM or through local COM calls if they reside on the same machine. For communication through HTTP, clients can always talk to MTS servers through the Microsoft Internet Information Server (IIS) which is the Web Server built into Windows NT. IIS will in turn make the appropriate COM calls to the MTS component servers. COMTI technology allows some third party transactions to participate with MTS and vice versa.Similarly the integration of MTS and MSMQ helps to provide asynchronous messaging in MTS. HTTP tunnelling through DCOM is an option too. | EJB components can be invoked from other machines by clients using RMI/IIOP or RMI/JRMP. For communication through HTTP, clients can always talk to servlets through the Web Server which will inturn make the appropriate calls to the EJB component. HTTP tunneling with CORBA is always possible. |
State Management
Every transactional application makes changes to one or more databases based on some input from a client or user. If the transaction commits, the database changes are made permanent. However, if the transaction fails and aborts, these changes are rolled back.
MTS uses a stateless model to ensure database consistency. Stateless models are essentially stored procedures that operate on the database state. There is no state that is kept between calls on the stored procedure, so as soon as that procedure has finished its task, you can just throw everything away, and start off on a clean slate.
This is an extremely old model that has served extremely well. This is why every database in the world has some sort of stored procedure language that runs right in the sql process and is tuned for database operations, and is good about transient state between calls (i.e., the database call is a central part of the memory management). The stateless model ensures the consistency of the database's state which is the primary goal of a transaction processing system.
MTS provides a mechanism called Shared Property Managers that makes it easy to share data among multiple concurrently executing objects without forcing the developer to program complex state sharing and synchronization logic. This state management in MTS, provides data type state management.
MTS components are essentially stateless components. MTS ensures database consistency by using such a stateless model. When a component completes its work and invokes SetComplete() or SetAbort(), MTS deactivates that component. Even though clients still retain their references to these components, MTS, transparent to the client, silently re-creates another instance using its Just In Time Activation (JITA) algorithm, the next time the client invokes a method on the component. MTS components participating in a transaction are not allowed to maintain any in-memory state across transactional boundaries. Thus, any state that the component had is destroyed by the time the transaction ends and so must be refreshed from a reliable source (e.g., the shared property manager, the client or the database) when it is next needed. This mechanism guarantees that the next transaction will always use the right state values of the component.
MTS - Typical Component Code | EJB - Typical Component Code |
///////////////////////////////////////////////////////////
public void credit (double amount, int key) { /////////////////////////////////////////////////////////// double balance = 0.0; boolean bSuccess = false; try { balance = getBalance (key); } catch (Exception e) { e.printStackTrace (); } System.out.println("changing Checkings::balance from " + balance);
// Get the Object Context |
//////////////////////////////////////////////////////////////// public void credit (double amount, int key) { //////////////////////////////////////////////////////////////// double balance = 0.0; try { balance = this.getBalance (key); } catch (Exception e) { e.printStackTrace (); } System.out.println ("changing Checking::balance from " + balance); try { //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// |
Component coding in MTS | Component coding in EJB |
EJB handles state management and uses both the stateless and stateful models to ensuring database consistency. If a session bean is stateless, the container may automatically reset the state within that component after each method call. If the session bean component is stateful, the container may automatically maintain the component's state until the component is destroyed, even if it is temporarily evicted from memory. A session bean represents work performed by an individual client. This work may either span just one single method call or may require multiple method calls. If the work spans multiple method calls, the component state must be maintained between calls. The state management options for a session bean are defined in the StateManagementType attribute in the deployment descriptor. All entity beans are inherently stateful. The EJB model uses methods like ejbLoad(), ejbStore(), ejbActivate() and ejbPassivate() to handle state management in each EJB class.
MTS - State Management | EJB - State Management |
MTS uses a stateless model to ensure server resource management. The component does not maintain any state across transactional boundaries and is required to call SetComplete() or SetAbort() as often as possible to release state. As soon as a component completes its work, MTS deactivates the component. | EJB uses both a
stateless and a stateful model. Stateful session beans
maintain state information about the connection to a
client, but this may not necessarily be database state.
Database state consistency is ensured for entity beans
using state management algoritms. The container has automatic state management algorithms which take care of component state management. The EJB model uses methods like ejbLoad(), ejbStore(), ejbActivate() and ejbPassivate() to handle state management in each EJB class. |
Persistence Management
MTS components are not persistent, even though they may contain information that needs to be persisted. MTS doesn't directly support the idea of a persistent component model. If the need arises (which may be extremely rare), the component developer needs to invent and implement his own persistence management algorithm in the component, allowing the component to read or write data to the database as needed. Thus if the need arises, the developer has to manually code the mapping of an MTS component call to database interaction.
EJB has two types of components -- persistent (entity beans) and non-persistent (session beans). An entity bean represents persistent data. Each entity bean is long lived -- as long as the associated data exists in the domain model. Each entity bean may be shared by multiple clients. The EJB spec defines a programming model for implementing persistence management in an EJB component. Whenever components are created or destroyed, or whenever components are loaded or evicted from memory, Persistence management algorithms acts on the component. The EJB spec defines methods like ejbCreate(), ejbPostCreate(), ejbRemove(), ejbLoad(), ejbStore(), ejbActivate(), and ejbPassivate() to handle persistence management in each EJB class. The ContainerManagedFields attribute in the deployment descriptor defines the persistence management option for an entity bean.
Persistence management in Entity beans are of two types. When the bean manages its own persistence, its called Bean Managed Persistence (BMP). When the bean delegates the task of Persistence Management to its container, it is called Container Managed Persistence (CMP).
In Bean Managed Persistence, the enterprise bean has to implement persistence operations directly in the bean class methods using JDBC or embedded SQL calls.
In Container Managed Persistence, the entity bean delegates persistence management to the container so that the EJB container can implicitly and transparently manage persistence for the entity bean. This means that instead of the bean developer having to code JDBC and embedded SQL calls into the bean, these database calls can be generated and handled by the EJB container. The EJB spec does not define how the container has to manage persistence and leaves the implementation details to the container tools vendor.
Session beans are very similar to MTS components in that they are not inherently persistent. However, if they need to implement persistence management, they can implement their own within the methods of the bean. The bean can also implement transaction synchronization code directly in the bean using the SessionSynchronization interface. They can use notification methods such as afterBegin(), beforeCompletion(), and afterCompletion() to signal transaction demarcation points, allowing the component to read or write data to the database as needed.
MTS - Persistence Management | EJB - Persistence Management |
MTS components are not persistent, even though they may contain information that needs to be persisted. If the need arises (which may be extremely rare), the component developer needs to invent and implement his own persistence management algorithm in the component, allowing the component to read or write data to the database as needed. | EJB has both
persistent (entity beans) and non-persistent (session
beans) components. The EJB spec defines a persistence
programming model using methods like ejbCreate(),
ejbPostCreate(), ejbRemove(), ejbLoad(), ejbStore(),
ejbActivate(), and ejbPassivate() to handle
persistence management in each EJB. Entity Beans are inherently persistent and are of two types. Bean Managed Persistence (BMP) Entity Beans where the persistence management is handled by the bean methods using JDBC or SQL calls directly. Container Managed Persistence (CMP) Entity Beans, where the container tools vendor implicitly and transparently implements persistence management for the bean. Session beans are inherently non-persistent components. However, they can implement their own persistence management, if required. They can also use the optional SessionSynchronization interface to implement transactional synchronization using the afterBegin(), beforeCompletion(), and afterCompletion() notification methods to signal transaction demarcation points. |
Resource Management
The MTS runtime implements different policies to manage scarce resources such as memory and threads. This enables components to operate automatically in a multi-user environment without forcing developers to build this complexity into their applications.
The EJB Server implements different policies to manage scarce resources such as memory and threads. Some EJB Servers may maintain all components in memory, while others may evict every component after every method call. Some Servers may even use a least-recently-used algorithm to evict components when the resources get tight.
Security
Both MTS and EJB allow setting a component's security requirements to be set by an administrator.
MTS provides a distributed security service that is integrated with Windows NT security. This allows developers to prevent unauthorized access to business applications, even if the application includes components purchased from third parties. MTS also provides standard graphical tools that allows an administrator to set the component's security requirements. Distributed security services are provided by the NT Lan Manager (NTLM) security protocol supported by both Windows and Windows NT. The administrator is allowed to graphically administer authorization and restrict access to secure objects and methods. The administrator can thus, using the GUI tools supplied, graphically associate a method with a list of users that have rights to invoke a method.
EJB provides authorization using the Java security model. EJB server implementations may choose to use connection-based authentication in which the client program establishes a connection to the EJB server. The client's identity is attached to the connection at connection establishment time. The EJB/CORBA mapping specifies that the CORBA Principal propagation mechanism be used. This means that the client ORB adds the client's principal to each client request. The communication mechanism between the client and the server propagates the client's identity to the server. Security in EJB 1.1 is declaratively defined in the deployment descriptors and is role based.
Java platform security supports authentication and authorization services to restrict access to secure objects and methods. The security rules for each EJBean are defined declaratively in a set of AccessControlEntry objects defined in the deployment descriptor. An AccessControlEntry object associates a method with a list of users that have rights to invoke a method. The EJB container uses the AccessControlEntry attribute to perform all security checks on behalf of the enterprise bean.
MTS - Security | EJB - Security |
MTS provides authentication and authorization using NT security. Distributed security services are provided by the NT Lan Manager (NTLM) security protocol supported by both Windows and Windows NT. The administrator can thus use the GUI tools supplied, to graphically associate a component or method with a list of users that have rights to invoke that component or method. | In EJB the security rules for each EJBean are defined declaratively in a set of AccessControlEntry objects defined in the deployment descriptor. An AccessControlEntry object associates a method with a list of users that have rights to invoke a method. The EJB container uses the AccessControlEntry attribute to perform all security checks on behalf of the enterprise bean. |
Deployment
Both MTS and EJB models support Application Partitioning. Administrators can easily partition an application across multiple servers by deploying an application’s components into several packages, with each package running on its own server. This improves system fault isolation, while increasing application performance and scalability.
MTS includes a component packaging service that manages the complicated logistics of integrating, installing, and deploying many components as a single application. In addition to this, using MTS packages, developers and administrators can easily isolate components so they operate in their own system process. This is called Process Isolation. This provides an additional level of failure isolation and data protection.
EJB components can be packaged as individual enterprise beans, as a collection of EJBs, or as a complete application system. EJB components are distributed as jar files. The jar file contains a manifest file outlining the contents of the file, the enterprise bean class files, the Deployment Descriptor objects, and optionally the Environment properties. Deployment Descriptors defined in XML, specify the runtime settings for the EJB. The settings can be set at application assembly or deployment time.
The Source Code Comparison
The source code comparison which follows tries to build a three-tiered Checking account application, with the client tier, a middleware component tier (of either an MTS or an EJB component) and a database as shown in Figure 6:
Figure 6: Our Example three-tier Bank Checking Account Application
The Interface Definition Code Comparison
The Interface
Whenever a client needs some service from a remote distributed object, it invokes a method implemented by the remote object. The service that the remote distributed object (Server) provides is encapsulated as an object and the remote object's interface is described in an Interface Definition Language (IDL). The interfaces specified in the IDL file serve as a contract between a remote object server and its clients. Clients can thus interact with these remote object servers by invoking methods defined in the IDL.
MTS - The IDL file shows that our MTS server implements a dual interface. COM supports both static and dynamic invocation of objects. For the static invocation to work, The Microsoft IDL (MIDL) compiler creates the proxy and stub code when run on the IDL file. These are registered in the systems registry to allow greater flexibility of their use. This is the vtable method of invoking objects. For dynamic invocation to work, COM objects implement an interface called IDispatch. As with CORBA or Java/RMI, to allow for dynamic invocation, there has to be some way to describe the object methods and their parameters. Type libraries are files that describe the object, and COM provides interfaces, obtained through the IDispatch interface, to query an Object's type library. In COM, an object whose methods are dynamically invoked must be written to support IDispatch. The MTS IDL file also associates the IChecking interface with an object class Checking as shown in the coclass block. Also notice that in MTS, each interface is assigned a Universally Unique IDentifier (UUID) called the Interface ID (IID). Similarly, each object class is assigned a unique UUID called a CLasS ID (CLSID). COM gives up on multiple inheritance to provide a binary standard for object implementations. Instead of supporting multiple inheritance, COM uses the notion of an object having multiple interfaces to achieve the same purpose. This also allows for some flexible forms of programming.
The Checking Account Server component has methods for managing a typical Bank Checking Account. Consequently, it has methods to create an account, and credit & debit money from the Account. Consequently, we define different methods like createAccount(), credit(), debit(), getBalance() and getCustomerName().
EJB - Notice that unlike MTS, EJB uses a .java file to define it's remote interface. This interface will ensure type consistency between the EJB client and the EJB Server Object. Every remotable server object in EJB has to extend the javax.ejb.EJBObject class. Similarly, any method that can be remotely invoked in EJB may throw a java.rmi.RemoteException. java.rmi.RemoteException is the superclass of many more RMI specific exception classes. We define an interface called Checking which extends the javax.ejb.EJBObject class. Also notice that all the remote methods like createAccount(), credit(), debit(), getBalance() and getCustomerName() throw a java.rmi.RemoteException.
MTS - IDL Definition |
EJB - Home and Remote Interfaces | |
[ uuid (6B6EBD40-01C0-11d3-97EE-006097A7D34F), version (1.0) ] library Bank { importlib ("stdole32.tlb"); /*
/* |
package Bank; import javax.ejb.*;
public interface
CheckingHome extends EJBHome { |
|
package Bank; import javax.ejb.*; public interface
Checking extends EJBObject { |
||
Bank.idl |
CheckingHome.java & Checking.java |
The Component Implementation Code Comparison
Implementing the Server Component
MTS server component- All the classes that are required for Java/COM are defined in the com.ms.com package. The com.ms.mtx package contains all the MTS related class definitions. The com.ms.wfc.data package contains all the ADO related class definitions. The MTS Server object shown below implements the IChecking interface that we defined in our IDL file. Implementation code for the different methods like createAccount(), credit(), debit(), getBalance() and getCustomerName() are also shown.
In MTS, the developer implements the business methods, but doesn't have to implement any callback methods. The developer must code transaction commit and rollback demarcation statements. If everything goes right, the developer must call SetComplete() on the transaction context object. If anything goes wrong, the developer must call SetAbort() on the transaction context object. MTS supports four automatic transaction semantics which may de assigned declaratively. MTS does not support manual transactions.
EJB server component- All the classes that are required for Java/RMI are defined in the java.rmi package. The javax.ejb package contains all the EJB related class definitions. The java.sql package contains all the JDBC related class definitions. The EJB Server object shown below implements the SessionBean interface since its a session bean. Implementation code for the different methods like createAccount(), credit(), debit(), getBalance() and getCustomerName() are also shown.
In EJB, the transaction semantics for an enterprise bean are defined declaratively rather than programmatically. At runtime, the EJB container automatically implements transaction services according to the TransactionAttribute specified in the deployment descriptor. In EJB, the developer must define the remote interface, and the developer must implement the business methods and the EJB callback methods (although in many cases they can be left empty). The EJB container deployment tools automatically generate the EJB wrapper objects.
MTS - Component implementation code |
EJB - enterprise Bean implementation Code |
package
bank; import com.ms.com.*; |
package
Bank; import java.rmi.*; |
/**
@com.register(clsid=6B6EBD42-01C0-11D3-97EE-006097A7D34F,
* typelib=6B6EBD40-01C0-11D3-97EE-006097A7D34F, version="1.0") * @com.transaction (required) */ //////////////////////////////////////////////////////////////// // Class Definition //////////////////////////////////////////////////////////////// public class CheckingImpl implements IUnknown, com.ms.com.NoAutoScripting, bank.ICheckingDefault { //////////////////////////////////////////////////////////////// static final int ALL_FIELDS = 0; static final int NAME_FIELD = 1; static final int BALANCE_FIELD= 2; //odbc type connection string static final String m_strOpenConn = "PROVIDER=Microsoft.Jet.OLEDB.3.51;"+ "Data Source=E:\\MyProjects\\AccountMTS\\BankServer\\Bank.mdb"; |
//////////////////////////////////////////////////////////////////
// Class Definition ////////////////////////////////////////////////////////////////// public class CheckingBean implements SessionBean { ////////////////////////////////////////////////////////////////// protected SessionContext _sessionContext; static final int ALL_FIELDS = 0; static final int NAME_FIELD = 1; static final int BALANCE_FIELD= 2; ////////////////////////////////////////////////////////////////// // Home Interface methods ////////////////////////////////////////////////////////////////// public void ejbCreate () { } |
////////////////////////////////////////////////////////////////
// Business methods //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// public void createAccount (int key, String name, double startingBalance) { //////////////////////////////////////////////////////////////// if (startingBalance < 0) return; boolean bSuccess = false; IObjectContext context = null; try { // Get the Object Context context = (IObjectContext)MTx.GetObjectContext (); truePut (CheckingImpl.ALL_FIELDS, key, (new Double (startingBalance)).toString (), name); bSuccess = true; } catch (Exception e) { bSuccess = false; e.printStackTrace (); } // Upon exit, always call SetComplete () if happy, // or SetAbort () if unhappy // We do this since we never save state across method calls. finally { if (context!=null) { if (bSuccess == true) context.SetComplete (); else context.SetAbort (); } } } |
//////////////////////////////////////////////////////////////////
// Remote Interface methods ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// public void createAccount (int key, String name, double startingBalance) { ////////////////////////////////////////////////////////////////// if (startingBalance > 0) { truePut (CheckingBean.ALL_FIELDS, key, startingBalance, name); } } |
////////////////////////////////////////////////////////////////
public void credit (double amount, int key) { //////////////////////////////////////////////////////////////// double balance = 0.0; boolean bSuccess = false; try { balance = getBalance (key); } catch (Exception e) { e.printStackTrace (); } System.out.println("changing Checkings::balance from " + balance);
// Get the Object Context |
//////////////////////////////////////////////////////////////// public void credit (double amount, int key) { //////////////////////////////////////////////////////////////// double balance = 0.0; try { balance = this.getBalance (key); } catch (Exception e) { e.printStackTrace (); } System.out.println ("changing Checking::balance from " + balance); try { |
////////////////////////////////////////////////////////////////
public void debit (double amount, int key) { //////////////////////////////////////////////////////////////// double balance = 0.0; boolean bSuccess = false; try { balance = getBalance (key); } catch (Exception e) { e.printStackTrace (); } System.out.println ("changing Checkings::balance from " + balance);
// Get the Object Context |
//////////////////////////////////////////////////////////////// public void debit (double amount, int key) { //////////////////////////////////////////////////////////////// double balance = 0.0; try { balance = getBalance (key); } catch (Exception e) { e.printStackTrace (); } System.out.println("changing CheckingBean::balance from " + balance); try { if ( (amount > 0) && (balance >= amount) ) { balance -= amount; truePut (CheckingBean.BALANCE_FIELD, key, balance, ""); } } catch (Exception e) { e.printStackTrace (); } System.out.println(" to " + balance); } |
////////////////////////////////////////////////////////////////
public double getBalance (int key) { //////////////////////////////////////////////////////////////// double balance = 0.0; boolean bSuccess= false; System.out.println ("Invoking Checkings::getBalance"); IObjectContext context = null; try { // Get the Object Context context = (IObjectContext)MTx.GetObjectContext (); String result = trueGet (CheckingImpl.BALANCE_FIELD, key); balance = (new Double (result)).doubleValue(); bSuccess = true; } catch (Exception e) { bSuccess = false; e.printStackTrace (); } // Upon exit, always call SetComplete () if happy, // or SetAbort () if unhappy // We do this since we never save state across method calls. finally { if (context!=null) { if (bSuccess == true) context.SetComplete (); else context.SetAbort (); } } System.out.println ("Checkings::balance is " + balance); return balance; } |
//////////////////////////////////////////////////////////////// public double getBalance (int key) { //////////////////////////////////////////////////////////////// double balance = 0.0;
try { |
////////////////////////////////////////////////////////////////
public String getCustomerName (int key) { //////////////////////////////////////////////////////////////// String name = null; boolean bSuccess= false; System.out.println ("Invoking Checkings::getCustomerName"); IObjectContext context = null; try { // Get the Object Context context = (IObjectContext)MTx.GetObjectContext (); name = trueGet (CheckingImpl.NAME_FIELD, key); bSuccess = true; } catch (Exception e) { bSuccess = false; e.printStackTrace (); } // Upon exit, always call SetComplete () if happy, // or SetAbort () if unhappy // We do this since we never save state across method calls. finally { if (context!=null) { if (bSuccess == true) context.SetComplete (); else context.SetAbort (); } } System.out.println ("Checkings::customer_name is " + name); return name; } |
//////////////////////////////////////////////////////////////// public String getCustomerName (int key) { //////////////////////////////////////////////////////////////// String name = null; boolean bSuccess= false; try { name = trueGet (CheckingBean.NAME_FIELD, key); } catch (Exception e) { e.printStackTrace (); } System.out.println ("Checking::customer_name is " + name); return name; } |
////////////////////////////////////////////////////////////////
// Other Internal methods //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// String trueGet (int type, int key) { //////////////////////////////////////////////////////////////// Connection connection = null; Recordset rset = null; Variant rowCount = new Variant (); String result = null; String query = null; String fieldName = null; switch (type) { case CheckingImpl.NAME_FIELD: query = "SELECT CUSTOMER_NAME FROM Checkings WHERE ACCOUNT_NUMBER="; fieldName = "CUSTOMER_NAME"; break; case
CheckingImpl.BALANCE_FIELD: |
//////////////////////////////////////////////////////////////// // Other Internal methods //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// String trueGet (int type, int key) { //////////////////////////////////////////////////////////////// Connection connection = null; PreparedStatement stmt= null; ResultSet rset = null; String result = null; String query = null; String fieldName = null; switch (type) { case CheckingBean.NAME_FIELD: query = "SELECT CUSTOMER_NAME FROM Checkings "+ "WHERE ACCOUNT_NUMBER = ?"; fieldName = "CUSTOMER_NAME"; break; case
CheckingBean.BALANCE_FIELD: |
////////////////////////////////////////////////////////////////
String truePut (int type, int key, String balance, String name) { //////////////////////////////////////////////////////////////// Connection connection = null; Recordset rset = null; Variant rowCount = new Variant (); String result = null; String query = null; switch (type) { case CheckingImpl.BALANCE_FIELD: query = "UPDATE Checkings SET BALANCE=" + balance + " WHERE ACCOUNT_NUMBER="+ key; break; case
CheckingImpl.NAME_FIELD: } |
//////////////////////////////////////////////////////////////// String truePut (int type, int key, double balance, String name) { //////////////////////////////////////////////////////////////// Connection connection = null; PreparedStatement stmt= null; ResultSet rset = null; String result = null; String query = null; switch (type) { case CheckingBean.BALANCE_FIELD: query = "UPDATE Checkings SET BALANCE = ? "+ "WHERE ACCOUNT_NUMBER = ?"; break; case
CheckingBean.NAME_FIELD: |
//////////////////////////////////////////////////////////////// public Connection getConnection () throws Exception { //////////////////////////////////////////////////////////////// return DriverManager.getConnection ("jdbc:odbc:Bank"); } |
|
//////////////////////////////////////////////////////////////// public CheckingBean () { //////////////////////////////////////////////////////////////// } //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// } |
|
CheckingImpl.java |
CheckingBean.java |
The Client Code Comparison
Implementing the MTS/EJB Client
Both the EJB and MTS clients have been implemented using Java code using the Visual J++ version 6.0 compiler.
MTS Client - The MTS client shown below calls into the MTS server object's methods by first acquiring a pointer to the server object. The new keyword here instantiates the bank.Checking MTS Server object. This leads the Microsoft JVM to use the CLSID to make a CoCreateInstance() call. The IUnknown pointer returned by CoCreateInstance() is then cast to bank.IChecking, as shown below.
EJB Client
- The EJB client shown below calls into the EJB server object's
methods by first acquiring a pointer to the server object. The
client first uses JNDI to get a reference to the EJB component's
home interface. It then uses this reference to create an instance
of the EJB component and holds on to this reference when calling
into the component methods.
MTS - Client Code |
EJB - Client Code |
import
com.ms.wfc.app.*; import com.ms.wfc.core.*; import com.ms.wfc.ui.*; import com.ms.wfc.html.*; import com.ms.com.*; import bank.*; |
import
com.ms.wfc.app.*; import com.ms.wfc.core.*; import com.ms.wfc.ui.*; import com.ms.wfc.html.*; import com.ms.com.*; import java.rmi.*; import java.util.*; import Bank.*; |
public
class CheckingClient extends Form { static final int INITIAL_RECORD = 11; int accountNo; String name; double balance; int count; bank.IChecking server; |
public
class CheckingClient extends Form { static final int INITIAL_RECORD = 11; int accountNo; String name; double balance; int count; public CheckingHome home = null; public Checking server = null; |
////////////////////////////////////////////////////////////////
public CheckingClient () { //////////////////////////////////////////////////////////////// super(); initForm(); try { server = (bank.IChecking) new bank.Checking (); } catch (Exception ex) { ex.printStackTrace (); } fillAccountList (); } |
//////////////////////////////////////////////////////////////// public CheckingClient () { //////////////////////////////////////////////////////////////// super(); initForm(); try { home = (CheckingHome) Naming.lookup ("checking"); if (home == null) { System.out.println ("null TellerHome returned..."); } server = home.create (); } catch (Exception e) { e.printStackTrace (); } fillAccountList (); } |
/**
* CheckingClient overrides dispose so it can clean up the * component list. */ //////////////////////////////////////////////////////////////// public void dispose() { //////////////////////////////////////////////////////////////// super.dispose(); components.dispose(); } |
/**
* CheckingClient overrides dispose so it can clean up the * component list. */ //////////////////////////////////////////////////////////////// public void dispose() { //////////////////////////////////////////////////////////////// super.dispose(); components.dispose(); } |
////////////////////////////////////////////////////////////////
public void fillAccountList() { //////////////////////////////////////////////////////////////// accountList.removeAll (); count = 0; boolean bMoreElements = true; do { try { name = server.getCustomerName (count+INITIAL_RECORD); if(name == null) break; accountList.addItem ((new Integer (count+INITIAL_RECORD)) .toString ()); } catch (Exception ex) { ex.printStackTrace (); bMoreElements = false; } ++count; } while (bMoreElements == true); } |
//////////////////////////////////////////////////////////////// public void fillAccountList() { //////////////////////////////////////////////////////////////// accountList.removeAll (); count = 0; boolean bMoreElements = true; do { try { name = server.getCustomerName (count+INITIAL_RECORD); if(name == null) break; accountList.addItem((new Integer (count+INITIAL_RECORD)) .toString ()); } catch (Exception ex) { ex.printStackTrace (); bMoreElements = false; } ++count; } while (bMoreElements == true); } |
////////////////////////////////////////////////////////////////
private void retrieveButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// if (accountList.getSelectedItem () == null) return; String accSel = (accountList.getSelectedItem ()).toString (); accountNo = (new Integer (accSel)).intValue (); try { balance = server.getBalance (accountNo); name = server.getCustomerName (accountNo); numberEdit.setText ( (new Integer (accountNo)).toString () ); nameEdit.setText (name); balanceEdit.setText ( (new Double (balance)).toString () ); } catch (Exception ex) { ex.printStackTrace (); } } |
//////////////////////////////////////////////////////////////// private void retrieveButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// if (accountList.getSelectedItem () == null) return; String accSel = (accountList.getSelectedItem ()).toString (); accountNo = (new Integer (accSel)).intValue (); try { balance = server.getBalance (accountNo); name = server.getCustomerName (accountNo); numberEdit.setText ( (new Integer (accountNo)).toString () ); nameEdit.setText (name); balanceEdit.setText ( (new Double (balance)).toString () ); } catch (Exception ex) { ex.printStackTrace (); } } |
////////////////////////////////////////////////////////////////
private void addButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// accountNo = count+INITIAL_RECORD; name = nameEdit.getText (); balance = ( new Double (balanceEdit.getText ()) ).doubleValue (); if ((accountNo == 0) || (name == null)) return; try { server.createAccount (accountNo, name, balance); } catch (Exception ex) { ex.printStackTrace (); } fillAccountList (); } |
//////////////////////////////////////////////////////////////// private void addButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// accountNo = count+INITIAL_RECORD; name = nameEdit.getText (); balance = ( new Double (balanceEdit.getText ()) ).doubleValue (); if ((accountNo == 0) || (name == null)) return; try { server.createAccount (accountNo, name, balance); } catch (Exception ex) { ex.printStackTrace (); } fillAccountList (); } |
////////////////////////////////////////////////////////////////
private void closeButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// Application.exit (); } |
//////////////////////////////////////////////////////////////// private void closeButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// Application.exit (); } |
////////////////////////////////////////////////////////////////
private void creditButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// if (accountList.getSelectedItem () == null) return; String accSel = (accountList.getSelectedItem ()).toString (); accountNo = (new Integer (accSel)).intValue (); double amount = ( new Double (creditEdit.getText ()) ).doubleValue (); try { server.credit (amount, accountNo); balance = server.getBalance (accountNo); name = server.getCustomerName (accountNo); numberEdit.setText ( (new Integer (accountNo)).toString () ); nameEdit.setText (name); balanceEdit.setText ( (new Double (balance)).toString () ); } catch (Exception ex) { ex.printStackTrace (); } } |
//////////////////////////////////////////////////////////////// private void creditButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// if (accountList.getSelectedItem () == null) return; String accSel = (accountList.getSelectedItem ()).toString (); accountNo = (new Integer (accSel)).intValue (); double amount = ( new Double (creditEdit.getText ()) ).doubleValue (); try { server.credit (amount, accountNo); balance = server.getBalance (accountNo); name = server.getCustomerName (accountNo); numberEdit.setText ( (new Integer (accountNo)).toString () ); nameEdit.setText (name); balanceEdit.setText ( (new Double (balance)).toString () ); } catch (Exception ex) { ex.printStackTrace (); } } |
////////////////////////////////////////////////////////////////
private void debitButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// if (accountList.getSelectedItem () == null) return; String accSel = (accountList.getSelectedItem ()).toString (); accountNo = (new Integer (accSel)).intValue (); double amount = ( new Double (debitEdit.getText ()) ).doubleValue (); try { server.debit (amount, accountNo); balance = server.getBalance (accountNo); name = server.getCustomerName (accountNo); numberEdit.setText ( (new Integer (accountNo)).toString () ); nameEdit.setText (name); balanceEdit.setText ( (new Double (balance)).toString () ); } catch (Exception ex) { ex.printStackTrace (); } } |
//////////////////////////////////////////////////////////////// private void debitButton_click(Object source, Event e) { //////////////////////////////////////////////////////////////// if (accountList.getSelectedItem () == null) return; String accSel = (accountList.getSelectedItem ()).toString (); accountNo = (new Integer (accSel)).intValue (); double amount = ( new Double (debitEdit.getText ()) ).doubleValue (); try { server.debit (amount, accountNo); balance = server.getBalance (accountNo); name = server.getCustomerName (accountNo); numberEdit.setText ( (new Integer (accountNo)).toString () ); nameEdit.setText (name); balanceEdit.setText ( (new Double (balance)).toString () ); } catch (Exception ex) { ex.printStackTrace (); } } |
/**
* NOTE: The following code is required by the Visual J++ form * designer. It can be modified using the form editor. Do not * modify it using the code editor. */ Container components = new Container(); ListBox accountList = new ListBox(); Button retrieveButton = new Button(); Button closeButton = new Button(); Button addButton = new Button(); Edit numberEdit = new Edit(); Edit nameEdit = new Edit(); Edit balanceEdit = new Edit(); Label label1 = new Label(); Label label2 = new Label(); Label label3 = new Label(); Edit creditEdit = new Edit(); Edit debitEdit = new Edit(); Button creditButton = new Button(); Button debitButton = new Button(); private
void initForm() { accountList.setAllowDrop(true);
retrieveButton.setLocation(new
Point(184, 240)); closeButton.setLocation(new
Point(184, 280)); addButton.setLocation(new
Point(96, 112)); numberEdit.setLocation(new
Point(120, 16)); nameEdit.setLocation(new
Point(120, 48)); balanceEdit.setLocation(new
Point(120, 80)); label1.setLocation(new
Point(16, 16)); label2.setLocation(new
Point(16, 48)); label3.setLocation(new
Point(16, 80)); creditEdit.setLocation(new
Point(16, 152)); debitEdit.setLocation(new
Point(16, 184)); creditButton.setLocation(new
Point(184, 152)); debitButton.setLocation(new
Point(184, 184)); this.setNewControls(new
Control[] { |
/**
* NOTE: The following code is required by the Visual J++ form * designer. It can be modified using the form editor. Do not * modify it using the code editor. */ Container components = new Container(); ListBox accountList = new ListBox(); Button retrieveButton = new Button(); Button closeButton = new Button(); Button addButton = new Button(); Edit numberEdit = new Edit(); Edit nameEdit = new Edit(); Edit balanceEdit = new Edit(); Label label1 = new Label(); Label label2 = new Label(); Label label3 = new Label(); Edit creditEdit = new Edit(); Edit debitEdit = new Edit(); Button creditButton = new Button(); Button debitButton = new Button(); private
void initForm() { accountList.setAllowDrop(true);
retrieveButton.setLocation(new
Point(184, 240)); closeButton.setLocation(new
Point(184, 280)); addButton.setLocation(new
Point(96, 112)); numberEdit.setLocation(new
Point(120, 16)); nameEdit.setLocation(new
Point(120, 48)); balanceEdit.setLocation(new
Point(120, 80)); label1.setLocation(new
Point(16, 16)); label2.setLocation(new
Point(16, 48)); label3.setLocation(new
Point(16, 80)); creditEdit.setLocation(new
Point(16, 152)); debitEdit.setLocation(new
Point(16, 184)); creditButton.setLocation(new
Point(184, 152)); debitButton.setLocation(new
Point(184, 184)); this.setNewControls(new
Control[] { |
/**
* The main entry point for the application. * * @param args Array of parameters passed to the application * via the command line. */ //////////////////////////////////////////////////////////////// public static void main (String args[]) { //////////////////////////////////////////////////////////////// Application.run (new CheckingClient ()); } } |
/**
* The main entry point for the application. * * @param args Array of parameters passed to the application * via the command line. */ //////////////////////////////////////////////////////////////// public static void main (String args[]) { //////////////////////////////////////////////////////////////// Application.run (new CheckingClient ()); } } |
CheckingClient.java |
CheckingClient.java |
Conclusion
The architectures of MTS and EJB provide mechanisms for developing distributed enterprise components. Though the mechanisms that they employ to achieve their objective may be different, the approach each of them take is more or less similar.
Acknowledgements |
A lot of people took the time to
review this write-up and offer their valuble comments. I
thank all of them. In particular, I would like to thank Chad
Verbowski (Microsoft), Chris Raber (Gemstone), Sriram Srinivasan (BEA Weblogic), Hal Hildebrand (Oracle) and Rickard
Oberg (DreamBeans). I am still receiving excellent comments and suggestions from various people. This article reflects the discussions that I have had with a lot of them. The acknowledgements column is a way of showing my appreciation to all these people who have said 'We Care'. |
The Sources
Download the MTS and EJB implementations of the Bank Checking Account as a zip file