JetNet/Oracle Tuxedo Guide |
In JetNet and Oracle Tuxedo applications, services are subroutines that do the work required for an application to access a resource manager, usually a database. They are invoked by service requests made by clients or other services.
To expedite responses to service requests, the application can run multiple instantiations of a server. Service requests are routed to servers in the way that provides the fastest response.
This chapter covers:
Services |
A service can consist of three parts:
The latter method is convenient if you are creating services that do not access a database. You might also choose to create the service components manually if you converted an application using the Service routines are responsible for optionally receiving data from the client, performing some task, and optionally returning data to the client. A service routine can perform any task, such as building a query for accessing a database.
Service routines are built for you when you use the screen wizard to create the server portion of your application—that is, the service component. These services perform the database transactions required by your application. For more information on creating services with the screen wizard, refer to page 4-19.
You can also write service routines. These can be written in the same way you write any other Panther application code. JPL is most convenient, but you can code a service as a C or Java function if that suits your application needs.
The JPL service code can reside in a library that is accessible to the server, or it can be implemented as screen-level JPL (in the JPL Procedures property) on a service component (described in the Service Component section). The routine cannot be attached at widget-level since the routine must be recognized outside the scope of the widget. At runtime, the service code procedure is sought first at screen-level (on the service component), then in public modules, and finally in open libraries on the server.
For information on how to write a service routine, refer to page 5-9.
A service component is a graphical service. It's a Panther screen that should look, for the most part, like the client screen it is servicing. However, service components reside on the server (in a server library such as In brief, the client makes a service request and passes information to the service. The service accesses its corresponding service component which receives the information and performs any runtime processing, including accessing the database via the transaction manager or the database interface. In this way, the service component can carry out a variety of database-related services, such as finding, inserting, deleting and updating records.
The easiest way to create service components is by using the screen wizard. You can create them at the same time you create the client screen. When service components are created in this way, the service routines are automatically provided, so you don't have to write the routines at all. In addition, service components can hold the routines for more than one service, and multiple services can use the same service routine or the same service component.
Service components can be created and edited in the screen editor, just like any Panther screen. Refer to page 12-1 in the Application Development Guide for further information on creating service components.
When a service call is made, or a server is told to advertise services, the application server obtains information about the services by consulting the JIF, which at runtime resides on the application server in A JIF service definition requires the following information:
clnt2svr
utility and special handling is required, for example, if a client screen implements partial commands via the transaction manager. For more information on clnt2svr
, refer to page A-2.
Service Routine
Service Component
server.lib
) and so are not visible to the user at runtime. The service component should contain the same widgets as the client screen so that it can handle the data that flows between the client screen and the service.
JIF Service Definition
common.lib
.
You can create and modify the JIF in the JIF editor. For information on using the JIF editor, refer to Chapter 24, "JIF Editor," in Using the Editors.
You can optionally specify several other service attributes in the JIF:
Optional Service Attributes
For further information on these service features, refer to page 24-5 in the Using the Editors.
Creating Graphical Services |
Services can be represented as a screen, called a service component, that resides on the server. You can create client screens and associated service components with the screen wizard or you can build service components by dragging database objects from a repository to screens using the screen editor.
Using the screen wizard makes service creation practically effortless. To facilitate client and server development, the wizard lets you build the client screen and service component at the same time.
The JPL code that implements the transactions via service calls and the transaction manager are provided for you, and made public via the service component's JPL Procedures property. Unless specialized tasks are necessary for your application, you do not need to write any service code at all.
Database transaction services are named by the screen wizard and implemented via the Service properties on the master table view of the client screen: Delete Service, Insert Service, Select Service, and Update Service.
While client screens and service components in the screen wizard are created as pairs, that is, one service component for each client screen, you can also develop service components that can service multiple client screens. This can be done by creating a service component for each table view that client screens might use. In this way, a service component can service multiple client screens, and in turn, client screens might make service requests to more than one service component. If a client screen must access multiple service components, Service properties must be defined for all table views on the screen. In other words, the master table view on the client screen will call services specific to the master section of the screen, and the detail and subdetail sections will call services specific to their own table view. Therefore, the Service properties for each table view on the screen must be identified.
In general, it is best to create both client screen and corresponding service component at the same time. Save client screens in the client library, and save service components in the server library. Even if you don't plan on using the resulting client screen for your application interface, you can use it to test its associated service component; consider saving such client screens in a test library.
Once services are created in the screen wizard, you must define them in the JIF. When you invoke the JIF editor:
Once services are added to the JIF, all servers that are currently running are made aware of changes to the JIF. The new services are immediately available for the application to advertise. Refer to page 5-14 for more information.
For more information on using the screen wizard, refer to Chapter 4, "Screen Wizard," in Using the Editors.
To create a service component using the screen editor, you must include and identify all the components necessary to implement the service. In addition to building the screen, you must code the service routines and set the appropriate property values on the client screens that will use the service component.
For screens that use the transaction manager, both client screen and service component must contain the same database information—the same table views, the same columns, and so on. To create a service component from scratch with the screen editor:
Building Services with the Screen Editor
For the most part, service components must have the same contents and property values as the client screens that use them. Therefore, changes you make on a client screen must also be made to its corresponding service component.
To modify a service component, open it in the screen editor and make the appropriate edits. Some typical modifications might include adding a transaction manager hook function as screen-level JPL or defining the Use In Where property of a widget.
Initiating a Service |
To initiate a service, a client screen must have a way to make a service call. A service call can be implemented as JPL code (using the service_call
command) attached to a widget on the client screen, or by naming the service in the Service properties of a client screen's master table view. Services identified as Service properties are handled by the JetNet transaction model jetrb1
on screens that use the transaction manager.
For more information on the JetNet transaction model, refer to page 35-12.
Using Service Aliases to Test Services |
Once the service has been added to the JIF and the service component and service code are available on the application server, you can test its behavior. To allow several developers to work on the same application, each developer has the ability to test their own version of a service using service aliases.
With service aliases, a developer's name can be appended to the service name. The original service name is available for all other users of the application; the developer is able to change functionality without breaking the application for other users.
To use service aliases:
The JIF automatically assigns a unique name to each service when the service is added to the JIF. This unique service name, in combination with the developer's name, provide the service alias.
To discontinue service aliases:
Writing Service Routines |
A service routine is responsible for:
Table 5-1 lists the basic functions that a service routine should perform and includes the JPL, library function, and SQL that can be used to implement them.
The following procedure is an example of a JPL service routine that receives the account id and amount from the client and uses that information, by way of the transaction manager, to perform a bank account withdrawal.
The following service routine uses data it receives from the client to apply business logic. Storing business logic on the application server can facilitate application maintenance since you only need to update the service routine in order to effect new business practices. In this example, the service routine uses the customer's id number and the gross amount of an order to determine at what amount a percentage of discount should be applied.
You can write JPL service routines directly in the screen editor. The service and your application's requirements, for development and production, will determine where it makes the most sense to store and invoke service code:
proc withdraw()
vars message
// service WITHDRAWAL
receive arguments ({account_id, amount})
call sm_tm_command("SELECT")
if (account_id->num_occurrences <= 0)
{
service_return failure ({message = "Invalid account."})
}
// check if the amount to be withdrawn is more
// than the withdrawal limit
if (amount > max_withdrawal)
{
message = \
"Withdrawal limit is " ## max_withdrawal
service_return failure ({message})
}
account_balance = account_balance - amount
if (account_balance < 0)
{
message = \
"Account overdraft attempt on account " ## account_id
log message
service_return failure ({message})
}
call sm_tm_command("SAVE")
service_return ({message = @NULL, balance = account_balance})
}// Use gross_amt to determine level of discount for
// a customer. Apply the discount and return net_cost
proc discount()
receive arguments ({customer_id, gross_amt})
call sm_tm_command("VIEW")
if (customer_id->num_occurrences <= 0)
{
service_return failure ({message = "Invalid customer id"})
}
if (gross_cost > 1000)
{
net_cost = gross_cost * (1 - discount)
}
else if (gross_amt > 100)
{
net_cost = gross_amt * (1 - discount / 2)
}
else
net_cost = gross_amt
service_return success ({net_cost}) Storing and Invoking JPL Service Code
A service routine that resides directly on the service component is immediately available when the client makes the service request. Service components are opened or made available either when their associated service is advertised by the server, or when the service is requested.
Panther's built-in development and production The built-in Changes you make to the service code are immediately available to your application without having to restart servers or recompile.
If you have service routines that are not associated with a service component, there are two ways you can make these routines available:
Service Code and Service Components
pre_service
handlers make the necessary service component available, and therefore, the routine is made available.
post_service
handlers close or deselect the service component after the service completes.
JIF-Invoked Services
For development purposes, store a JPL service routine in a module—one service routine per module—and do not include the In this way, you can easily access, update, and retest code without affecting the server.
Once you are ready to deploy your application, edit the JPL module and add a If the service routine does not reside directly with a service component, the JPL procedures can be made public either from the service component's entry function, or on server initialization.
When JPL is made public, it is available to the entire application until the server is brought down, or until the module is unloaded. For development purposes, making modules public on server initialization is the least flexible, because the server offering the services must be brought down and reinitialized in order to retest service code that has been modified.
During the development cycle, consider unloading public modules from the service component's exit function. In this way you can update the code, if necessary, and retest it without affecting the server that advertises the service.
proc
statement. When the service is requested, the JIF is consulted to determine the name of the service's routine—in this case, the procedure and module name would be the same. At runtime, if a procedure by the given name is not found, the service seeks a module having the specified name, and executes its unnamed procedure (refer to page 19-2 in the Application Development Guide for information on the unnamed procedure in a JPL module).
proc
statement to identify the procedure (same as the module name). You can then public
these JPL service routines when the server is initialized.
Public Services
Service Groups |
Once you determine what services are required by your application, you can create service groups. You can group services for your application if you have just one application server or multiple servers. Service groups are useful because:
You define service groups in the JIF. Refer to page 24-12 in the Using the Editors for information on using the JIF editor.
To determine what services should be grouped, consider establishing a logical coherence within the application's tasks, such as:
Criteria for Grouping Services
In general, it is easier and more efficient for servers to advertise groups of services. For instance, if a server's initialization routine specifies that all services should be advertised, all is interpreted as all services defined in the JIF (refer to page 3-23 for JetNet or to page 8-17 for Oracle Tuxedo).
If your application has multiple (unique) servers, each server should have at least one service group that contains all of the services that the server handles. Additional servers might be necessary if some services use an XA-compliant database interface, while others do not, or if your application accesses more than one database.
During development, you can easily add or update services associated with a service group. When a service is added or updated in the JIF and it is saved to the application's common library, all running servers are notified to reread the JIF. Service groups that are advertised at server initialization are readvertised, and the new or updated service is immediately available to your application.
For details on how to define service groups in the JIF, refer to page 24-12 in the Using the Editors. For details on advertising a service group when starting your servers, refer to page 3-23.
Adding Services to Existing Service Groups
Service Messages and Data Types |
In JetNet and Oracle Tuxedo applications, clients and servers exchange data through messages, and the data type of those messages must be specified. For example, a service call can have 0 to 2 messages, depending on how the service is defined, for request data supplied to the server, and reply data expected by the client when the service returns.
Messages can be composed of simple strings; or they can contain combinations of numeric and string data.
In JetNet applications, messages are in JAMFLEX
data buffers.
In Oracle Tuxedo applications, messages can be data buffers using the JAMFLEX
, FML
, or FML32
data types, or messages can be strings using the STRING
data type.
For example, when a client calls a bank deposit service, it supplies a message to the service that is composed of several data fields, such as the bank account number and deposit amount. The client also expects the service to return with a reply message whose data includes the bank balance. So, the service call specifies two messages, one for input data and another for reply data, where each message can itself contain multiple data fields:
service_call "DEPOSIT"( \
{account_id, amt}, \
{errMsg, acctBal} )
In this example, the service request message consists of the account number and deposit amount; on return, the service can supply the client with an error message in case the service fails, and the updated account balance. Each message contains two data fields.
The previous example contains two JAMFLEX
messages. A service call can also specify messages of different types. For example the following call to service OPEN_ACCT
specifies a STRING
message type for input data and a JAMFLEX
buffer for reply data:
service_call "OPEN_ACCT"("Fred Jones", {acct_num, start_bal})
The following sections discuss service message types.
Service messages can be defined as buffers that can contain multiple components of data of different types. In JetNet applications, messages are in JAMFLEX
data buffers, which can contain string, integer, and floating point data. In Oracle Tuxedo applications, messages can be data buffers using the JAMFLEX
, FML
, or FML32
data types.
All buffer types are represented in JPL as a comma-delimited list of fields in this format:
{[field [, field] ] }
The buffer can contain 0 or more fields, where each buffer field has this format:
fieldname [= prolfx-expr]
fieldname
is the name of a field in the buffer, and prolfx-expr
can be a Panther variable or constant. If you omit prolfx-expr
, Panther assumes that fieldname
has the same name as a Panther variable or widget and maps its data accordingly.
Note:
If the buffer type is FML
or FML32
, fieldname must correspond to the name of a field defined in the FML file. Refer to page 5-17 for more information.
For example, a message can be specified as follows if the Panther variables and the corresponding buffer field names have the same names:
{EMP_ID, EMP_NAME, EMP_ADDR}
In the next example, the names of the Panther variables and the corresponding buffer field names are different, so explicit mapping is required:
{EMP_ID=id, EMP_NAME=name, EMP_ADDR=addr}
Reply buffer data can be automatically mapped to Panther variables of the same name through ellipses. For example, this service_call
command specifies to map all buffer fields to client variables that have the same name:
service_call "get_emp_age" ({emp_id},{...})
In the next example, the buffer field emp_age
is mapped to Panther variable age
. All other buffer fields are mapped to same-named client variables:
service_call "get_emp_age" ({emp_id}, {emp_age=age, ...})
You can specify the data in a message buffer to be NULL
through the keyword @tpi_null
. For example, this service_return
command returns a NULL
buffer to the service call:
service_return failure ({@tpi_null})
Buffers can reference array occurrences. If a message field specifies the name of an array, Panther references each occurrence in that array. You can also specify ranges of occurrences. In the following example, the first 5 occurrences of emp_id
are sent to the service:
service_call "get_emp_args" ({emp_id[1..5]}, {...})
A Oracle Tuxedo application can support FML
buffers for messages, both FML
and the enhanced FML32
. For complete information on FML
buffers, refer to your Oracle Tuxedo documentation.
You define FML
fields in the FML
file as one of the following data types: char, string, short, long, float, and double. Fields are listed by name, field number, and type. A fourth Flag
field is unused. For example:
#Name Number Type Flag
#---- ------ ---- ----
CUSTNUM 1 string -
CUSTID 2 long -
CUSTADDR 3 string -
CUSTCITY 4 string -
CUSTSTATE 5 string -
CUSTZIP 6 long -
Type conversion from the data type specified by the user to the FML
field type is performed as the data allows. In the following example, if amount
is of type float, and if amount = 3, then 3 is converted to a floating point number:
{amount = 3}
You can convert JAMFLEX
buffers to FML
buffers later in the development cycle:
FML
file.
broadcast
or notify
commands used in the application to specify the appropriate FML
type.
A Oracle Tuxedo application can also support For example, the following JPL procedure calls a service that expects In this example, service In the next example, the input message to the same service refers to a Panther variable:
In this case, the first name passed to the service is the value in the The data types for service messages are set in the JIF as part of service or queue definitions. For each service or queue definition, you can specify the data type for its request and reply messages. You specify a data type for both messages, only one, or for none.
When the request broker processes one of these messages, it checks the JIF for its data type and uses this information in order to pack or unpack the message data. The format of a message must conform with its JIF definition; otherwise, an error occurs.
Several JPL commands send messages that are independent of service definitions:
STRING Data Types
STRING
message types. A STRING
message can send or receive only a single string. A STRING
input message can be any string expression, either a variable or string constant; a STRING
reply message must be a variable.
STRING
messages for input and output:.
proc lastname
vars last_name
service_call "get_last_name" ("Jim", last_name)
msg emsg "Last name is ", last_nameget_last_name
receives the string Jim
as an input message. When the service completes, the return data is put in the output message and mapped to Panther variable last_name
.
service_call "get_last_name" (first_name[i], last_name)
i
th occurrence in array first_name
.
Setting Service Message Types
service_call
messages can contain zero to two comma-delimited messages. Other commands such as dequeue
and service_forward
allow only zero or one message for either input or output.
These commands set their message's data type. For example, this broadcast
command sends a message that informs the specified user how much time has elapsed since she logged in and how much time remains until the login lapses:
broadcast USER userName TYPE JAMFLEX \
({sinceLogin, timeLeft})