Appendix D Creating C Components
After you generate method skeletons, prototypes, and implementation templates, write the code for each method in the method implementation file. You can include C or C++ functions in C components. Jaguar provides C routines for common C component tasks (see Chapter 5, "C Routines Reference" in the Jaguar CTS API Reference).
To adapt a C++ class for use as a C component, you must write C wrappers. For details, see "C components that are wrappers for C++ classes".
Jaguar Manager creates template files for each method when you define the method signatures (or method prototypes) and generate skeleton routines. You can modify the template files to implement the method bodies, or you can code your methods from scratch according to the rules laid out here.
Function overloading is not supported for C components.
You can also include Jaguar routines to:
Each method in the Jaguar component definition is implemented by a C function with the same name as the method.
Method implementation functions must return CS_RETCODE. Your implementation function can return the following values:
In general, you should return CS_SUCCEED unless a fatal error occurs. Returning CS_FAIL prevents the client stub from receiving output parameter values. Use an inout or output parameter if you need to communicate method status information to the client application.
The Jaguar server calls methods using a specific C calling convention. Follow these rules to ensure compatibility with the Jaguar method calling convention:
"Datatypes for C method implementation functions" shows the datatypes displayed in Jaguar Manager, the datatypes used by C components, and the argument modes. The left column contains the datatype name as it displays in Jaguar Manager. The second and third columns contain the names of the corresponding C datatypes for input, inout, and output parameters.
If the Jaguar Manager method definition returns a value other than ResultSet or ResultSets, an additional output parameter is added to the front of the implementation function's parameter list. This additional parameter receives the "logical return code" for the method invocation, as described in "Logical method return values".
Argument modes specify how an argument is passed. Arguments can have one of these modes:
All parameters specified as input are passed by value except for those parameters declared as string, string<255>, or binary in Jaguar Manager. Except for binary and string parameters, Jaguar always preallocates sufficient space for inout, output, or return parameters. binary and string parameters are mapped to special datatypes, and you may need to reallocate space for the output value as described below.
string<255> parameters are passed as a CS_CHAR *. On input, the Jaguar server null-terminates CS_CHAR parameter values using the length meta-information associated with the datatype. On output, updated CS_CHAR parameter values must be null-terminated.
string<255> parameter values cannot be longer than 255 bytes. Use string parameters if your application requires larger values.
string parameters are passed in a CS_STRING_HOLDER structure. binary parameters are passed in a CS_BINARY_HOLDER structure. These structures are defined in jagpublic.h as follows:
typedef struct _cs_longchar_holder
{
CS_LONGCHAR *value;
CS_INT length;
} CS_STRING_HOLDER;
typedef struct _cs_longbinary_holder
{
CS_LONGBINARY *value;
CS_INT length;
} CS_BINARY_HOLDER;
#define CS_LONGCHAR_HOLDER CS_STRING_HOLDER
#define CS_LONGBINARY_HOLDER CS_BINARY_HOLDER
To allow backward compatibility with code that was written for Jaguar version 1.1, you can use CS_LONGCHAR_HOLDER in place of CS_STRING_HOLDER, and CS_LONGBINARY_HOLDER in place of CS_BINARY_HOLDER.
On input, the value field contains the input value and the length field specifies the input length. For output, you can set a new value and length in the structure as follows:
The following example calls the JagFree and JagAlloc routines to reallocate a larger value buffer:
JagFree(myholder->value);
myholder->value = JagAlloc(new_length);
if (myholder->value == NULL)
{
JagLog(JAG_TRUE, "Out of memory!\n");
return CS_FAIL;
}
memcpy(myholder->value, new_value, new_length);
myholder->length = new_length;
NULLs cannot be passed to or returned by method calls. Instead of using NULL for string parameters, pass zero-length values.
If the Jaguar Manager method definition returns a value other than ResultSet or ResultSets, the C function signature contains an additional parameter in the first position. This parameter functions as a logical return value for method invocations. When the C function returns, the output value of this parameter is forwarded to the client, and the client receives it as the return value for the stub method invocation. Datatype mappings for this added parameter are the same as for an output parameter.
If the Jaguar Manager method definition returns ResultSet or ResultSets, you must use the C Result Set API calls to build the result set or sets to be sent to the client, as described in "Methods that return row results".
In most cases, the implementation of C component methods require no special coding. Simply add code to the method body that contains the application logic to respond to the input parameter values and assign the correct return values to inout, output, and return parameters.
The exceptions to this rule are:
C components do not contain private data. To allocate separate data for instances of the same component, use the JagSetInstanceData and JagGetInstanceData routines.
The Jaguar server provides two functions for managing instance-specific data:
Chapter 5, "C Routines Reference" in the Jaguar CTS API Reference contains reference pages for these routines.
Since methods in a C component must be implemented as C functions, you must code C wrappers for C++ classes.
Beginning in version 2.0, Jaguar provides direct support for running C++ classes as components, as described in Chapter 16, "Creating CORBA C++ Components" Sybase supports the technique described here, but recommends that you create a C++ component to run your C++ classes directly.
The procedure for creating a wrapper for a C++ class is as follows:
CS_RETCODE CS_PUBLIC create() {
StockTrade *st_ref;
/*
** Create an instance of the C++
** StockTrade object.
*/
st_ref = new StockTrade();
/*
** Associate it with the Jaguar component
** instance.
*/
if (JagSetInstanceData((CS_VOID *)st_ref)
!= CS_SUCCEED)
{
return CS_FAIL;
}
return CS_SUCCEED;
}
CS_RETCODE CS_PUBLIC buyStock (
CS_CHAR *ticker,
CS_INT n_desired,
CS_INT n_bought)
{
StockTrade *st_ref;
if (JagGetInstanceData((CS_VOID *)&st_ref)
!= CS_SUCCEED)
{
return CS_FAIL;
}
st_ref::buyStock(ticker, n_desired,
n_bought);
return;
}
CS_RETCODE CS_PUBLIC destroy() {
StockTrade *st_ref;
if (JagGetInstanceData((CS_VOID *)&st_ref)
!= CS_SUCCEED)
{
return CS_FAIL;
}
delete st_ref;
return CS_SUCCEED;
}
A Jaguar server uses a connection cache to maintain a pool of connections from the Jaguar server to database servers. A component can connect to a database server using an existing connection in a connection cache without creating a new connection.
Jaguar's transactional model works only with connections obtained from the Jaguar Connection Manager. Connections that you open yourself will not be affected by Jaguar transactions.
For more information about coding connection management routines into components, see Chapter 28, "Using Connection Management".
To return row results, call the result-set routines listed in Chapter 5, "C Routines Reference" in the Jaguar CTS API Reference. "Sending result sets from a C or C++ component" explains the call sequence and contains examples.
Components in the same package can share data--that is, variable values. For example, a counter that tracks how many objects have been created for a single component could be used as a shared variable. Shared variables are organized into collections. These variables are referred to as shared because components in the same package can read and update the same data. A collection can contain any number of shared variables. Shared variables can be identified by name or by index number. Shared variables are initialized as null and are not saved when the server is shut down.
Because it is important to maintain the integrity of the shared data in shared variables, a single read or update operation on a shared variable is atomic. Atomic means that an operation on data will complete before any other operations can access that data. Multiple reads and updates on any number of shared variables in a single collection can be synchronized by locking that collection.
You cannot use shared variables in components that are configured for automatic failover, because these components cannot use local shared resources. See "Transactions tab component properties" for more information. If you need to share data, you can store shared data in a remote database. See "Thread-safety features" for more information.
To share data between components, you must include the jagpublic.h file in the C source file.
The general procedure for sharing data is:
You can also list all collection names on the server by calling the JagGetCollectionList routine. For more information see "List all collections".
The component must create the collection before it can create shared variables.
To create a new collection, call the JagNewCollection routine. This routine:
Lock level must be set to one of the following:
JAG_LOCKCOLLECTION - allows locks to be set on collections
JAG_LOCKDATA - does not allow locks to be set on collections
To create a new shared variable, call the JagNewSharedData or JagNewSharedDataByIndex routine. These routines create a shared variable value initialized to NULL. JagNewSharedData creates a new shared variable or returns a reference to an existing shared variable by name. JagNewSharedDataByIndex creates a new shared variable or returns a reference to an existing shared variable by index number. A shared variable created by index can only be retrieved or updated by index. Similarly, a shared variable created by name can only be retrieved or updated by name. Since a reference is returned, you do not need to follow these routines with the JagGetSharedData or JagGetSharedDataByIndex routine.
For both routines, *pExists is set to:
Locking a collection is strictly advisory. Use JagLockCollection and JagLockNoWaitCollection routines to lock collections. Even though a collection is locked, the JagGetSharedValue and JagSetSharedValue routines can still read and update the shared variables in the collection. To ensure that multiple read or update operations on any shared variable in a collection are atomic, lock the collection before executing read or update operations on the shared variables in the collection.
Call the JagGetLockLevel routine to determine a collection's isolation mode. If the collection's isolation mode is JAG_LOCKCOLLECTION, then the component object can lock the collection. Otherwise, the lock will be rejected.
If you call the JagLockCollection routine to lock a collection that is locked by another component, JagLockCollection waits until the collection is unlocked by the other component, then locks the collection. If the lock is successful, JAG_SUCCEED is returned. If the collection has already been locked by the calling object, this routine does not lock the collection again and JAG_SUCCEED is returned.
The JagLockNoWaitCollection routine does not wait until a locked collection is unlocked; the JagLockNoWaitCollection routine immediately returns execution to the calling routine. This routine returns JAG_SUCCEED and sets *pLocked to JAG_TRUE if the collection was not locked or if the collection is already locked by the same calling object. If the collection was locked by another component object, JAG_SUCCEED is still returned but *pLocked is set to JAG_FALSE.
The JagLockCollection and JagLockNoWaitCollection routines return JAG_FAIL if an error, such as the collection's isolation mode is JAG_LOCKDATA, occurs.
Call the JagUnlockCollection routine to release a lock on a collection. A locked collection is automatically released when the component object's method execution is completed. However, to make your application more efficient and prevent deadlocks, unlock a collection when the component object is finished updating or reading the shared variable in the collection so that other component objects can access the collection right away.
Before reading or updating the shared variable, the component object must retrieve references to the collection and shared variable.
The JagGetCollection routine returns a reference to the specified collection. Once the component instance has retrieved a reference, the component object can lock and unlock the collection, create a new shared variable in the collection, or retrieve a reference to an existing shared variable.
If the collection exists, JAG_SUCCEED is returned and **ppCollection is set to the collection reference. If the collection does not exist, JAG_FAIL is returned and **ppCollection is set to NULL.
The JagGetSharedData and JagGetSharedDataByIndex routines return a reference to the specified shared variable. The component object must have already retrieved the collection reference before calling these routines. When calling the JagGetSharedData routine, you specify the shared variable by name. When calling the JagGetSharedDataByIndex routine, you specify the shared variable by index number.
For both routines, if the shared variable exists, JAG_SUCCEED is returned and **ppData is set to the shared variable reference. If the shared variable does not exist, JAG_FAIL is returned and **ppData is set to NULL for both routines.
The JagGetSharedValue routine retrieves the value for a specified shared variable and places the value in a buffer. The component object must have retrieved the shared variable reference before executing this routine. The component object must create a buffer in which to copy a value. The buffer must be large enough to hold any value that can be stored in the shared variable. You must specify the buffer (and its size) in which the value is to be copied. The buffer must be large enough to contain the value. If the value is too large for the buffer, JAG_FAIL and the size of the value are returned.
If the value is successfully copied into the buffer, JAG_SUCCEED and the number of bytes copied to the buffer are returned. If *outlen is 0, then there was no value to copy.
The JagSetSharedValue routine copies a value to a specified shared variable. The component object must have retrieved the shared variable reference before calling this routine. The component object must pass a pointer to the value you want the component object to copy to the shared variable. This routine copies the value to the shared variable. You must specify the size of the value. If the value is a null-terminated string, you must include the length of the null terminator in the length of the string.
Jaguar maintains the values of shared data in its own memory space. When JagSetSharedValue() copies the data, it does not copy the pointer to the data. Similarly, JagGetSharedValue() copies the data into a buffer supplied by the caller, it does not place a pointer to the data in the user's buffer.
If the new value is copied to the shared value, JAG_SUCCEED is returned. If an error occurs, JAG_FAIL is returned.
After a method finishes all operations on a collection, release the reference and all shared variable references. This helps to prevent memory leaks. Releasing collection and shared variable references does not release the shared variable values.
First, release shared variable references and then release the collection reference. To release shared variable references, call the JagFreeSharedDataHandle routine, passing the shared variable reference as input. To release collection references, call the JagFreeCollectionHandle routine on the collection reference.
If the shared variable or collection reference is released, JAG_SUCCEED is returned. If an error occurs, JAG_FAIL is returned.
Call the JagGetCollectionList routine to retrieve a list of all the collection names on the server. The server returns a JagNameList structure. This routine can be called in conjunction with administering the Jaguar server. Call the JagFreeCollectionList routine to free the memory allocated for the JagNameList structure.
The JagGetCollectionList routine returns a reference to a JagNameList structure that includes all the collection names defined on the Jaguar server. The JagNameList structure is:
typedef struct _jagnamelist
{
SQLINT num_names;
SQLPOINTER *names;
} JagNameList;
where:
num_names is the number of array elements.
*names is an array of num_names elements; each element points to a null-terminated collection name.
Methods in a transactional component should call one of the transaction state primitive routines listed in Chapter 5, "C Routines Reference" of the Jaguar CTS API Reference.
Even if your component is not transactional, you should call one of these methods to explicitly specify whether the instance should be deactivated.
For transactional components, choose the routine that reflects the state of the work that the component is contributing to the transaction, as follows:
For nontransactional components, call either JagCompleteWork or JagRollbackWork to deactivate and destroy the component instance. To keep the instance active, call JagContinueWork or JagDisallowCommit.
If a method does not explicitly set transaction state before returning, the default behavior is JagContinueWork.
To customize what happens when a component instance is created or destroyed, write customized code into the create (create.c.new) and destroy (destroy.c.new) routine templates that are generated by Jaguar Manager. create and destroy are typically used to manage instance-specific data that the component requires. For example, some methods might need to be executed in a certain sequence. You can customize the create and destroy routines to keep track of which methods have been executed. For details on managing instance-specific data, see "Components that require instance specific data".
The create and destroy routines are optional. You can also implement create and destroy in another source file and ignore the generated templates. The create and destroy routines cannot have parameters and cannot return result sets.
The Jaguar server calls create when creating a new instance of the component. The signature for create is:
CS_RETCODE CS_PUBLIC create()
create must return CS_SUCCEED.
The Jaguar server calls destroy when destroying an instance of the component. The signature for destroy is:
CS_RETCODE CS_PUBLIC destroy()
destroy must return CS_SUCCEED.
As a general rule, code C component methods to handle unrecoverable errors as follows:
Chapter 5, "C Routines Reference" of the Jaguar CTS API Reference contains reference pages for these routines.
Copyright © 2000 Sybase, Inc. All rights reserved. |