This chapter describes how GemStone Smalltalk finds the objects to which your programs refer and explains how you can arrange to share (or not to share) objects with other GemStone users.
Sharing Objects
explains how GemStone Smalltalk allows users to share objects of any kind.
UserProfile and Session-Based Symbol Lists
describes the mechanism that the GemStone Smalltalk compiler uses to find objects referred to in your programs.
Using Your Symbol Dictionaries
discusses how you can enable other users of your application to share information.
GemStone Smalltalk permits concurrent access by many users to the same data objects. For example, all GemStone Smalltalk programmers can make references to the kernel class Object. These references point directly to the single class Object—not to copies of Object.
GemStone allows shared access to objects without regard for whether those objects are files, scalar variables, or collections representing entire databases. This ability to share data facilitates the development of multi-user applications.
To find the object referred to by a variable, GemStone follows a well-defined search path:
1. The local variable definitions: temporary variables and arguments.
2. Those variables defined by the class of the current method definition: instance, class, class instance, or pool variables.
3. The symbol list assigned to your current session (see the following discussion).
If GemStone cannot find a match for a name in one of these areas, you are given an error message.
The GemStone system administrator assigns each GemStone user an object of class UserProfile. Your UserProfile stores such information as your name, your encrypted password, and access privileges. Your UserProfile also contains the instance variable symbolList.
When you log in to GemStone, the system creates your current session (which is an instance of GsCurrentSession) and initializes it with a copy of the UserProfile’s symbolList object. GemStone Smalltalk refers to this copy of the symbol list to find objects you name in your application. See Figure 3.1.
This instance of GsCurrentSession is not copied into any client interface nor committed as a persistent object. Since the symbolList is transient, changes to it cannot incur concurrency conflicts, nor are they subject to rollback after an abort.
Changes to the current session’s symbolList (the transient symbolList) do not affect the UserProfile symbolList (the persistent symbolList). Thus, the UserProfile symbolList can continue to serve as a default list for other logins. Methods are provided to synchronize your session and UserProfile symbolLists.
In creating your UserProfile symbol list, the data curator adds SymbolDictionaries containing associations that define the names of all objects that the data curator thinks you might need. Although the decision about which objects to include is entirely up to the data curator, your symbol list contains at least two dictionaries:
The symbol list may also include special-purpose dictionaries that are shared with other users, so that you can all read and modify the objects they contain. The data curator can arrange for a dictionary to be shared by inserting a reference to that dictionary in each user’s UserProfile symbol list.
Except for the dictionaries Globals and UserGlobals, the contents of each user’s SymbolList are likely to be different.
To get a list of the dictionaries in your persistent symbol list, send your UserProfile the message dictionaryNames. For example:
topaz 1> printit
System myUserProfile dictionaryNames
%
1 UserGlobals
2 UserClasses
3 ClassesForTesting
4 Globals
5 Published
The SymbolDictionaries listed in the example have the following function:
To list the contents of a symbol dictionary:
topaz 1> printit
UserGlobals keys
%
a SymbolSet
...
#1 GcUser
#2 UserGlobals
#3 GsPackagePolicy_Current
#4 PackageLibrary
...
If you examine all of your symbol list dictionaries, you’ll see that most of the kernel classes are listed. In addition, there are global variables, both public and for internal use. For a detailed description of GemStone kernel objects, see Appendix D of the System Administration Guide.
You’ll discover that most of the dictionaries refer to themselves. Since the symbol list must contain all source code symbols that are not defined locally nor by the class of a method, the symbol list dictionaries need to define names for themselves so that you can refer to them in your code. Figure 3.2 illustrates that the dictionary named UserGlobals contains an association for which the key is UserGlobals and the value is the dictionary itself.
The object server searches symbol lists sequentially, taking the first definition of a symbol it encounters. Therefore, if a name, say “#BillOfMaterials,” is defined in the first dictionary and in the last, GemStone Smalltalk finds only the first definition.
NOTE
To insert or remove a SymbolDictionary to/from your symbol list, you must have the necessary system privilege. For details, see "User Accounts and Security" in the System Administration Guide.
Creating a dictionary is like creating any other object, as the following example shows. Once you’ve created the new dictionary, you can add it to your symbol list by sending your UserProfile the message insertDictionary: aSymbolDict at: anInt.
| newDict |
newDict := SymbolDictionary new.
newDict at: #NewDict put: newDict.
System myUserProfile insertDictionary: newDict at: 1.
As you might expect, insertDictionary: at: shifts existing symbol list dictionaries as needed to accommodate the new dictionary. In Example 3.3, the new dictionary is inserted into the UserProfile symbolList and then updated in the current session.
Because the GemStone Smalltalk compiler searches symbol lists sequentially, taking the first definition of a symbol it encounters, your choice of the index at which to insert a new dictionary is significant.
The following example places the object MyCollection (a class) in the user’s private dictionary named MyClassDict. Then it inserts MyClassDict in the first position of the current Session’s symbolList, which causes the object server to search MyClassDict prior to UserGlobals. This means that the GemStone object server will always find MyCollection in MyClassDict, not in UserGlobals.
| myClassDict |
(System myUserProfile resolveSymbol: #MyClassDict) isNil
ifTrue:[myClassDict := (System myUserProfile createDictionary:
#MyClassDict)]
ifFalse:[myClassDict := (System myUserProfile resolveSymbol:
#MyClassDict) value].
Object subclass: 'MyCollection'
instVarNames: #('this' 'that' 'theOther')
classVars: #()
classInstVars: #()
poolDictionaries: {}
inDictionary: myClassDict.
GsSession currentSession userProfile
insertDictionary: myClassDict at: 1.
Object subclass: 'MyCollection'
instVarNames: #('snakes' 'snails' 'tails')
classVars: #()
classInstVars: #()
poolDictionaries: {}
inDictionary: UserGlobals
Recall that the object server returns only the first occurrence found when searching the dictionaries listed by the current session’s symbol list. When you subsequently refer to MyCollection, the object server returns only the version in MyClassDict (which you inserted in the first position of the symbol list) and ignores the version in UserGlobals. If you had inserted MyClassDict after UserGlobals, the object server would only find the version of MyCollection in UserGlobals.
You may redefine any object by creating a new object of the same name and placing it in a dictionary that is searched before the dictionary in which the matching object resides. Therefore, inserting, reordering, or deleting a dictionary from the symbol list may cause the GemStone object server to return a different object than you may expect.
This situation also happens when you create a class with a name identical to one of the kernel class names.
CAUTION
Avoid redefining any kernel classes. Their implementation may change from one version of GemStone to the next. Creating a subclass of a kernel class to redefine or extend that functionality is usually more appropriate.
To remove a symbol dictionary, send your UserProfile the message removeDictionaryAt: anInteger, passing in the index of the dictionary you want to remove.
There are many ways that the current session’s symbol list can get out of sync with the UserProfile symbol list. As some of the examples in this chapter show, updates can made to the current session symbol list that exist only as long as you are logged in. By changing only the symbol list for the current session, you can dynamically change the session namespace without causing concurrency conflict. For example, if you are developing a new class, you can purposely set your current session symbol list to include new objects for testing.
Three UserProfile methods help synchronize the persistent and transient symbol lists:
insertDictionary: aDictionary at: anIndex
This method inserts a Dictionary into the UserProfile symbol list at the specified index.
removeDictionaryAt: anIndex
This method removes the specified dictionary from the UserProfile symbol list.
symbolList: aSymbolList
This method replaces the UserProfile symbol list with the specified symbol list.
Each of these methods modifies the UserProfile symbol list. If the receiver is identical to “GsSession currentSession userProfile”, the current session’s symbol list is updated. If a problem occurs during one of these methods, the persistent symbol list is updated, but the transient current session symbol list is left in its old state.
In Example 3.5, the transient symbol list is copied into the persistent UserProfile symbol list. The example continues with adding a new dictionary to the current session and finally resets the current session’s symbol list back to the UserProfile symbol list.
"Copy the GsSession symbol list to the UserProfile"
System myUserProfile symbolList:
(GsSession currentSession symbolList copy).
"Check that the symbol lists are the same"
GsSession currentSession symbolList =
System myUserProfile symbolList.
"Add a new dictionary to the current session"
GsSession currentSession symbolList add: SymbolDictionary new.
"Compare the two symbol lists; they should differ"
GsSession currentSession symbolList =
System myUserProfile symbolList.
"Update the UserProfile symbolList to current session"
GsSession currentSession symbolList replaceElementsFrom:
(System myUserProfile symbolList).
To find out which dictionary defines a particular object name, send your UserProfile the message symbolResolutionOf: aSymbol. If aSymbol is in your symbol list, the result is a string giving the symbol list position of the dictionary defining aSymbol, the name of that dictionary, and a description of the association for which aSymbol is a key. For example:
If aSymbol is defined in more than one dictionary, symbolResolutionOf: finds only the first reference.
To find out which dictionaries stores a name for an object and what that name is, send your UserProfile the message dictionariesAndSymbolsOf: anObject. This message returns an array of arrays containing the dictionaries in which anObject is stored, and the symbols which name that object in that dictionary.
Example 3.7 uses dictionariesAndSymbolsOf: to find out which dictionaries in the symbol list stores a reference to class DateTime.
| anArray myUserPro |
myUserPro := System myUserProfile.
"Find first Dictionary containing DateTime"
anArray := (myUserPro dictionariesAndSymbolsOf: DateTime) first.
anArray at: 1.
aSymbolDictionary
"Get the name of the SymbolDictionary"
(anArray at: 1) keyAtValue: (anArray at: 1)Globals
As you know, all GemStone users have access to such objects as the kernel classes Integer and Collection because those objects are referred to by a dictionary (usually called Globals) that is present in every user’s symbol list.
If you want GemStone users to share other objects as well, you need to arrange for references to those objects to be added to the users’ symbol lists.
NOTE
To insert or remove a SymbolDictionary to/from your symbol list, or to make any changes to a UserProfile that is not your own, you must have the necessary system privilege. For details, see "User Accounts and Security" in the System Administration Guide.
The Published Dictionary, PublishedObjectSecurityPolicy, and the groups Subscribers and Publishers together provide an example of how to set up a system for sharing objects.
The Published Dictionary is an initially empty dictionary referred to by your UserProfile. You can use the Published dictionary to "publish" application objects to all users — for example, symbols that most users might need to access. The Published Dictionary is not used by GemStone classes; rather, it is available for application use.
The PublishedObjectSecurityPolicy is owned by the Data Curator and has World access set to none. Two groups have access to the PublishedObjectSecurityPolicy:
Publishers can create objects in the PublishedObjectSecurityPolicy and enter them in the Published Dictionary. Then members of the Subscribers group can access the objects.
For example, your system administrator might add each member of a programming team to the group Publishers. After completing the definition of a new class, a programmer could make the class available to colleagues by adding it to the Published dictionary. Because this dictionary is already in each user’s symbol list, whatever you add becomes visible to users the next time they obtain a fresh transaction view of the repository. Using the Published dictionary lets you share these objects without having to put them in Globals, which contains the GemStone kernel classes, and without the necessity of adding a special dictionary to each user’s symbol list.