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.
The UserProfile’s Symbol List
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.
If GemStone cannot find a match for a name in one of these areas, you are given an error message.
Each GemStone user is associated with an instance of the class UserProfile. This UserProfile stores such information as the GemStone user name, the encrypted password, and access privileges. Your UserProfile also contains the instance variable symbolList.
This UserProfile can be used to login multiple sessions at the same time, including logins by different people in different locations. The way the symbolLists are handled includes options to accommodate this situation.
When creating the UserProfile’s symbol list, the administrator adds specific SymbolDictionaries to the SymbolList. The SymbolDictionaries contain associations that define the names of all objects that are globally resolvable by your UserProfile. While this will vary by application, your symbol list contains at least two dictionaries:
Your symbol list will usually include other application-specific dictionaries to hold the code you are developing. These may be shared with other users, so that you can all read and modify the objects they contain. An administrator can arrange for a dictionary to be shared by inserting a reference to that dictionary in each user’s UserProfile symbol list.
While every user will have the shared Globals and a private UserGlobals dictionary, and by default most users will have the Published dictionary, the list of SymbolDictionaries in each user’s SymbolList may otherwise be completely different.
To get a list of the dictionaries in your symbol list, send your UserProfile the message dictionaryNames. For example:
topaz 1> printit
System myUserProfile dictionaryNames
%
1 UserGlobals
2 ClassesForTesting
3 Globals
4 Published
5 UserClasses
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 description of GemStone kernel objects, see the appropriate appendix 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.1 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 that, 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.
System myUserProfile symbolList
createDictionaryNamed: #NewDict at: 1.
When you specify an index for a new SymbolDictionary, the existing symbol list dictionaries are shifted as needed to accommodate the new dictionary.
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.
Object subclass: 'MyCollection'
instVarNames: #('snakes' 'snails' 'tails')
classVars: #()
classInstVars: #()
poolDictionaries: {}
inDictionary: UserGlobals
! Resolves to UserGlobals
MyCollection instVarNames printString
| myClassDict |
(System myUserProfile resolveSymbol: #MyClassDict)
ifNil:[myClassDict := (System myUserProfile createDictionary:
#MyClassDict)]
ifNotNil:[myClassDict := (System myUserProfile resolveSymbol:
#MyClassDict) value].
GsSession currentSession userProfile
insertDictionary: myClassDict at: 1.
Object subclass: 'MyCollection'
instVarNames: #('this' 'that' 'theOther')
classVars: #()
classInstVars: #()
poolDictionaries: {}
inDictionary: MyClassDict.
! Now resolves to different class in MyClassDict
MyCollection instVarNames printString
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.
To remove a symbol dictionary, send your UserProfile the message removeDictionaryAt: anInteger, passing in the index of the dictionary you want to remove.
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.5 uses dictionariesAndSymbolsOf: to find out which dictionaries in the symbol list stores a reference to class DateTime.
topaz 1 > printit
| anArray myUserPro |
myUserPro := System myUserProfile.
"Find the first SymbolDictionary containing DateTime."
anArray := (myUserPro dictionariesAndSymbolsOf: DateTime) first.
"Get the name of the SymbolDictionary, which is a key within itself"
(anArray at: 1) keyAtValue: (anArray at: 1)
%
Globals
Since the persistent symbolList is shared between all sessions that use that UserProfile to login, you must use caution in making updates that you don’t want to be picked up by other logins using the same UserProfile.
This is possible by using the transient symbolList. The transient symbolList is kept in the singleton instances GsCurrentSession, which you can access using GsSession currentSession or GsCurrentSession >> currentSession. The instance of GsCurrentSession is not copied into any client interface nor committed as a persistent object. Since the transient copy of the symbolList is transient, changes to it cannot incur concurrency conflicts, nor are they subject to rollback after an abort.
Executing any of the following methods will create a copy of the transient symbolList in session state:
System class >> refreshTransientSymbolList
GsCurrentSession >> transientSymbolList: aSymbolList
GsCurrentSession >> transientSymbolList
Before executing any of these methods, GsSession currentSession symbolList refers to the persistent SymbolList (System myUserProfile symbolList).
After executing any of these methods, GsSession currentSession symbolList refers to a transient copy of the persistent SymbolList.
GsCurrentSession >> symbolList is used to resolve global names in the image. If you application may use transient symbolLists, this is the most reliable route to determine how symbols will resolve in the current session, since it will provide the persistent symbolList if there is no transient symboList, otherwise the current transient symboList.
When you have a transient symbolList (that is, if you have executed any of the methods listed here), then you will need to be conscious of how you are updating the symbolList, to make sure you get the expected behavior.
GsSession currentSession transientSymbolList createDictionaryNamed:at:
If you want to make changes to both the persistent symbolList, and the transient symbolList, methods such as this update both the persistent and transient symbolLists.:
System myUserProfile insertDictionary:at:
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 the Globals dictionary 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 snapshot 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.