ptg 1984 CHAPTER 49 SQL Server Service Broker received within the specified time, the code successfully locks that conversation group (that is, locks the specified queue for receiving). If not, @ConversationGroupId is NULL. After this call, the program attempts to receive the next message in the queue by using the RECEIVE statement, whose syntax is similar to that of SELECT, except that instead of speci- fying a table name, you specify a queue name. Next, the code checks the received message type and takes the appropriate action. If the message type is Error, it ends the dialog, reporting the error. If the message type is EndDialog, it simply ends its side of the dialog. If it is a catalog change message, it updates Publication.Product so that the related row in Publication.ProductCatalog (which associates products with catalogs) now points to the newest data. Just as with the initiator code, the target code first declares its outgoing messages as a typed XML variable ( @AckXml). This helps in making sure that the outgoing message will be received without error. One issue to be mindful of is that all this code is executing in the scope of a single trans- action. If any part of the code fails, the ROLLBACK statement rolls back any DML as well as message sends and receives. To test the code, you can execute the following statement: EXEC Production.ProductModelUpdate 749, ‘A Super Product’ NOTE You might want to come up with a clever way of populating the body of the responding Production.CatalogChangeAckQueueReader stored procedure to deal with the incom- ing acknowledgment messages sent by the target. Prioritizing Services The next issue to consider is conversation priority. A safe assumption is that some of your software applications are more important than others. Likewise, some of your SSB services are mission-critical (and should be configurable as such) and others may be only for casual use. You can easily set a numeric priority for these services so that when system resources are scarce (during periods of high server and network utilization), messages for your criti- cal applications are processed first. SQL Server 2008 offers a simple syntax for accomplishing this. The following example illustrates how to set the priority to level 1 for messages transmitted from our SSB initiator (local) service to our target (remote) service: CREATE BROKER PRIORITY CatChangeInitToTarget FOR CONVERSATION SET ( CONTRACT_NAME = [//samspublishing.com/SS2008/SSB/Contracts/BasicCatalogChangeContract], LOCAL_SERVICE_NAME = ptg 1985 Service Broker Routing and Security 49 [//samspublishing.com/SS2008/SSB/Services/CatalogChangeInitiatorService], REMOTE_SERVICE_NAME = N’//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyTarget’, PRIORITY_LEVEL = 1 ) Next, you need to learn how to set up Service Broker messaging applications to run on multiple instances of SQL Server. Service Broker Routing and Security The following sections detail how to set up the Service Broker constructs you need for your applications to work in a secure, distributed environment. Using Certificates for Conversation Encryption In Listing 49.4, you might have noticed that you set the ENCRYPTION = OFF flag in the code of the initiator. You had to do this because Service Broker relies heavily on certifi- cate-based encryption. If you had left the encryption flag set to its default of ON, your messages would not have left their transmission queue, and Service Broker would have raised an error. Service Broker services running on multiple SQL Server instances communicate across the network via endpoints that are secured using certificates. Certificates are the foundation of secure network communications. They are used for securing email (PGP), FTP (S/FTP), HTTP (SSL), .NET assemblies (strong naming), and more. The basic premise of certificates is the use of public key cryptography: When a digital certificate is created, a public key (shared openly) and a private key (never shared) are created simultaneously via a special algorithm such as RSA. Data to be securely transmitted is first encrypted by the sender, using the receiver’s public key, acquired either through a registration authority (RA) or otherwise. When the encrypted data is received, the receiver decrypts the data by using its private key. SQL Server provides the DDL statement CREATE CERTIFICATE for creating certificates. This statement provides several methods for doing so, but in this chapter you use it only to create self-signed certificates—that is, certificates signed by their creator, not by a trusted external authority (such as VeriSign), known as a certificate authority (CA). Before you create a certificate, you need to create a master key for the master database (just as you did with AdventureWorks2008 and XCatMgmt) that can be used to protect the private keys you’ll create. You also need to create certificates on master for use with the endpoint you need (as described later in this section). Note that you have to perform this step and subsequent steps on all the instances of SQL Server that will be communicating via Service Broker. In the sample code, by convention, the objects created on the first instance all terminate with the string I1, and in the second instance, they terminate with I2. In addition, we refer to the first SQL Server instance as I1 and the second as I2. ptg 1986 CHAPTER 49 SQL Server Service Broker The following T-SQL creates a master key in the master database: USE master GO CREATE MASTER KEY ENCRYPTION BY PASSWORD = ‘masterI1’ Now you can create your first certificate in master on I1. (Note that SUBJECT is a metadata field defined in the certificate standard X.509.) Here’s how you do it: CREATE CERTIFICATE SSBMasterCertI1 WITH SUBJECT = ‘SSBMasterCertOnInstance1’, START_DATE = ‘2008-10-02 07:30:35’; Note: The start date must not be in the future, or the certificate is not useful Next, you need to create a TCP endpoint that enables Service Broker in I1 to communi- cate with Service Broker in I2. (The CREATE ENDPOINT syntax is shown in Chapter 54, “Managing Linked and Remote Servers,” for use in creating web services.) This endpoint uses the keys in the certificate you just created to communicate securely. You use the following code to create the endpoint: CREATE ENDPOINT SSBEndpointI1 STATE = STARTED AS TCP (LISTENER_PORT = 4022) the default SSB port; TCP-based FOR SERVICE_BROKER (ENCRYPTION = REQUIRED, AUTHENTICATION = CERTIFICATE SSBMasterCertI1) GO GRANT CONNECT ON ENDPOINT::SSBEndpointI1 TO [SSBTestUserName] For I2 to encrypt messages using the public key that resides in MasterCertI1, that public key must be exported from the database to a file and then imported into a new certificate on I2. You can use the new BACKUP CERTIFICATE command to accomplish the export to file, and then you can perform the same steps on I2 to import MasterCertI1’s public key into a matching certificate: BACKUP CERTIFICATE SSBMasterCertI1 TO FILE = ‘c:\temp\MasterCertI1_PK.cer’ Now you need to switch over to the second SQL Service instance and run the following code: USE master GO CREATE MASTER KEY ENCRYPTION BY PASSWORD = ‘masterI2’ GO CREATE CERTIFICATE SSBMasterCertI2 WITH SUBJECT = ‘MasterCertOnInstance2’, START_DATE = ‘2008-10-02 07:30:35’; ptg 1987 Service Broker Routing and Security 49 GO CREATE ENDPOINT SSBEndpointI2 STATE = STARTED AS TCP (LISTENER_PORT = 4022) FOR SERVICE_BROKER (ENCRYPTION = REQUIRED, AUTHENTICATION = CERTIFICATE SSBMasterCertI2) GO GRANT CONNECT ON ENDPOINT::SSBEndpointI2 TO [SSBTestUserName] GO BACKUP CERTIFICATE SSBMasterCertI2 TO FILE = ‘c:\temp\SSBMasterCertI2_PK.cer’ GO CREATE CERTIFICATE SSBMasterCertI1 AUTHORIZATION [SSBTestUserName] FROM FILE = ‘C:\temp\SSBMasterCertI1_PK.cer’ The last statement in this code creates a certificate on I2 that has the same name and contains the same public key as SSBMasterCertI1. It also performs an essential function: it assigns that public key to SSBTestUserName by using the AUTHORIZATION keyword. Now you can return to the first instance to do the same there, using SSBMasterCertI2’s public key: CREATE CERTIFICATE SSBMasterCertI2 AUTHORIZATION [SSBTestUserName] FROM FILE = ‘C:\temp\SSBMasterCertI2_PK.cer’ Note that you set up SSBTestUserName as a domain user and then add this user as a login with db_owner permissions on both instances. You don’t need to create SSBTestUserName in XCatMgmt because this user is already (or should be) there. For purposes of this discus- sion, you can assume that both instances can use the path c:\temp. The next step is to create a new database on I2 called XBikeDistribution. The concept behind this database is that XBikeDistribution is a subscriber to one of the catalogs published by XCatMgmt. When a product in that catalog changes, XBikeDistribution needs to know about it. You can use the following code to create this database so that it is as lean as possible: USE master GO CREATE DATABASE XBikeDistribution WITH TRUSTWORTHY ON GO ALTER DATABASE XBikeDistribution SET ENABLE_BROKER GO USE XBikeDistribution GO ptg 1988 CHAPTER 49 SQL Server Service Broker CREATE MASTER KEY ENCRYPTION BY PASSWORD = ‘XBikeDistributionPW’ GO CREATE SCHEMA Cataloging CREATE TABLE Cataloging.CatalogSimple ( CatalogId int IDENTITY(1,1) PRIMARY KEY, CatalogXml xml NOT NULL, LastUpdateDate datetime DEFAULT GETDATE() ) GO CREATE USER [SSBTestUserName] The next step is to add the XML schema collection you used in previous examples ( GenericAcknowledgementSchema; found in Listing 49.2) to XBikeDistribution. Then you need to set up certificates for use in the Service Broker dialogs that will take place between the catalog change service running in XCatMgmt and a new service you need to set up in XBikeDistribution. On I2, you run the following code to set up a certificate for use in Service Broker conversations: CREATE CERTIFICATE SSBDialogCertI2 WITH SUBJECT = ‘SSBDialogCertOnInstance2’, START_DATE = ‘2008-10-02 07:30:35’ ACTIVE FOR BEGIN_DIALOG = ON; GO BACKUP CERTIFICATE SSBDialogCertI2 TO FILE = ‘c:\temp\SSBDialogCertI2_PK.cer’ Now you need to switch back to I1, change to XCatMgmt, and create two more certificates, one for each side of the exchange: USE XCatMgmt GO CREATE CERTIFICATE SSBDialogCertI1 WITH SUBJECT = ‘SSBDialogCertOnInstance1’, START_DATE = ‘2008-10-02 07:30:35’ ACTIVE FOR BEGIN_DIALOG = ON; GO BACKUP CERTIFICATE SSBDialogCertI1 TO FILE = ‘c:\temp\SSBDialogCertI1_PK.cer’ GO CREATE CERTIFICATE SSBDialogCertI2 AUTHORIZATION [SSBTestUserName] FROM FILE = ‘C:\temp\SSBDialogCertI2_PK.cer’; Note that the second certificate in this code contains the public key you just created and exported to file in SSBDialogCertI2. ptg 1989 Service Broker Routing and Security 49 Now you need to return to I2 and import the public key in SSBDialogCertI1 and associ- ate it with SSBTestUserName: CREATE CERTIFICATE SSBDialogCertI1 AUTHORIZATION [SSBTestUserName] FROM FILE = ‘C:\temp\SSBDialogCertI1_PK.cer’; By the time you’re done creating all these certificates, you should have the following .cer files in your directory: SSBMasterCertI1_PK.cer, SSBMasterCertI2_PK.cer, SSBDialogCertI2_PK.cer, and SSBDialogCertI1_PK.cer. By now, the pattern of certificate creation and public key user association should be quite clear. The next step is to set up the Service Broker constructs. You need to start on I2 in XBikeDistribution because it has no constructs yet. First, you create the GenericAcknowledgementSchema XML schema collection in XBikeDistribution, as shown earlier in Listing 49.2. Note that in this Service Broker conversation, a service in XCatMgmt plays the role of initiator, and a service in XBikeDistribution is the target. (Because you’ve seen this all before, the following examples lump a lot of the DDL together.) Next, you need to switch to I2 and run the code in Listing 49.6. LISTING 49.6 T-SQL for Creating All the Service Broker Constructs on a Second SQL Server Instance USE XBikeDistribution GO CREATE MESSAGE TYPE [//samspublishing.com/SS2008/SSB/MessageTypes/CatalogChangeNotifySimple] AUTHORIZATION [SSBTestUserName] VALIDATION = WELL_FORMED_XML; GO CREATE MESSAGE TYPE [//samspublishing.com/SS2008/SSB/MessageTypes/GenericAck] AUTHORIZATION [SSBTestUserName] VALIDATION = VALID_XML WITH SCHEMA COLLECTION GenericAcknowledgementSchema GO CREATE CONTRACT [//samspublishing.com/SS2008/SSB/Contracts/SimpleCatalogNotifyContract] AUTHORIZATION [SSBTestUserName] ( [//samspublishing.com/SS2008/SSB/MessageTypes/CatalogChangeNotifySimple] SENT BY INITIATOR, [//samspublishing.com/SS2008/SSB/MessageTypes/GenericAck] SENT BY TARGET ) GO CREATE PROC Cataloging.CatalogChangeNotifyQueueReader ptg 1990 CHAPTER 49 SQL Server Service Broker AS GO CREATE QUEUE Cataloging.CatalogChangeNotifyReceiveQueue WITH STATUS = ON, ACTIVATION ( STATUS = ON, PROCEDURE_NAME = Cataloging.CatalogChangeNotifyQueueReader, MAX_QUEUE_READERS = 10, EXECUTE AS ‘SSBTestUserName’ ) GO CREATE SERVICE [//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyTarget] ON QUEUE Cataloging.CatalogChangeNotifyReceiveQueue ( [//samspublishing.com/SS2008/SSB/Contracts/SimpleCatalogNotifyContract] ) GO CREATE ROUTE [//samspublishing.com/SS2008/SSB/Routes/RouteToCatalogChangeNotifyInitator] WITH SERVICE_NAME = ‘//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyInitiator’, ADDRESS = ‘TCP://192.168.22.5:4022’; GO CREATE REMOTE SERVICE BINDING [//samspublishing.com/SS2008/SSB/RSBindings/RSBForCatalogChangeNotifyInitator] TO SERVICE ‘//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyInitiator’ WITH USER = [SSBTestUserName], ANONYMOUS = OFF GO GRANT SEND ON SERVICE::[//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyTarget] TO [SSBTestUserName] Listing 49.7 contains all the DDL you need to create the complementary Service Broker objects in XCatMgmt on I1. LISTING 49.7 T-SQL for Creating All the Service Broker Constructs on the First SQL Server Instance USE XCatMgmt GO CREATE MESSAGE TYPE ptg 1991 Service Broker Routing and Security 49 [//samspublishing.com/SS2008/SSB/MessageTypes/CatalogChangeNotifySimple] AUTHORIZATION [SSBTestUserName] VALIDATION = WELL_FORMED_XML; GO CREATE CONTRACT [//samspublishing.com/SS2008/SSB/Contracts/SimpleCatalogNotifyContract] AUTHORIZATION [SSBTestUserName] ( [//samspublishing.com/SS2008/SSB/MessageTypes/CatalogChangeNotifySimple] SENT BY INITIATOR, [//samspublishing.com/SS2008/SSB/MessageTypes/GenericAck] SENT BY TARGET ) GO CREATE PROC Publication.CatalogChangeNotifyAckQueueReader AS GO CREATE QUEUE Publication.CatalogChangeNotifyAckQueue WITH STATUS = ON, ACTIVATION ( STATUS = ON, PROCEDURE_NAME = Publication.CatalogChangeNotifyAckQueueReader, MAX_QUEUE_READERS = 10, EXECUTE AS ‘SSBTestUserName’ ) GO CREATE SERVICE [//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyInitiator] ON QUEUE Publication.CatalogChangeNotifyAckQueue ( [//samspublishing.com/SS2008/SSB/Contracts/SimpleCatalogNotifyContract] ) GO CREATE ROUTE [//samspublishing.com/SS2008/SSB/Routes/RouteToCatalogChangeNotifyTarget] WITH SERVICE_NAME = ‘//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyTarget’, ADDRESS = ‘TCP://192.168.22.6:4022’; GO CREATE REMOTE SERVICE BINDING [//samspublishing.com/SS2008/SSB/RSBindings/RSBForCatalogChangeNotifyTarget] TO SERVICE ‘//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyTarget’ WITH USER = [SSBTestUserName], ANONYMOUS = OFF GO ptg 1992 CHAPTER 49 SQL Server Service Broker GRANT SEND ON SERVICE::[//samspublishing.com/SS2008/SSB/Services/CatalogChangeNotifyInitiator] TO [SSBTestUserName] If you look closely at Listings 49.6 and 49.7, you see that they introduce two new Service Broker constructs: routes and remote service bindings. Building Routes to Map Conversations Between SQL Server Instances Routes are used to map a Service Broker service to a TCP address for use when conversing between different SQL Server instances. They provide a useful level of abstraction because the address of a route can be changed without having to change any of the constructs that depend on it. In this way, it is possible to change a route and, as long as the message types, required XML schema collections, contracts, queues, and services exist in the data- bases on the machines involved, only the route needs to be updated. Routes can also be used when only a single SQL Server instance is involved so that it can easily be changed to work when a new instance comes into play. NOTE For the routes in this example to work, the TCP port specified earlier in CREATE END- POINT (port 4022) must be open on any firewalls involved. The routes created in Listings 49.6 and 49.7 are thus necessary because the initiator needs to be able to locate the target service to send messages, and the target service needs to be able to do the same. Creating Remote Service Bindings for Conversations When initiating a conversation with a service on a nonlocal SQL Server instance, you need to create a remote service binding. A remote service binding associates a local database user with a remote service. When messages are transmitted between the instances, Service Broker encrypts them, using the public key of the local user’s certificate. They are then decrypted on the other end, using the private key. Because you have created matching pairs of certificates and associated them with the user in the master database and in all participating databases on both instances (and you have also turned off anonymous access), you can be certain that communications will happen securely over the network. A Final Note on the Sample System If you like, you can create the initiator and target service programs for the final example in this chapter. For example, you could create an update trigger (as the initiator) on XCatMgmt.Publication.ProductCatalog that sends a CatalogChangeNotifySimple well- formed XML message to XBikeDistribution.Cataloging.CatalogChangeNotifyReceiveQueue. You fill in the body of the target’s activated procedure XBikeDistribution.Cataloging.CatalogChangeNotifyQueueReader to receive these ptg 1993 Troubleshooting SSB Applications with ssbdiagnose.exe 49 messages and reply by sending a message of type GenericAck to the existing Production.CatalogChangeAckQueue. Remember that you need to acquire the service_broker_guid of the remote SQL Server instance’s Service Broker instance to send messages successfully. Also, you need to be sure to set the ENCRYPTION flag to ON (the default) when calling BEGIN DIALOG CONVERSATION. Troubleshooting SSB Applications with ssbdiagnose.exe SQL Server 2008 comes with a new command-line application, ssbdiagnose.exe.Youuse this advanced utility to monitor SSB conversations or to check application configuration status, both across databases and between computers. It supports a variety of authentication and configuration options and generates either plain text or XML as its output. The general syntax for ssbdiagnose.exe has three parts: . Output format and error level . Connection information . Application-specific settings You can leverage the full syntax of ssbdiagnose.exe by simply executing it from the command line with no options (it is located in the folder [SQL Server installation root]\[version number]\Tools\Binn). To run it, open a command window (run the CMD.exe program) and then navigate to your installation location using the CD command. As an exercise, you can execute any of the examples that follow. In the following example, you connect to the current SSB application (as developed in Listings 49.1 through 49.7) and check on the services’ configuration status. Remember that the application lives in two databases ( AdventureWorks2008 and XCatMgmt), so you need to provide parameters to check on both sides of the service contract. Because the service and contract names are quite long, we suggest enclosing the command-line contents in a batch file and running it from there. Execute the following command, substituting connection credentials with those appropri- ate for your environment: SSBDiagnose.exe -U SSBTestUserName -P SSBTestUserName CONFIGURATION FROM SERVICE “//samspublishing.com/SS2008/SSB/Services/CatalogChangeInitiatorService” -S localhost\SQL08 -d AdventureWorks2008 TO SERVICE “//samspublishing.com/SS2008/SSB/Services/CatalogMaintenanceService” -S localhost\SQL08 -d XCatMgmt ON CONTRACT “//samspublishing.com/SS2008/SSB/Contracts/BasicCatalogChangeContract” If you’ve executed all the sample code and listings in this chapter, you should see no errors in the ssbdiagnose.exe output. For troubleshooting, refer to the next section to learn how to explore the system catalogs for SSB. . with I2. In addition, we refer to the first SQL Server instance as I1 and the second as I2. ptg 1986 CHAPTER 49 SQL Server Service Broker The following T -SQL creates a master key in the master database: USE. SERVICE “//samspublishing.com/SS2008/SSB/Services/CatalogChangeInitiatorService” -S localhost SQL0 8 -d AdventureWorks2008 TO SERVICE “//samspublishing.com/SS2008/SSB/Services/CatalogMaintenanceService” -S localhost SQL0 8. 49.6. LISTING 49.6 T -SQL for Creating All the Service Broker Constructs on a Second SQL Server Instance USE XBikeDistribution GO CREATE MESSAGE TYPE [//samspublishing.com/SS2008/SSB/MessageTypes/CatalogChangeNotifySimple] AUTHORIZATION