Working with shared objects
To allow you to work with shared data in a distributed application, PowerBuilder
provides support for shared objects. Shared
objects are user objects that can be shared by multiple client connections.
Benefits of using shared objects
Shared objects provide significant benefits to both distributed
PowerBuilder applications and Internet applications that use Web.PB.
Shared objects allow you to:
- Provide convenient
access to common data that would otherwise need to be retrieved
separately by each client connection - Reduce the number of database accesses, allowing
the database server to be available for other processing - Maintain state information in Web.PB applications
Using shared objects in nondistributed applications In nondistributed applications, shared objects can also be
used to enable parallel, asynchronous execution of application program
logic. When you register a shared object, PowerBuilder creates a
separate thread for each shared object instance and any objects
that the shared object creates. Therefore, any work performed inside
the shared object will run in a separate thread.
What you can share
You can share any custom class (nonvisual) user object you
create. In addition, you can share many of the built-in, nonvisual
objects that are available with PowerBuilder, as well as standard
class user objects that inherit from these built-in objects. However,
PowerBuilder does not
allow you to share the following
types of objects:
- DataStore
- DynamicDescriptionArea
- DynamicStagingArea
- Transaction
These objects cannot be shared because PowerBuilder expects
system resources associated with these objects to be available locally.
How shared objects work
Server establishes the shared object
To allow multiple client applications to share a single object,
the server application performs these operations:
- Invokes the SharedObjectRegister
function to register a named instance of the object. - Invokes the SharedObjectGet function to get an object
instance that is a reference to the shared object.
The server can perform these operations in its main thread
or inside a client session. The server application does not need
to issue a CREATE statement for the shared object. When the server
calls the SharedObjectRegister function, PowerBuilder automatically
creates the shared object instance.
Client accesses the remote object
Shared objects are accessible only from the server’s
main session and from the client sessions created for each client
connection on the server. Client applications cannot access a shared
object directly. To access a shared object, a client needs to communicate
with a remote object that delegates work to the shared object. Often
the remote object has an instance variable that provides a reference
to the shared object.
Once the server has registered the shared object instance
and retrieved a reference to the object, client applications can
use the services provided by the shared object by interfacing with
the remote object, which in turn passes requests on to the shared
object
. The remote object has methods that
provide indirect access to the methods of the shared object. Typically,
these methods have the same names as the methods defined for the
shared object.
Arguments and return values The data types permitted for the arguments and the return
value of a shared object method are the same as for a remote object
method. For a complete list of valid data types, see “Writing the user object
methods”.
What happens at execution time
Each shared object has its own session
When you register a shared object, PowerBuilder creates a
separate thread (or session) for the shared object instance and
any objects that the shared object creates. The thread for a shared
object is created using the Application object definition for the
server application.
The shared object session has its own copy of the application’s
global variables, but the events for the Application object are
not triggered. Therefore, if you want the shared object to perform
any setup operations (such as initializing variables), you need
to code these operations in the Constructor event of the shared
object because the Open and ConnectionBegin events of the Application
object will not be fired.
Requests are queued
As clients make requests for shared object services, these
requests are sequentially queued to avoid problems that might arise
from concurrent access. This ensures that only one user can modify
the contents of a shared object at a time.
Execution-time errors are passed back to the
caller
If an execution time error occurs while a shared object instance
is being used, the error is passed back to the caller. If a fatal
system error occurs in the server environment, the error is passed
back to the caller as a fatal error.
How shared objects are destroyed
A shared object instance is deleted when any of the following
events takes place:
- The server application
unregisters the object’s named instance and no more references
to the object exist. - The object is explicitly destroyed with a DESTROY
statement. - The server application shuts down.
The shared object instance is not
destroyed
when a reference to the shared object is implicitly destroyed by
the PowerBuilder garbage collector.
Example
Description
This example illustrates the use of a shared object to retrieve
database information. The server application uses a timer to perform
retrieval operations against the database at regular intervals.
The result set is stored in an instance variable of the shared object
so that client applications can get to the data without having to
access the database directly. To get the data, the client application
calls a user object function that returns the data stored in the object’s
instance variable.
Shared object definition on the server
The server application has an object called uo_customers
that operates as a shared object.
Instance variables
The uo_customers object has the following instance
variables:
1 |
datastore ids_datastore |
1 |
uo_timer iuo_mytimer |
The ids_datastore variable keeps track of customer
data retrieved from the database.
Constructor
The Constructor event for the uo_customers object
first connects to the database and creates the DataStore that will
be used for database retrieval. Then the Constructor creates an
instance of the user object uo_timer, which is inherited
from the Timing object, and calls the Start function. The Start function
activates the timer with an interval of 300 seconds so that database retrieval
operations are performed at 5-minute intervals. The last line of
the Constructor passes a reference to the uo_customers
object to the timer object:
1 |
SQLCA.DBMS = "ODBC" |
1 |
SQLCA.AutoCommit = False |
1 |
SQLCA.DBParm = "ConnectString='DSN=EAS Demo DB V3; |
1 |
UID=dba;PWD=sql'" |
1 |
CONNECT USING SQLCA; |
1 |
1 |
if SQLCA.sqlcode = 0 then |
1 |
MessageBox ("Connect to Database Successful", & |
1 |
sqlca.sqlerrtext) |
1 |
end if |
1 |
1 |
ids_datastore = create datastore |
1 |
ids_datastore.dataobject = "d_customer" |
1 |
ids_datastore.SetTransObject (SQLCA) |
1 |
1 |
iuo_mytimer = CREATE uo_timer |
1 |
iuo_mytimer.Start(300) |
1 |
iuo_mytimer.PassObject(this) |
refresh_custlist function
The refresh_custlist function retrieves a list of
customers and returns the number of rows retrieved. The function
has no arguments.
Here is the script:
1 |
long ll_rowcount |
1 |
ll_rowcount = ids_datastore.Retrieve() |
1 |
return ll_rowcount |
retrieve_custlist function
The retrieve_custlist function returns the customer
data in the ids_datastore instance variable. The function
has no arguments.
Here is the script:
1 |
s_customers custdata |
1 |
custdata.customers = ids_datastore.object.data |
1 |
1 |
return custdata |
Destructor
The Destructor event for the uo_customers object
stops the timer and disconnects from the database:
1 |
iuo_mytimer.Stop() |
1 |
DISCONNECT USING SQLCA; |
Timing object definition
The uo_timer object is a standard class user object
that inherits from the Timing object. The object’s Timer
event has a script that retrieves a list of customers from the database
by calling the refresh_custlist function of the shared
object.
Instance variables
The uo_timer object has an instance variable that
provides a reference to the shared object uo_customers:
1 |
uo_customers iuo_shared_object |
PassObject function
The PassObject function caches the shared object reference
in the iuo_shared_object instance variable. The
function takes the argument auo_customers, which is of
type uo_customers. The function returns an integer.
Here is the script:
1 |
iuo_shared_object = auo_customers |
1 |
return 1 |
Timer event
The Timer event calls the refresh_custlist function
by referencing the shared object:
1 |
long rowcount |
1 |
rowcount = iuo_shared_object.refresh_custlist() |
Remote object definitionon the server
The server application has an object called uo_custdata
that provides an interface between the client application and the
shared object on the server.
Instance variables
The uo_custdata object has an instance variable that
provides an object reference to the shared object uo_customers:
1 |
uo_customers iuo_shared_object |
Constructor
The Constructor event for the uo_custdata object
gets a reference to the shared object instance for uo_customers:
1 |
SharedObjectGet("share1",iuo_shared_object) |
retrieve_custlist function
The retrieve_custlist function calls the corresponding
function in the shared object uo_customers. The function
retrieves the structure that contains the list of customers retrieved
from the database. The function has no arguments.
Here is the script:
1 |
s_customers custdata |
1 |
1 |
custdata = iuo_shared_object.retrieve_custlist() |
1 |
1 |
return custdata |
Main window on the server
At startup time, the server application starts the listener
and displays a window that provides buttons for registering and
unregistering a shared object.
Register button
The Register button registers uo_customers as a shared
object with the name share1:
1 |
SharedObjectRegister("uo_customers","share1") |
Unregister button
The Unregister button unregisters share1:
1 |
SharedObjectUnRegister("share1") |
Client application window
The client application has a window that contains a DataWindow
control that allows the user to display customer data. When the
user clicks the Retrieve button, the client connects to the server
application to get the data. Once the data is retrieved, it is displayed
in the DataWindow control in the client window.
Retrieve button
The Retrieve button in the client window invokes the retrieve_custlist
function by referencing the uo_custdata object:
1 |
s_customers custdata |
1 |
1 |
connection myconnect |
1 |
myconnect = create connection |
1 |
myconnect.driver = "Winsock" |
1 |
myconnect.application = "pbserver" |
1 |
myconnect.location = "server01" |
1 |
myconnect.ConnectToServer() |
1 |
1 |
uo_custdata iuo_custdata |
1 |
1 |
myconnect.CreateInstance(iuo_custdata) |
1 |
custdata = iuo_custdata.retrieve_custlist() |
1 |
dw_1.object.data = custdata.customers |