This chapter describes connectors, which allow an application developer to explicitly declare an association between a root client object and a root server object.
Connecting Root Objects
explains which objects to associate using connectors.
Connecting and Disconnecting
describes what connectors do and when they do it.
Kinds of Connectors
describes the available kinds of connectors and the differences between them.
Every replicate and forwarder in the client is connected to an object in the server. You do not, however, need a connector for every replicate or forwarder. A typical application only needs connectors for a small number of root objects.
A connector connects more than the specified client object to the specified server object. Through transitive reference, a connector connects whole networks of objects. Most objects (except atomic objects—characters, booleans, small integers, nil) refer to others through their instance variables. And their instance variables refer to their instance variables, and so on, branch and twig, until you reach the leaves of a large network of objects with a treelike structure.
You can take advantage of this hierarchical structure to minimize application overhead. Identify the object at the root of each subsystem of shared objects, and then connect only these root objects. Depending on how you’ve defined configuration parameters and related matters, you can synchronize entire subsystems in GemStone/S this way. After you’ve connected the application’s roots, GemBuilder automatically manages all the objects referenced from these roots.
Figure 4.1 shows an application in which several connected objects are accessed through global or shared variables in client Smalltalk. One system represents an employee database. Another system represents a data entry application for creating and modifying objects. A third system represents a report writer for these objects. Dotted lines in the figure group the logically related subsystems.
The data entry application and the report writer reside in the client Smalltalk image; however, the employee database is stored on the GemStone server, as it defines a large amount of persistent data that other users may need to share, data that benefits from GemStone/S’s capacity, stability, robustness, and fast searches.
Figure 4.2 shows the state of the employee data when stored on the server:
In Figure 4.2, objects a and b are root objects: those objects from which all others can be reached by transitive closure: by direct reference, or by indirect reference through any number of layers.
The above discussion has focused on shared instances from your applications, but in order to share instances in any way, GemBuilder and GemStone must first share definitions for each class of shared instance.
Some connectors connect their objects whenever any session logs in. Some do so only when logging in using a specific session parameters object:
When a session logs in, the connectors of its session parameters and all global connectors connect automatically. When a session logs out, its connectors disconnect.
Connectors are saved in client Smalltalk sets, separate ones for global connectors and each session parameters object. Two connectors are considered equal if they resolve to the same client object. Client Smalltalk sets eliminate duplicates based on equality. Therefore:
NOTE
Adding a global or session connector that points to the same object as an existing connector will remove the existing connector.
Duplicate session connectors are not removed if they are stored in different session parameters.
GemBuilder provides a configuration parameter, connectVerification, that, when true, causes connectors to verify at login that they are not redefining a connector that already exists. In addition, class connectors verify that the two classes they are connecting have compatible structures.
If a connector fails verification, GemBuilder issues a notifier if verbose is also true, or raises an exception otherwise. You can set connectVerification in the Connector Browser or in the Settings Browser.
At login, a connector associates an object in a single-user image with an object in a multiuser repository. The value of either could have changed since last login. Which value is valid?
Connectors can initialize either object by performing a specified postconnect action:
Update Smalltalk
default for all but class connectors, initializes the client object with the current state of the GemStone server object.
Update GemStone
initializes the GemStone server object with the current state of the client object.
Forward to the server or client
makes one object a forwarder to the other. Forwarders are discussed starting here.
No initialization
leaves the client object and GemStone server object unmodified after connection—default for class connectors.
As the name implies, postconnect actions execute only at initial connection. After that, changes propagate according to mark dirty specifications, as described in Synchronizing State, or they do not propagate at all, as is normally the case with class connectors, as described in Class Mapping.
By default, after login and initialization, class connectors do not propagate changes. If you’ve defined classes differently on the client and the server, you probably had good reason to do so; you probably don’t want one object space to update the other with its own class definition. Therefore, to avoid updating class definitions, class connectors generally specify a postconnect action of none.
For similar reasons, class connectors cannot specify that the client class is a forwarder—the forwarder and clientForwarder postconnect action are unavailable for class connectors.
If you change either a client or GemStone class definition during a session, you must propagate the change yourself by disconnecting and reconnecting the connector. The Connector Browser, described starting here, provides convenient buttons for the purpose.
NOTE
Remember to restore a postconnect action of none after you complete the desired update.
At login, connectors connect objects according to their specifications; thereafter, they are inactive. Changes to instances that occur during the course of a session are replicated either because those instances are synchronized replicates that mark changes dirty, or because one is a forwarder to the other. Changes to class definitions or other unsynchronized changes must be propagated manually. To do so, use the Disconnect and Connect buttons in the Connector Browser to disconnect and reconnect the appropriate connector.
Connectors with a post-connect action of #clientForwarder cannot be explicitly disconnected. These connectors only disconnect at logout.
At logout, GemBuilder sets the instance variables of connectors to nil, if the GemBuilder configuration parameter connectorNilling is set to true (the default). This reduces the risk of defunct stub or forwarder errors, replacing them with nil doesNotUnderstand errors.
Only connectors whose values are set from the server on login are cleared when connectorNilling is true. Session-based name, class variable, or class instance variable connectors that have a postconnect action of #updateST or #forwarder are cleared. Fast connectors, class connectors, or connectors whose postconnect action is #updateGS or #none do not have instance variables set to nil.
connectorNilling can be set for individual sessions, if desired. See Setting Configuration Parameters for details on setting session-specific configuration parameters. The detailed description for this configuration parameter is here.
Five kinds of connectors use different ways of finding the two objects to connect. You have already encountered one kind:
Class connector—connects a client Smalltalk and GemStone class. As discussed in Class Mapping, to replicate an object, both client and repository must define the class, and the two classes must be connected using a class connector.
For replicating instances, however, we need ways to connect root objects:
Name connector—connects client and server objects identified by name. Figure 4.3 illustrates how a name connector connects a client object to a server object.
Class variable connector—first resolves the named objects representing the classes, then looks for a class variable in the GemStone class, and a Class or Shared Variable in the client Smalltalk class with the specified name and connects those objects.
Class instance variable connector—first resolves the named objects representing the classes, then looks for a class instance variable in each class with the specified name and connects those objects.
Fast connector—connects the GemStone kernel classes to their client Smalltalk counterparts. Fast connectors are predefined. The kernel classes to which they point will not change identity during the course of a session. The GemStone kernel class connectors are predefined, and GemBuilder relies on them. Applications should not define fast connectors.
At login, GemBuilder connects connectors in the following order:
1. First, predefined fast connectors for kernel classes;
2. next, class connectors whose postconnect action is anything other than updateGS; and finally
3. all other connectors, in no particular order.
You can control the order in which connectors connect by connecting them explicitly in your code, instead of relying on GemBuilder’s automatic mechanism to connect them for you at login.
Except for fast connectors (discussed in the following section), all kinds of connectors find the objects to connect through a name lookup. Names must be found in namespaces. GemBuilder looks in the namespace “Smalltalk”; a fully-qualified name can also be used.
In the client, GemStone implements namespaces with symbol dictionaries. If the symbol list of the session user includes the symbol dictionary defining object A, then object A is visible to that user.
Lookup occurs when the connector connects, usually when the session first logs in.
You can bypass name lookup by using a fast connector, which saves direct references to the client Smalltalk objects and the object IDs of the GemStone server objects that are connected.
NOTE
The name “fast connector” is historic. These connectors are not necessarily faster than other connectors.
Using fast connectors can be risky. If the GemStone server object is renamed or redefined, a fast connector will continue to point to the old object: the one with the same object identifier. When the identity of an object changes (for example, if it is a variable that you assign to a new object), a fast connector becomes incorrect. An out-of-date fast connector may cause an “object does not exist” error, or it may silently continue to pass messages to an old object.
Because using object identity is not always an appropriate way to resolve an object, we recommend that you do not use fast connectors.
To make and manage connectors interactively, see Connector Browser. The next section describes making and managing connectors in code.
GbsConnector is the abstract superclass for the connector class hierarchy. These classes implement connection methods and define instance variables to refer to the associated GemStone and client objects. Figure 4.4 shows the hierarchy.
To create a connector programmatically:
2. Set its postconnect action, if other than the default.
3. Add it to the global connector list, or a connector list for session parameters.
Create the required GemStone session parameters and connectors in an initialization method. (Creation methods for session parameters are described in Session Parameters.)
One simple creation method for a name connector requires only the names of the two objects to be connected:
GbsNameConnector stName: stName
gsName: gsName
You can create a class connector this way too:
GbsClassConnector stName: stName
gsName: gsName
The above methods require that the server object already exist. If GemBuilder must create the object, choose an instance creation method that specifies the GemStone server dictionary in which to place it:
GbsNameConnector stName: stName
gsName: gsName
dictionaryName: gsDictionary
To create a class variable connector:
GbsClassVarConnector
stName: #ClassName
gsName: #ClassName
cvarName: #ClassVarName
Similarly, a class instance variable connector:
GbsClassInstVarConnector
stName: #ClassName
gsName: #ClassName
cvarName: #ClassInstVarName
For more, browse instance creation methods for each connector class.
The symbolic names for postconnect actions are #updateST, #updateGS, #forwarder, #clientForwarder, and #none. All connectors default to using #updateST except class connectors, which default to #none.
To cause a GemStone server object to take its initial values at login from its Smalltalk counterpart, send postConnectAction: #updateGS to the connector. This is occasionally useful for loading data into GemStone from the client image.
When you create a connector, you must decide whether it is to be managed by an individual session parameters object or globally. Leaving it unmanaged can have several adverse effects: it will not be connected and disconnected when required, and object retrieval may slow.
A connector is managed by adding it to the appropriate list of connectors.
If you want a connector in effect whenever any session logs in, put it in the global connectors collection:
GBSM addGlobalConnector: aConnector
A new global connector first takes effect the next time any session logs in.
Each session parameters object maintains its own list of session connectors. If you want a connector in effect whenever a session logs in using specific parameters, add a connector to the session parameters object:
ThisApplicationParameters addConnector: aConnector
A new session connector first takes effect the next time a session logs in using those parameters.
To initialize a system with two roots, the global BigDictionary, and a class variable in MyClass called MyClassVar, your application might execute code such as that shown in Example 4.1:
GBSM addGlobalConnector: (GbsNameConnector
stName: #MyGlobal
gsName: #MyGlobal);
addGlobalConnector: (GbsClassVarConnector
stName: #MyClass
gsName: #MyClass
cvarName: #MyClassVar)
Initialization code such as that in Example 4.1 needs to execute only once. From then on, every time you log into GemStone, MyGlobal and MyClassVar (and all the objects they reference) connect; after that, replication and updating occur as specified.
The following examples illustrate one approach to managing GemBuilder sessions and connectors: a session control class that defines these methods for, in this example, a help request system.
An instance of the session control class could be stored in the application object as a class variable, in which case the session information would be the same for all instances of the application, or it could be stored in the application as an instance variable, in which case each instance of the application would get its own copy to change as needed. In either case, methods to create the session parameters object and its connectors might follow these patterns:
Example 4.2 shows the method session, which returns the application’s logged-in session. If the session is not logged in, the method requests an RPC login and returns the resulting session. If login fails, the method returns nil.
session "self session"
(session isNil or: [session isLoggedIn not]) ifTrue: [
session := self sessionParameters loginRpc.
session isNil ifTrue: [^nil]].
^session
Example 4.3 shows a method that initializes a set of session parameters. (For security, you may choose to prompt for passwords instead.)
sessionParameters
| params |
sessionParameters isNil ifTrue: [
params := GbsSessionParameters new.
params gemStoneName: 'gs64stone'.
params username: 'DataCurator'.
params password: 'swordfish'.
params gemService: 'gemnetobject'.
params rememberPassword: true.
params rememberHostPassword: true.
self addConnectorsTo: params.
sessionParameters := params.
GBSM addParameters: params].
^sessionParameters
addConnectorsTo: aParams
self addClassConnectorsTo: aParams.
self addClassVarConnectorsTo: aParams
Example 4.5 shows a method that creates class connectors and adds them to the session parameters connector list:
addClassConnectorsTo: aParams
aParams addConnector:
(GbsClassConnector
stName: #GST_Action
gsName: #GST_Action).
aParams addConnector:
(GbsClassConnector
stName: #GST_Customer
gsName: #GST_Customer).
aParams addConnector:
(GbsClassConnector
stName: #GST_Engineer
gsName: #GST_Engineer).
Example 4.6 shows a method that creates class variable connectors and adds them to the session parameters connector list:
addClassVarConnectorsTo: aParams
| aConnector |
aParams addConnector:
(aConnector := GbsClassVarConnector
stName: #GST_HelpRequest
gsName: #GST_HelpRequest
cvarName: #AllRequests).
aConnector postConnectAction: #forwarder.
aParams addConnector:
(GbsClassVarConnector
stName: #GST_Company
gsName: #GST_Company
cvarName: #AllCompanies)
You can create methods similar to those shown in examples and to create name connectors and global connectors for your application, as well.
NOTE
If more than one session is logged into GemStone using the same session parameters object, and you add a connector to one of those sessions, GemBuilder will try to connect that connector for all sessions sharing the same parameters. If any fail to reference the GemStone server object represented by the connector, you’ll receive an error message stating that the connector failed to connect.