Two Tier vs N-Tier
Client/server applications, and even Java applications that call a database directly, represent the original, two-tiered application architecture. This architecture fits many needs, but often there is a penalty - the need to redevelop or copy code from one application to another as it is developed. More importantly, although Java is certainly a significant language for development, it's a recently developed one, and much of the logic that we need to use is written in other languages. Distributed applications, be they Web applets or standalone applications, also have a need to centralize logic, both to minimize the maintenance of the client, and to keep download time and response times reasonable.
Most of these issues have existed for years now, and the concept of externalizing and centralizing business logic from application logic developed into what is usually called a three-tiered or n-tiered architecture. CICS was arguably one of the first middle-tier solutions developed. Tuxedo from BEA has been available for many years as well. The concept is simple - remove common business logic from application code and place it in a central repository, where any application might call it. And one of the first things this middle-tier became charged with was handling transaction management. A transaction is a set of one or more database statements that must be treated as a unit of work. Simpler applications have no real need of transaction management, but applications that access multiple databases on different machines, perhaps in different locations, have a strong need for transaction management. This provides answers to the problem of two-phased commit, and of grouping transaction logic.
Middle Tier Servers
Over the years, a number of solutions have been tried, some more successful than others. In a mainframe world, CICS provides a certain amount of this functionality. In the client/server world, Tuxedo and other TP Monitors allow this type of development to occur. Unfortunately, the models that these systems present differ greatly from how we would like to view the world as Java programmers. Ideally, we'd like to be able to use the same syntax and constructs that we use for our application programming to talk to the Middle Tier. We'd like to view the logic in that tier as objects with methods, or sometimes as a single logical database, regardless of the number of real physical databases involved in the process. Previous solutions fell very short of this desire, which leads us to Jaguar CTS.
Note: The Common Object Request Broker Architecture (CORBA) specification provides another solution to this problem. Jaguar will eventually be CORBA-compliant so you will be able to leverage any Jaguar work should you need to implement a CORBA solution as well.
Jaguar CTS is a Component Transaction Server from Sybase, Inc. It's currently at version 1.1, which means it's still very new, and still has its share of undesired features. That's bugs to you and me. But in truth, Jaguar is also ten years old and represents a proven history of providing open solutions. So how can it be both? Jaguar is based upon Sybase's Open Client/Open Server architecture, mainly Open Server. Open Server was a set of APIs that allowed you to build business logic that could be called from the database and referred to in applications. Unfortunately, every time you made a change to the logic, you had to rebuild the Server and it didn't readily support a variety of programming languages or components.
The Jaguar Manager
Jaguar is the result of a concerted effort to take the power of Open Server and make it usable for the component builder, regardless of the language used. Unlike Open Server, Jaguar is a tool that allows you to register components, which can be developed in a number of languages and object models. In particular, Jaguar supports Java, C/C++ and Active X controls (C/C++, Visual Basic, PowerBuilder, Delphi, etc.). Native support for PowerBuilder code is in development, as is the ability to use CORBA objects. We'll focus here on what we can do in Java.
Before we dive in, it's probably good to know a little bit about what Jaguar can do. First of all, Jaguar allows you to develop components which will be placed inside the server. Once there, Jaguar can provide access to these components to any language or product that can use a .JAVA file, a .C(PP) file or any product that can call stored procedures in a database.
Jaguar also provides several features that make it more than just an object broker. One of these is transaction management. We spoke a little about transaction management above. Jaguar makes it easy to group components into a transaction and specify which components participate. Jaguar also provides database connection caching, allowing connections to be reused and reducing the time it takes to establish a connection to a database.
One of the biggest advantages to Jaguar from a component standpoint is the ability to develop components in a variety of languages. Equally important is the ability to use Jaguar as a server from a variety of languages. The first point allows many companies to leverage existing code, usually in C or C++, with minimal changes. There are certain restrictions on what can be passed into and out of Jaguar and how methods are declared, but these will be lifted or reduced in version 1.5. For now, functions that Jaguar will export must be declared to return void. There's also a restriction on what you can pass in as parameters. Simple data types such as integer and string are fine. More complex types including arrays, structures and objects cannot be passed in. Probably the most significant impact this has is on result sets.
Methods in Jaguar can return a result set, which is one or more rows of data, plus enough metadata to describe the columns. Java has a number of classes that can act as result set consumers, or you can manually digest the result set (the metadata is the first row of the data in this case). Unfortunately, this is a one way street. You can't make changes to the result set and return them in bulk, or even as the row changes, at least not in the same manner that you would if you had obtained the result set directly from a database. This means that you'll end up writing insert, update and delete functions for your server objects so that they can handle changes, and code in the client objects that will know when to call the correct method. You'll probably have to keep track of the state of each row with a flag in order to accomplish this.
Transaction monitoring allows Jaguar to provide transaction coordination across multiple objects. Objects can be marked as needing to be part of a transaction, either a new one or one that is already occurring. Objects that are part of an already occurring transaction only create a new transaction if they are the first object called. Objects that require a new transaction always create a transaction for themselves.
Within a transaction, Jaguar keeps track of commits and rollbacks and intercepts these calls to the database. A real commit occurs only when the last object in a transaction executes a commit. If any object in the transaction performs a rollback, the entire set of statements for the transaction (including those made by other objects that issued a commit) will be rolled back. This allows you to code your components as if they were the only object in a transaction for testing, but use them with other objects in production.
Jaguar also features connection caching. One of the biggest performance hits of a database application is the creation of a connection to the database. Since each object is distinct and will have its own connection, creating a series of objects could require a large number of connections, all using the same database parameters. Jaguar provides connection caching to minimize this work. If a cache exists that matches the database information of a requested connection, Jaguar does not perform an actual physical connection for each request. The first time through, a connection is established, but subsequent requests result in a logical mapping to the first physical connection. This results in faster setup of most components, but it does require setup for each user. Large numbers of users can make administration of the cache a difficult task, similar to the task of database administration. You should expect to have a Jaguar administrator who will work at least part time on creating connection caches and registering components.
Jaguar has several other features that are more for convenience and organization than for any significant benefit to programming. The next four items are used to group functionality or determine availability of the various components to a particular user.
Jaguar can have several 'named' servers that run on a particular machine. By default, the first server is named Jaguar and has listeners on ports 8080 (http) and 7878 (queries and master). New servers can be created, but they must have different ports for the various listeners. Roles, packages and components can also be assigned to a particular server. This allows you to segment the functionality your users see at a very broad level. Assuming you had some overall concept of three categories of user (guest, ordinary, administrative), you could have each connect to a different named Jaguar server. Packages could be installed only as needed for each of these servers, effectively removing sensitive functionality from unauthorized users. (Note: Roles can be used to achieve similar functionality on a more granular level.)
A package in Jaguar is a set of components. By creating a package, you allow for a larger grouping of functionality, which makes it easier to manage components. By installing a package into a particular server, you allow access to all of the components in that package. It's similar to the concept of a ROLE in a database.
Components are the objects that make up Jaguar. They have data and methods and can be grouped into packages. Components can be written in a variety of languages. Components cannot be placed into a server directly; they must be part of a package. The Jaguar manager provides a number of options for describing a component, including the methods of the component. It's also possible to declare a component and its method signatures prior to creation of the component. This allows the front end (client) programmers to begin coding prior to the completion of the server components.
Registering a Component - Language choices
Roles are optional in Jaguar, but they can be used in a similar fashion as ROLES in a database - namely, to restrict access to privileged functionality. Jaguar provides the ability to create users and assign them to roles, as well as the ability to assign roles to particular components.
Java and Jaguar
Now that we know a little bit about what Jaguar is, the question that should be on the tip of your tongue is why do I need it and how do I use it? I won't go into a long rationale for n-tiered architectures - either you need one or you don't. Given that you need an n-tiered approach, you might need Jaguar if you have a need for a variety of languages in the business logic layer, or you need the benefits of an object manager with transaction monitoring.
In the next two issues of JDJ, we'll look at how to use this. We'll start by examining a simple but realistic server component next month. Then we will focus on how to use a Jaguar component (any component, it doesn't have to be Java, although in our example it will be) in a client application or applet.
About the Author
Sean Rhody is a respected industry consultant and a leading authority on PowerBuilder. He is also editor-in-chief of the PowerBuilder Developer's Journal and one of the authors of "PowerBuilder 5.0: Secrets of the PowerBuilder Masters." You can contact Sean at [email protected]