3. Resolving Names and Sharing Objects

Previous chapter

Next chapter

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.

3.1 Sharing Objects

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.

3.2 The UserProfile’s Symbol List

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.

What’s In Your Symbol List?

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:

  • A “system globals” dictionary called Globals. This dictionary contains some or all of the GemStone Smalltalk kernel classes (Object, Class, Collection, etc.) and any other objects to which all of your GemStone users need to refer. Although you can read the objects in Globals, normally only SystemUser is permitted to modify them.
  • A dictionary that is specific to your UserProfile, called UserGlobals. This dictionary can be used to store objects for your own use and new classes you do not need to share with other GemStone users. This dictionary also contains GemStone infrastructure that is UserProfile specific.

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.

Examining Your Symbol List

To get a list of the dictionaries in your symbol list, send your UserProfile the message dictionaryNames. For example:

Example 3.1

topaz 1> run 
System myUserProfile dictionaryNames
%
 1 UserGlobals
 2 ClassesForTesting
 3 Globals
 4 Published
 5 UserClasses
 
 

The SymbolDictionaries listed in the example have the following function:

  • UserGlobals
    Contains per-user application and application service objects.
  • ClassesForTesting
    A user-defined dictionary.
  • Globals
    Provides access for the GemStone kernel classes.
  • Published
    Provides space for globally visible shared objects created by a user.
  • UserClasses
    Usually only present if you are using GemBuilder for Smalltalk (GBS) to replicate classes to the server. Putting this dictionary before the Globals dictionary allows an application or user to override kernel classes without changing them. Keeping it separate from UserGlobals allows a distinction between classes and application objects.

To list the contents of a symbol dictionary:

  • If you are using Topaz, execute an expression that returns the dictionary. Example 3.2 lists the dictionary keys. Alternatively, you could just execute UserGlobals to examine all keys and values.
  • If you are running GBS, select the expression UserGlobals in a GemStone workspace and execute GS-Inspect it.
Example 3.2

topaz 1> run 
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.

Figure 3.1 Self-Referencing Symbol Dictionary

 

You can get the name of a SymbolDictionary by sending name. For example,

topaz 1> run
System myUserProfile symbolList collect: [:ea | ea name]
%
a Array
  #1 UserGlobals
  #2 Globals
  #3 Published

Inserting and Removing Dictionaries from Your Symbol List

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.

For example:

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.

Example 3.3

Object subclass: 'MyCollection'
	instVarNames: #('snakes' 'snails' 'tails')
	classVars: #()
	classInstVars: #()
	poolDictionaries: {}
	inDictionary: UserGlobals
 
! Resolves to UserGlobals
MyCollection instVarNames printString
 
| myClassDict |
(System myUserProfile resolveSymbol: #MyClassDict) isNil
	ifTrue:[myClassDict := (System myUserProfile createDictionary:
				#MyClassDict)]
	ifFalse:[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.

Avoid redefining any kernel classes in other SymbolDictionaries. 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.

Finding Out Which Dictionary Names an Object

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:

Example 3.4

topaz 1> run
System myUserProfile symbolResolutionOf: #Bag%
2 Globals
    Bag  Bag
 

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.

Example 3.5

topaz 1 > run
| 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
 

Note that dictionariesAndSymbolsOf: may return zero, one, or multiple dictionaries.

The Transient Symbol List

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. In releases before 3.6, a transient symbolList was created by default, but in 3.6 and later, you must execute code that specifies use of a 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.

 

Figure 3.2 The GsSession symbolList — a copy of the UserProfile symbolList

 

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.

Updating Symbol Lists

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.

  • If you add a new symbolDictionary to the persistent symbolDictionary but not to the transient symbolDictionary, and these are different instances, then the new symbolDictionary is not resolvable using GsCurrentSession >> symbolList, and so not readily usable by name. This is the case, for example, if you make changes to the result of System myUserProfile symboList. after executing GsCurrentSession transientSymbolList.
  • If you add a new symbolDictionary to the transient symbolDictionary but not to the persistent symbolDictionary, the new symbolDictionary will be available for the session, but will disappear when the session logs out. This expression will insert a dictionary into the transient symbolList, and create the transient symbolList if it was not previously different than the persistent symbolList:
GsSession currentSession transientSymbolList createDictionaryNamed:at:
  • If you add a new symbolDictionary to both the transient symbolDictionary and to the persistent symbolDictionary, and you abort your transaction, the new symbolDictionary will disappear from the persistent symbolDictionary, but remain in the transient symbolDictionary until the session logs out.

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:

3.3 Using Your Symbol Dictionaries

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.

Publishers, Subscribers and the Published Dictionary

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:

  • Subscribers have read-only access.
  • Publishers have read-write access.

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.

Previous chapter

Next chapter