Windows Client Development |
The SAS Workspace Manager creates and manages SAS Workspaces. With the Workspace Manager, you can:
The Workspace Manager can be used from Visual Basic, C, C++, and VBScript (with the help of Scripto).
The SAS Workspace Manager can create a SAS Workspace in one of three ways:
The userid that is used to log on to SAS will be determined when the workspace is launched. Once launched, the userid cannot be changed.
Stored passwords are not encrypted. This applies to both LDAP entries and files.
The information in a file is only restricted by the permissions on the file. If you are concerned about security, you may not want to use files to store LoginDefs.
Administrators of LDAP servers should configure the directory such that the right to read each LoginDef is restricted to the owner of the LoginDef. Granting access to a LoginDef allows a user to start SAS and log on as the User defined in the LoginDef. It also allows the user to view the password. Also note that the password is sent across the wire in plaintext.
IOM servers can be launched in two ways:
The typical reason this scenario is selected is that it facilitates the use of an OLE DB provider along with a SAS Workspace. While it is possible to use OLE DB without the workspace manager, the only way to use OLE DB with a live SAS Workspace interface is by using the workspace manager.
This allows launching of SAS Workspaces from a shared repository (either in a file or LDAP server). This allows you to launch SAS without regard to the hardware platform or communications protocol (COM, DCOM, or the IOM Bridge for COM) that is being used. This also supports simultaneous OLE DB and SAS Workspace interaction.
There are three definitions that can be created to assist in launching an IOM Server:
A server definition must be created before an IOM Server can be launched with the workspace manager. The server definition can either be loaded from persistent storage (a file or LDAP server), or created dynamically. A ServerDef includes a Logical Name attribute. The server definition is independent of the user.
This is user-specific information such as a user name and password. Login definitions are a convenience and are not required for creating a connection to an IOM server. They provide a mechanism for storing persistent definitions of user names and passwords.
LoginDefs also allow multiple definitions for the same user on different security domains. For example, you could use one user name and password on MVS and a different one for UNIX. This is also possible without the use of a login definition, but the user will need to enter the username/password each time a server is launched.
The logical Name definition allows a description to be associated with each logical name used in a server definition. Logical name definitions are not used to launch a server. However, a logical name is required to launch a server when using the login definition.
These definitions may be stored in a file on the local system or may be stored in a network directory (LDAP server).
The SAS Workspace Manager can access system-wide state information that is stored in the Windows registry. This system-wide state consists of three search specifications for finding launch information:
This information is designed to make it easy to find launch information in a standard location. Also, UI applications can immediately list launch definitions found there.
The diagram below shows the relationship of the interfaces provided with the Workspace Manager.
The IWorkspaceManager interface is the top level interface. It provides collection methods for ServerDefs, LoginDefs, LogicalNameDefs, and running SAS Workspaces.
The scope applies to any function in IWorkspaceManager. It determines where the server and login defs are stored/retrieved. The scope can be set to an enum Scope value (ScopeLocal=1, ScopeSystem=2, ScopeGlobal=3.) This value is stored in the user registry each time Scope is called. Not calling scope causes the last used scope value to be used.
Returns a collection of serverdefs.
Returns a collection of logindefs.
Returns a collection of logicalNameDefs.
Returns a collection of workspaces, which is the set of workspaces that have been created using the SASWorkspaceManager with VisibilityProcess.
The file to read and write SAS Workspace Manager Definition files to/from (suggest *.swd).
Used to specify which LDAP server to use and where to look in the server. LDAP is used only for SASWorkspaceManagerGlobal scope.
The format of the ldapServerURL is LDAP://server:port/baseDN. BaseDN must be specified, and is the distinguished name of the container for the sasserver and saslogin definitions.
The baseDN should include cn=SAS at the start. (This is different than what is specified in the IT administrator, which does not include cn=SAS.) For example, if the baseDN specified in the IT administrator is cn=Applications,dc=myDomain,dc=myCompany,dc=com, then the baseDN specified for the Workspace Manager must be cn=SAS,cn=Applications,dc=myDomain,dc=myCompany,dc=com.
If the port is 0, the default LDAP_PORT (389) is used.
If persistInRegistry is True, the LDAP server that is specified is stored in the registry (HKEY_LOCAL_MACHINE). This occurs each time SetLDAPServer is called. If it is not called, the data previously stored in the registry will be used when the first method that access LDAP is called. If you get a return code of INVALID_LDAP_SERVER, the information in the registry is either wrong or the LDAP server is not running.
Specifies which LDAP userid to use. LDAP is only used for SASWorkspaceManagerGlobal scope.
UserName is a distinguished name of a user to use to connect to LDAP with. Password is the password that corresponds with UserName. If userName and password are both left blank (""), the Workspace Manager will use SSPI to connect to the server.
If persistInRegistry is True, the LDAP user is stored in the registry (HKEY_CURRENT_USER). This occurs each time SetLDAPUser is called. If this is not called, the data previously stored in the registry will be used.
This interface is used to create, enumerate, locate, and remove running SAS Workspace instances. The only SAS Workspaces that can be manipulated through IWorkspaces are those which are created from IWorkspaces.
The number of IWorkspaces in this container
Returns an Enumerator of IWorkspaces. Note that the strange name allows VB to treat this as a collection.
Determine whether Err.Description (which is really IErrorInfo->Description) is an XML table of all connection attempts (true) or just a description of the last failed connection attempt. This option is provided to facilitate problem determination in making connections. Each time the Workspace Manager attempts a connection with a server and fails, an additional record is written in the returned XML error info.
Note that VB may display a dialog box that is too small to display all the xml error info.
The xmlInfo string returned from CreateWorkspaceByLogicalName and CreateWorkspaceByServer is always XML.
This method creates a SAS Workspace from a logical name definition. The invocation of this method results in the following sequence of steps to create the new workspace:
The name that is passed to the method can be used in other IWorkspaces methods to determine which workspace to locate or remove. The name is also set on IWorkspace->Name before this method returns. The SAS Workspace Manager does not ever look at IWorkspace->Name, so a client could change the name after calling Create. The xmlInfo is only defined when this method returns an IWorkspace. See Error Reporting for details.
Note that the WorkspaceManager should be notified when a Workspace is no longer in use, so the workspace can be removed from the internal list, and so the reference the WorkspaceManager holds on the Workspace can be released.
This method creates a workspace from a ServerDef object instead of a logical name. It also accepts the actual userName and password instead of a reference to a LoginDef. This method is preferred over CreateWorkspaceByLogicalName since it does not require username/password pairs to be written to a file or a network directory.
This method attempts to connect to each of the hosts listed in the provided serverDef, one at a time, until either a successful connection is made or all hosts have failed. The userName/password is not required for serverDefs that specify ProtocolCom.
This method locates a workspace by name. This name must have been previously passed in to the workspace manager as the first parameter in the Add (or AddByObjects) method.
This method locates a workspace by its UUID. This UUID is created by SAS and is available through IWorkspace->UniqueIdentifier.
This method removes a workspace from the collection and release the reference. Note that this method does not Quit the workspace.
This method removes a workspace from the collection by UUID. This method performs the same function as the RemoveWorkspace method, but with a different input parameter.
This method creates a new workspace from an existing workspace, and manages it with the SAS workspace manager.
This method adds a Workspace that was created by something outside of the WorkspaceManager. This allows applications to call CoCreateInstance (or whatever else they may have), and notify the Workspace Manager about the existence of that Workspace. Then, that existing workspace can be associated with the OLE DB provider.
This interface allows you to create and manipulate login definitions (LoginDefs). A loginDef is only needed for connections using the IOM Bridge for COM. SSPI, the default NT security, is used for COM/DCOM connections.
Display name of this definition. The definition will persist under this name, so it must be unique. This is the name that is passed to GetWorkspace.
Username as known to the server host.
Password as known to the server host.
The security domain for which this username and password are valid.
All referenceDNs should be the same for any given user across domains.
ILoginDefs contains the standard collection methods Count, _NewEnum, Add, Item, and Remove where the key is the loginDefName. It also supports one additional method:
Returns the ILoginDef that has the given referenceDN and domain.
The workspace manager uses the server def to determine how to connect to the server. For a local or dcom server, only the Name and Hostname values need be filled out. The IOM Bridge for Com requires Protocol to be set to ProtocolBridge; Port and ServiceName may be used with the IOM Bridge for COM.
The TCP/IP port number used to connect with the bridge.
TCP/IP Service name to use to lookup the port. Both Port and ServiceName should not be specified.
Display name of this definition. This value must be set before the serverDef can be persisted.
The security domain that these machineDNSNames exist in. All hosts listed in the MachineDNSNames must be in the same security domain.
When setting this property, this VARIANT can contain one of:
We retrieving this property, the returned VARIANT will contain one of:
Specifying more than one machineDNSName is useful only for failover. It is not possible to control the order in which multiple machines are connected.
Using 'localhost' will cause the local machine to be used.
A description of why this serverdef exists.
Determines how to communicate with the SAS Server. One of ProtocolCom, ProtocolCorba, or ProtocolBridge.
Determines what gets encrypted when using the Bridge protocol. One of EncryptNone, EncryptUserAndPassword, or EncryptAll.
Encryption algorithm to use when using the Bridge. "SASPROPRIETARY" provides the weak SAS encryption algorithm.
Specify which security service to use when using DCOM. This does not apply to Bridge servers or local COM. This must be one of:
Returns the logical server names associated with this SAS Server.
Sets the logical server names assoicated with this SAS Server. The order of the names is not important.
IServerDefs contains the standard collection methods Count, _NewEnum, Add, Item, and Remove where the key is the serverDefName. It also supports one additional method:
Returns an array of serverDef cn's that have the given logicalName.
Login and Server definitions can be stored using either LDAP or via an LDIF file.
Persistence in LDAP is done through two objectclasses: sasServer and sasLogin. The BaseDN parameter of SetLDAPServer specifies the start of the SAS application tree.
LDAP has defined a standard for the textual interchange of LDAP data: the LDapInterchangeFormat. The SAS Workspace Manager has the ability to read LDIF files. This format is also supported by the SAS Spawner. Note that LDIF specifies that each object definition start with a DN, but we don't actually know the full DN of any object. The part that is not known is replaced with "$SUFFIX$" to allow administrators to use an automated search/replace mechanism should they want to import a file into LDAP.
If the call succeeds in obtaining a workspace, the method returns S_OK. However, there are many reasons an error may occur. Sometimes an error can occur even before the connection atempt is made. An example of such an error is:
Other errors can occur during the connection attempt. Examples include:
Errors can occur even though a successful connection is established. Errors that occur when connecting are reported either through the IErrorInfo mechanism (if no SAS workspace is created) or returned in the errorString parameter (if a SAS workspace is created.) The Err.Description (IErrorInfo->Description() in C) and errorString both use the same format which is XML.
If UseXMLInErrorInfo is set to false (it defaults to true), Err.Description will only contain a single error string, that refers to the last connection attempted.
The error information returned through XML allows applications to fix the problems detected. Applications interested in fixing these errors will need to parse the XML and possibly provide a UI to the user, or send a message to the administrator to get the errors fixed so they don't occur again. Here is a sample of the error XML that gets generated (and returned in the errorString parameter of either CreateWorkspaceByLogicalName or CreateWorkspaceByServer) when there is a successful connection made, but the first attempt failed:
<connectionAttempts> <connectionAttempt> <sasservername>MyServer</sasservername> <machineDNSName>alpine.com</machineDNSName> <saslogin>sascnn1</saslogin> <status>0x8004274d</status> <description>Could not establish a connection to the SAS server on the requested machine. Verify that the SAS server has been started with the -objectserver option or that the SAS spawner has been started. Verify that the port Combridge is attempting to connect to is the same as the port SAS (or the spawner) is listening on.</description> </connectionAttempt> <connectionAttempt> <sasserver>MyServer</sasserver> <machineDNSName>alpine.com</machineDNSName> <saslogin>sascnn1</saslogin> <status>0x0</status> <description>Connected</description> </connectionAttempt> </connectionAttempts>
For the SAS Workspace Manager, administration tasks include creating and saving LoginDefs, ServerDefs, and LogicalNameDefs. There are several alternatives for the administration of definitions. Definitions can be administrated directly through the IWorkspaceManager interfaces. These interfaces can write to LDAP and files. Note that the WorkspaceManager will only read/write those attributes that are of interest to it. If you are using files, any existing definitions or attributes that are not used by the Workspace Manager will be deleted, so caution should be used. Additional attributes that are defined for these objects are ignored.
Administration can also be performed on LDAP using a generic LDAP modification tool, such as LDIFDE (included with Windows2000) or ldapadd and ldapmodify (included with the UMich slapd distribution.) LDIF files can also be imported to LDAP.
A favorite text editor could be used to modify the LDIF files.
Most (if not all) administration should be done using the SAS Integration Technologies Administrator, which consolidates the administration chores of the SAS Workspace Manager, the SAS Spawner, and SAS Publish/Subscribe technologies.
Note that it is not necessary to perform any administration at all. Definitions can be created, used, then discarded without ever being persisted.
Dim obSAS As SAS.Workspace Dim obWorkspaceManager As New SASWorkspaceManager.WorkspaceManager Private Sub Form_Load() Dim obConnection As New ADODB.Connection Dim obRecordSet As New ADODB.Recordset Dim errorString As String Set obSAS = obWorkspaceManager.Workspaces.CreateWorkspaceByServer("MyServerName", VisibilityProcess, Nothing, "", "", errorString) obSAS.LanguageService.Submit "data a; x=1; y=100; output; x=2; y=200; output; run;" obConnection.Open "provider=sas.iomprovider.1; SAS Workspace ID=" + obSAS.UniqueIdentifier obRecordSet.Open "work.a", obConnection, adOpenStatic, adLockReadOnly, adCmdTableDirect obRecordSet.MoveFirst Debug.Print obRecordSet(1).Value End Sub Private Sub Form_Unload(Cancel As Integer) ' If we don't close SAS, the SAS process ' may stay around forever If Not (obSAS is Nothing) Then obWorkspaceManager.Workspaces.RemoveWorkspace obSAS obSAS.Close End If End Sub
Dim obSAS As SAS.Workspace Dim obWorkspaceManager As New SASWorkspaceManager.WorkspaceManager Private Sub Form_Load() Dim obConnection As New ADODB.Connection Dim obRecordSet As New ADODB.Recordset Dim obServerDef As New SASWorkspaceManager.ServerDef Dim xmlString As String obServerDef.Port = 6903 obServerDef.Protocol = ProtocolBridge ' Multiple hostNames can be provided ' for failover; order is unreliable. obServerDef.MachineDNSName = "bucky.alpine.com" Set obSAS = obWorkspaceManager.Workspaces.CreateWorkspaceByServer("MyServerName", VisibilityProcess, obServerDef, "UserABC", "ABC'sPassword", xmlString) obSAS.LanguageService.Submit "data a; x=1; y=100; output; x=2; y=200; output; run;" obConnection.Open "provider=sas.iomprovider.1; SAS Workspace ID=" + obSAS.UniqueIdentifier obRecordSet.Open "work.a", obConnection, adOpenStatic, adLockReadOnly, adCmdTableDirect obRecordSet.MoveFirst Debug.Print obRecordSet(1).Value End Sub Private Sub Form_Unload(Cancel As Integer) If not (obSAS is Nothing) Then obWorkspaceManager.Workspaces.RemoveWorkspace obSAS obSAS.Close End If End Sub
This example assumes that an administrator has already defined the "mvs" sasserver and the "BuckyBadger" login.
If the persistInRegistry parameter is true on SetLDAPServer, the provided LDAP URL is written to the system registry (HKEY_LOCAL_MACHINE). If persistIRegistry is true on SetLDAPUser, then the LDAP User and Password are persisted in the CurrentUser registry. An error will occur if persistInRegistry is True and the user does not have permission to write to the registry.
set obWorkspaceManager = CreateObject("SASWorkspaceManager.WorkspaceManager") ' Remember that VBScript can't use VB enums...replace "ScopeGlobal" with 3, ' or add this line for VBSCript ScopeGlobal = 3 obWorkspaceManager.Scope = ScopeGlobal obWorkspaceManager.SetLDAPServer "LDAP://mymachine.alpine.com:8001/o=Alpine Airways,c=US", True obWorkspaceManager.SetLDAPUser "cn=Dan,o=Alpine Airways,c=US", "DansLDAPPassword", True
This example assumes that an administrator has already defined the "mvs" sasserver and the "BuckyBadger" login.
set obWorkspaceManager = CreateObject("SASWorkspaceManager.WorkspaceManager") obWorkspaceManager.Scope = ScopeUser for each def in obWorkspaceManager.ServerDefs debug.print def.Name next
if (session("sas") = nothing) then set obWorkspaceManager = CreateObject("SASWorkspaceManager.WorkspaceManager") set session("sas") = obWorkspaceManager.Workspaces.CreateWorkspaceByLogicalName("MyServer", VisibilityProcess, "mvs", "BuckyBadger", "") end if
' In the Global.asp file, OnApplication_Start() sub set obWorkspaceManager = CreateObject("SASWorkspaceManager.WorkspaceManager") set application("sas") = obWorkspaceManager.Workspaces.CreateWorkspaceByLogicalName("MyServer", VisibilityProcess, "mvs", "BuckyBadger", "")
On Error GoTo CREATEERROR ' Establish LDAP/File parameters Set obSAS = obWorkspaceManager.Workspaces.CreateWorkspaceByLogicalName("mine", VisibilityProcess, "", "", errorXML) ' errorXML only gets returned when there is a successful connection, otherwise err.description gets the XML If (Len(errorXML) > 0) Then ParseXML errorXML CREATEERROR: If (Err.Number <> SASWorkspaceManager.Errors.SEE_XML) Then GoTo TROUBLE End If DISPLAYXML: ParseXML Err.Description GoTo NOTROUBLE TROUBLE: Debug.Print Str(Err.Number) + ": " + Err.Source + ": " + Err.Description NOTROUBLE: End Sub Private Sub ParseXML(xml As String) ' Another option would be to create some XML to make this look nice. ' Write the XML to a file so it can be parsed ' Form2 simply has a WebBrowser control in it Dim f As New Scripting.FileSystemObject Dim fname As String Dim tstream As Scripting.TextStream Dim xmlDoc As Object ' New MSXML.XMLDocument Dim ele As IXMLElement2 fname = f.BuildPath(f.GetSpecialFolder(TemporaryFolder), f.GetTempName) Set tstream = f.OpenTextFile(fname, ForWriting, True) tstream.Write ("<html><body><xml id=""combridgeOutput"">") tstream.Write (xml) tstream.Write ("</xml><table datasrc=""#combridgeOutput""><thead><th>sasServer</th><th>sasLogin</th>") tstream.Write ("<th colspan=50>machineDNSName</th><th colspan=20>port</th><th colspan=40>status</th>") tstream.Write ("<th colspan=200>description</th></thead><tbody><tr><td><span datafld=""sasserver"">") tstream.Write ("</span></td><td><span datafld=""saslogin""></span></td><td colspan=50>") tstream.Write ("<span datafld=""sasmachinednsname""></span></td><td colspan=50><span datafld=""sasport"">") tstream.Write ("</span></td><td colspan=40><span datafld=""status""></span></td><td colspan=200>") tstream.Write ("<span datafld=""description""></span></td></tr></tbody></table></body></html>") tstream.Close Form2.WebBrowser1.Navigate "file://" + fname Form2.Show End Sub
Windows Client Development |