1. Introduction

I recently had to work with Oracle Tuxedo during a migration project of a legacy vb6 application to .Net.

I found that there was not a lot of information at hand available for developers so I thought I would put together a quick and concise guide with a few of my own tips that I picked up during development.

Hopefully, this article will clear up a few mysteries and help get going if you find yourself in a similar situation.

Code examples are in C# but are equally as applicable in VB.Net with a simple syntax change.

2. What is Oracle Tuxedo

Tuxedo stands for Transactions under UniX Extended for Distributed Operation (TUXEDO) and is a middleware that handles translations and data conversions between dissimilar machines. Using Tuxedo, users can develop, manage, and deploy distributed applications independently of the underlying hardware, operating system, network, and database environment. It

As children, most of us has at some time made a telephone from two tin cans and a piece of string. If you imagine that the string is the network and the tin cans are the client- and server-side programs, essentially Tuxedo is the knots that hold the ends of the string to ends of the string to the cans.

Often you will find Tuxedo referred to as BEA Tuxedo, BEA owned the rights to Tuxedo before Oracle acquired BEA in 2008.

3. Setting up

During installation of Tuxedo, you can choose to include the Tuxedo .NET Workstation Client bundle. This provides easy access to the Tuxedo .NET client classes and connectivity between .NET applications and Tuxedo services. It can be used with the majority of .NET languages. (for example, C#, J#, VB .NET, and ASP.NET).

Once downloaded and installed, you must include the libwsclient.dll component in your solution. Oracle recommends manually registering libwscdnet.dll in the global assembly cache.

According to Oracle’s website, the .NET client has been tested up to .NET 4.5.

4.How does Tuxedo work?

In the Tuxedo architecture, named services are defined on a server-side table called the Tuxedo bulletin board.

Each service requires information from the client which must be transmitted using Tuxedo buffers (section 6). When initialised these are obtained from a shared memory location created by Tuxedo specifically for interprocess communication.

Once the buffers are populated with the associated payload (section 8), the client must pack these with a function to call the requested service defined with a string.

The request is then routed by Tuxedo to the specified service as defined on the bulletin board.

data/admin/2019/6/beafit.gif

Source : https://docs.oracle.com/cd/E13161_01/tuxedo/docs10gr3/int/intbas.html

5. Connecting to Tuxedo

Let's focus on how to express Tuxedo's functionality in .Net.

Firstly, in order to connect your application to your machine’s Tuxedo services, initialise a TypedTPINIT() object.

TypedTPINIT tpinfo = new TypedTPINIT();
tpinfo.cltname = "ApplicationVersion" //Value defined on server required authentication
tpinfo.usrname = "User9999" //Username
AppContext tuxContext = AppContext.tpinit(tpinfo); // connect to Tuxedo domain
tuxContext.tpterm(); // disconnect from Tuxedo domain
Utils.tuxputenv("WSNADDR=//10.2.0.5:1001"); //Specifies the network address of the workstation listener process through which clients gain access to the application.

TRIVIA : At initialisation time, TUXEDO is identified by the client program through the operating sytems environmental variables. The function Bea.Tuxedo.ATMI.Utils.tuxputenv(String) adds an environment variable to the system in real time and works across the different platforms on which TUXEDO is supported, including those platforms that don't normally have environment variables. This provides a portable alternative to setting the environment variable yourself. A second function, Bea.Tuxedo.ATMI.Utils.tuxreadenv(String, String)performs the same function but reads environmental variable directly from a file which may be easier to maintain.

In order to disconnect from Tuxedo services, use

tuxContext.tpterm(); // disconnect from the first Tuxedo domain

6. Creating and populating Tuxedo buffers

A buffer is a essentially a data container. Using buffers, information can be transmitted over any network, on any operating system and with any protocol supported by the BEA Tuxedo system. Tuxedo takes care of the encoding and decoding of message transfers between different machine types

There are five types of buffers, STRING, VIEW, CARRAY, FML, and XML. We will work specifically with FML.

FML itself stands for Field Manipulation Language and these buffers contain a tag/value pair storage structure called fields. FML also provides a set of functions to define, access and manipulate fielded buffers.

To get started with FML, create a TypedFML object:

TypedFML fmlBuff = new TypedFML32(<Integer (Buffer Size));

When a buffer contains no metadata (that is, no information about itself), then it is known as an untyped buffer. When a buffer includes metadata such as information that can be stored in it (for example, a type and subtype, or string names that characterize a buffer), then it is a typed buffer.

7. Field identifiers

Values are transmitted to the server using field identifiers or FLDIDs. They are composed of a unique number and are defined and maintained on the server.

When you connect to a Tuxedo service, you are calling server-side C code defined for that particular service. The processes defined in the service use the values provided by the buffer to perform its operation. It knows which values correspond to its required parameters by matching its FLDID which is just an arbitrary number.

In your application's code, you will normally use mnemonic identifiers (constants) to represent fields for easier recognition such as:

public readonly static FLDID VERSION = 33554542;

In this case, imagine VERSION representing a parameter required by a service to load a particular version of the application.

To create a FLDID representing VERSION, initialise a FLDID object:

FLDID fldFieldid = new FLDID(VERSION);

To associate information with the field, use the FML function, Fadd() on the buffer object created in section 6.

fmlBuff.Fadd(fldFieldid, "2.2");

Value is the data you want to associate to the field id of type Object should be a FLDID compatible type, (primitive data types). In this case, I've passed a string, "2.2")

You can associate multiple values with any one buffer.

8. Communicating with Tuxedo

AppContext.tpcall() is the request/response approach of four communication paradigms offered by Tuxedo. The other three are “Conversational”, “Queuing”, “Publish and Subscribe (Pub-sub)”. See here for more information.

Here we will use tpcall().

tpcall(<String (ServiceName)>, fmlBufIn, fmlBufOut, Flags);

The first parameter is a string referring to the name of the required service required by your application. Following on from our current theme of choosing the application's version, you could visualise this parameter as, "SELECT_SERVICE".

The fmlBufIn buffer contains the necessary fielded values, as defined in section 7 required by the code of your chosen service. We have renamed this fmlBuffIn to distinguish it from fmlBuffOut which is a second empty buffer required by the function which will contain the server’s response. Again the servers response will be data mapped mapped to FLDIDs.

9. Transactions

Tuxedo allows you to perform transactions meaning the updating or inserting data into multiple resources typically to a server-side database or file system.

In order to do this, we firstly create a Transaction object for our application.

Transaction transaction = New Transaction;

We then use its function, tpbegin() to initiate the transaction. This function has two parameters, your AppContext object from section 5 and a time-out value accepting a value of type long defining the maximum number of seconds allowed by the system before timing out.

transaction.tpbegin(AppContext, timeout);

You will then transfer the required data for the transaction to an in buffer using field identifiers.

Imagine us needing to update apple stock values.

FML32 fmlBufIn = New FML32(1024);
FML32 fmlBufOut = New FML32(1024); //Buffer Out required for tpcall()
fmlIn.field(ID) = 1
fmlIn.field(STOCKVALUE) = “Apples” 
fmlIn.field(QUANTITY) = 200

We then call the required service with tpcall().

tpcall(“Update_Stock”, fmlBufIn, fmlBufOut, 0, 0)

If the call is successful and returns with no errors, use tpcommit() on our transaction object.

transaction.tpbegin(AppContext, timeout)

This function terminates the transaction. When tpcommit() returns successfully, all changes to resources as a result of the current transaction become permanent.

10. Retrieving records from the server

In order to retrieve records from the server, populate your buffers for the designated service and call it with tpcall().

Use foccur(<FieldId>) on the the out buffer. Foccur() returns the number of values associated with the field id in the buffer. Multiple values can be associated with any one FieldId.

Int occurences = fmlBufOut.Foccur(STOCK);

If (oc > 1)  { //Check to see if the occurrences are greater than 1.
    Object value;
    FLDID fldFieldid = New FLDID(STOCK);
    object[] objArray = new object[oc - 1 + 1]; //Create an object array of length of occurrences
    int i;
    for (i = 0; i <= oc; i++) //Loop through number of occurrences
    {
      // Retrieve each occurrence and add it to objArray
      fmlBufOut.Fget(fldFieldid, i, obj); // ‘retrieves value under designated field id
      objArray(i) = obj;
    }
    value = objArray; // Transfer array to value
   //'If there is only one occurrence, don’t loop
    Else if (oc = 1) {
       fmlBufOut.Fget(fldFieldid, 0, value)
     }           
}

value will contain the record or records associated with the particular field id. The code above can handle both singular and multiple values.

The next step is to cast value into the expected data type. I suggest producing a class of functions to handle the casting of different data types.

For example, the function below could handle all retrieval of type string records for your application. Note, we have created a ReturnValues() function, this function could contain the same code directly above accepting a buffer and field id as parameters in order.

public string GetStringTuxedo(ref FML32 fmlBuf, FLDID fieldId)
{
    int occurences;
    string string;
    // Find occurrences 
    occurences = fmlBuf.Foccur(strChamp);
    // If no occurences, return nothing
    if (occurences == 0)
        return null;
    else if (occurences == 1) // If one occurrence, return string
    {
        string = (string)ReturnValues(fmlBuf, strChamp);
        return string;
    }
}

Similar functions could handle the conversations for the data types required by your application.

11. Tips

11.1 Wrap Tuxedo into an Adapter class

data/admin/2019/6/images.jpg

I suggest enclosing your Tuxedo logic into a separate adapter project or collection of wrapper classes depending on the requirements of your project.

This allows all associated projects in your solution to plug into the single location nicely encapsulating your Tuxedo functionality and hiding the Tuxedo api calls behind your own.

TRIVIA: Wrapper classes or adapter projects are often useful when very often you do not really use all methods declared by an API or interface, so implementing the required functionality explicitly is very verbose.

It's considered good practice when a particular API's "view of the world" is not compatible with the philosophy and architecture of the system currently being developed providing an additional level of indirection to rearrange expected parameters.

It’s also useful when we want to reuse legacy code in our application that we can't modify by wrapping it in our own code.

A typical Adapter project may include the following classes:

![data/admin/2019/6/TuxedoAdapter (1).jpg](/data/admin/2019/6/TuxedoAdapter (1).jpg)

  1. TypedBuffer Class
    • This class wraps around the `TypedFML32` object and 
      
public TypedBuffer(long size)
{
    try
    {
        tBuffer = new TypedFML32(size);
    }
    catch (Exception ex)
    {
        // handle exception
        MessageBox.Show(ex.ToString());  
    }
}

As well as a field property to set and get fields, similar to what we saw in section but implemented as a property of the Typedbuffer wrapper. Slick!

public object field {
    get {
        If (oc > 1) {
            Object value;
            FLDID flieldId = New FLDID (<fieldid>);
            object[] objArray = new object[oc - 1 + 1];
            int oc = CInt (Me.fmlBuffer.Foccur(fieldid));
            int i;
            for (i = 0; i <= oc; i++) {
                fmlBufOut.Fget(fldFieldid, i, obj);
                objArray[i] = obj;
            }
            value = objArray;
            Else {
                if (oc = 1) {
                    fmlBufOut.Fget(fldFieldid, 0, value)
                }
            }
        }
    }
    set {
        FLDID flieldId = new FLDID (<fieldid>);
        this.fmlBuffer.Fchgs (fldFieldid, 0, value);
    }
}
  1. Transaction Class
  • Should contain your AppContext, TypedTPINIT and Transaction objects and functions to initialise the Tuxedo context and make calls on those objects.
  1. ExceptionClass

    • Handle Tuxedo errors here in order to print to a local log or display in your application
  2. FieldIds

    • You may include all of your Field id constants within a single class if not externally loaded from outside of your application