|< BACK  NEXT >|
Now that we understand the administrative object model and various types of collections available in the catalog, we are almost ready to automate some common administration tasks. Before we do that, however, there are a few other programming issues we need to examine.
As with all COM interface methods, COMAdmin objects return error codes in the form of HRESULTs. For the benefit of C++ programmers, these error codes are defined in <winerror.h> as COMADMIN_E_XXX error messages. For VBScript programmers, error codes can be accessed through the familiar err object.
When you use methods such as Populate and SaveChanges on COMAdminCatalogCollection, you could be reading or writing data for every item in the collection. Complicated errors can occur and they can be difficult to diagnose based on a single numeric error code. To address this problem, COMAdmin objects provide extended error information through a special collection called the ErrorInfo collection.
The ErrorInfo collection can be obtained in one of the following two ways:
By calling GetCollection on the COMAdminCatalog object with ErrorInfo as the name of the collection.
By calling GetCollection on the COMAdminCatalogCollection object with ErrorInfo as the name of the collection, leaving the second parameter blank.
When a COMAdmin object method fails, you must fetch and Populate the ErrorInfo collection immediately (before calling any other method on the object). Otherwise, the ErrorInfo collection gets reset and you will lose the extended error information.
Each ErrorInfo object in the collection has two important properties Name and ErrorCode. The first property indicates the name of the object or file that had an error, and the second property contains the error code for the object or file.
The following VBScript code fragment shows how to retrieve and display the extended error information:
on error resume next ' do some operation that may fail if err <> 0 then Dim errorInfo set errorInfo = catalog.GetCollection("ErrorInfo") errorInfo.Populate Dim item for each item in errorInfo msgbox item.Name & " " & hex(item.Value("ErrorCode")) next end if
Sometimes, it is desirable to perform all the administrative operations within a single transaction so that the configuration changes are committed or aborted as one atomic operation. Some of the benefits of doing this are:
Consistency of data The administrative operations performed within a transaction are committed or aborted as a whole.
Consistent deployment across multiple machines If a COM+ application is deployed across multiple machines, it is guaranteed that all servers are left with an identical configuration.
Performance When you perform multiple operations within a transaction, all writes to RegDB are performed at once. Writing to RegDB is a relatively expensive operation. If you are making many writes to RegDB (by calling SaveChanges, for example), you will get better performance.
The good news is that the registration database (RegDB) is a transacted resource manager, that is, it can be enlisted with the DTC (see Chapter 8).
The bad news is that many other resources involved in normal administrative tasks, such as the Windows registry, the file system, and the Windows Installer (MSI), are not transactional. In case of an error, any changes made to these resources will not be rolled back. For example, if there is an error installing a COM+ application from an MSI file, the application will not appear in the catalog, but some files that were copied to the hard disk may still be left there, in which case one has to manually delete them.
To ensure proper data consistency, RegDB forces certain blocking and isolation behavior that you should be aware of.
When any COMAdmin method is called within a transaction that causes a write to the catalog, the catalog server puts a writer lock on the catalog, that is, any other writer trying to modify the catalog gets blocked until the current transaction gets committed or aborted. Only those writers that are part of the current transaction are allowed to enter the lock.
The readers are not blocked. However, the data that the external readers see will not reflect any interim changes made within the transaction until the transaction commits. Only those readers participating in the current transaction can see the interim data states.
To achieve this isolation behavior, RegDB effectively provides a data cache for the transaction to use. This impacts the behavior of the SaveChanges method.
Normally, a call to SaveChanges writes all the changes back to the catalog. Within a transaction, however, SaveChanges returns immediately, whether or not all changes have been transitionally committed to RegDB. This is particularly a problem for the StartApplication method call subsequent to a SaveChanges method call. There is no guarantee that StartApplication will use fresh data.
Within a transaction, it is a good idea to introduce a sleep for a few seconds between SaveChanges and StartApplication calls.
Now that we understand the transactional behavior of RegDB and the caveats thereof, let s see how we can perform administrative tasks within a transaction.
Using the transaction context object would probably be the simplest way to perform administrative operations within a transaction. Recall from Chapter 8 that a non-transactional client can use a transaction context object to create a single transaction.
The following VBScript code fragment illustrates the use of a transaction context object:
' Create the transaction context Dim txCtx Set txCtx = CreateObject("TxCtx.TransactionContext") ' Create the catalog object within the transaction Dim catalog Set catalog = txctx.CreateInstance("COMAdmin.COMAdminCatalog") ... if err <> 0 then txCtx.Abort else txCtx.Commit end if
|< BACK  NEXT >|