Recent Posts
Categories
Join 3 other subscribers
Blog Stats
- 4,760 hits
Enterprise Applicaiton Integration and SOA 2.0
We are using NServiceBus and the awesome new suite of monitoring tools, and go-live is just around the corner. We are hosting our audit and error queues on a dedicated audit server, as recommended, along with ServiceControl and ServicePulse. How do we configure authorization for the ServicePulse website to allow a select group of IT Ops users to access the site without opening up access to the whole company?
By default, ServicePulse runs as a self hosted web server with no option to add authentication or authorization:
However, ServicePulse also has a feature for extracting website files to a folder, like this:
C:\Program Files (x86)\Particular Software\ServicePulse>ServicePulse.Host.exe --extract --serviceControlUrl="http://localhost:33333/api" --outPath="C:\temp\SpWeb"
This enables you to create your own IIS website with a few clicks:
And now you have an IIS-hosted ServicePulse website to which you can add Windows auth or another authentication and authorization mechanism:
Unfortunately, Particular Software does not yet provide a means for enabling user-level authorization on the ServiceControl REST API, so the options for accessing ServiceInsight are:
Neither of these options feel very satisfying to me. Please add any thoughts and suggestions here: https://github.com/Particular/ServiceControl/issues/400
If you are setting up a new NServiceBus installation or are upgrading to the Particular Platform from an older version of NServiceBus, I hope this post helps you secure your ServicePulse dashboard.
I have been guilty of severely underestimating the time required to integrate software applications – think order of magnitude. This post lists questions that I would have asked, in hindsight, to flesh out integration estimates, since the more line-items an estimate contains, the higher, and consequently more accurate, the estimate.
These questions originated when developing a greenfield application that communicated with an existing system. There are additional considerations when integrating multiple greenfield or multiple existing systems, but many of the questions in this post still apply. The questions below refer to the greenfield application as “our application”, and the existing application as the “external system”.
This post is intended to be a starting point, spurring one’s imagination to the range of possible considerations when estimating effort levels in software integration. I still strongly recommend including additional estimate buffer, since Hofstadter’s_Law certainly applies.
Advice: Don’t re-invent the wheel. Integration services like NServiceBus (simple, low-cost, yet powerful), BizTalk (complex, high-cost), and others include out-of-the-box capabilities for publish-subscribe, asynchronous messaging, automatic retries, failed-message re-submission, and health monitoring.
This post outlines my efforts to upgrade a BizTalk application that calls a deprecated Web Service Enhancements (WSE) web service.
This post covers creating a BizTalk 2010 WCF send port and required pipeline components to call a WSE web service. In my scenario, I used a two-way WCF-Basic send port subscribing to messages coming from a receive location. The response returned from the WSE web service contained an escaped XML payload, so I wrote a custom pipeline component (perhaps fodder for a future blog post) to un-escape the XML. The response also contained a success/error status field, which I promoted. I created a send port subscribing to the Success status, and an orchestration subscribing to the Error status for generating ESB fault messages.
My first step in replicating WSE send-port functionality with WCF was to try capturing an HTTP Post from my BTS 2006 WSE send port.
<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header> <wsa:Action>http://myaction.com/webservices/MyAction</wsa:Action> <wsa:MessageID>uuid:5dcdf45c-bee9-482a-bfbe-4d189d5f2a6b</wsa:MessageID> <wsa:ReplyTo> <wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address> </wsa:ReplyTo> <wsa:To>http://mysite.com/myfolder/webservices/myservice.asmx</wsa:To> <wsse:Security soap:mustUnderstand="1"> <wsu:Timestamp wsu:Id="Timestamp-45641c38-06a4-4b44-b382-6d35471e275f"> <wsu:Created>2013-07-01T21:39:26Z</wsu:Created> <wsu:Expires>2013-07-01T21:44:26Z</wsu:Expires> </wsu:Timestamp> <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SecurityToken-a4c6efe3-4d17-47f8-8d1a-d3239fe3cdcf"> <wsse:Username>myUserName</wsse:Username> <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">MyPassword</wsse:Password> <wsse:Nonce>VOgPryrIoe8saLDE8wB6vg==</wsse:Nonce> <wsu:Created>2013-07-01T21:39:26Z</wsu:Created> </wsse:UsernameToken> </wsse:Security> </soap:Header> <soap:Body> <ns0:Export xmlns:ns0="http://mySite.com/webservices/"> <ns0:xmlInputDocument><?xml version="1.0" encoding="utf-8" ?><moreStuff /></ns0:xmlInputDocument> </ns0:Export> </soap:Body> </soap:Envelope>
I then generated schemas and bindings, and tested hitting the WSE web service with a WCF adapter to see how far off from the above format I was.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
(no <header> element here)
<s:Body>
<ns0:Export xmlns:ns0="http://mysite.com/webservices/">
<ns0:xmlInputDocument><?xml version="1.0" encoding="utf-8" ?><moreStuff /></ns0:xmlInputDocument>
</ns0:Export>
</s:Body>
</s:Envelope>
The next step was to generate the SOAP headers, then get the WCF adapter to include the headers in the request it generates.
I created a custom “assemble” pipeline component for generating SOAP headers and including them in the message context.
<wsa:Action>http://mysite.com/webservices/Export</wsa:Action><wsa:ReplyTo><wsa:Address>http://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymous</wsa:Address></wsa:ReplyTo><wsa:To>http://mysite.com/mywebservices/webservices/readingmanager.asmx</wsa:To>
// http://www.microsoft.com/downloads/en/details.aspx?FamilyID=018a09fd-3a74-43c5-8ec1-8d789091255d&displaylang=en using Microsoft.Web.Services3.Security.Tokens; public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext pc, Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg) { string namespaces = "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " + "xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" " + "xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" " + "xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\""; string timestamp = String.Format("<wsu:Timestamp wsu:Id=\"Timestamp-{0}\">" + "<wsu:Created>{1}</wsu:Created><wsu:Expires>{2}</wsu:Expires></wsu:Timestamp>", Guid.NewGuid(), DateTime.UtcNow.ToString("s") + "Z", DateTime.UtcNow.AddMinutes(5).ToString("s") + "Z"); var token = new UsernameToken(this.Username, this.Password, PasswordOption.SendPlainText); // http://blog.benpowell.co.uk/2010/11/supporting-ws-i-basic-profile-password.html string usernameToken = token.GetXml(new XmlDocument()).OuterXml; // remove namespace definitions since we'll define them in the <header> node below usernameToken = Regex.Replace(usernameToken, " xmlns:[^=]+=\"[^\"]+\"", ""); string securityXml = "<wsse:Security s:mustUnderstand=\"1\">" + timestamp + usernameToken + "</wsse:Security>"; string header = string.Empty; // add header fields from config header += this.AdditionalHeaderElementText; header += "<wsa:MessageID>uuid:" + inmsg.MessageID + "</wsa:MessageID>"; header += securityXml; header = String.Format("<headers {0}>{1}</headers>", namespaces, header); // Write the OutboundCustomHeaders property to the message context (distinguish it) so the WCF adapter will add these values to the SOAP header // http://blogical.se/blogs/mikael_sand/archive/2012/07/06/setting-custom-soap-headers-in-the-wcf-adapter.aspx inmsg.Context.Write("OutboundCustomHeaders", "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties", header); return inmsg; }
It would be ideal to upgrade WSE web services to WCF to improve performance. If, however, you are stuck calling a WSE web service and you want to use a BizTalk WCF send port, I hope this post helps. Please respond with any questions or comments.
You may be familiar with how NServiceBus immediately re-tries transactions that fail, and the number of re-tries is configurable. This is handy in situations like when a deadlock is hit – the retry will likely succeed:
You are also probably aware that sometimes it takes a few moments for something else to happen in the system before our transaction will succeed. NSB now has a “second-level retry” feature that kicks in when immediate retries are used up: NSB waits a configurable interval (10 seconds in the example below), then re-tries the transaction with a fresh set of immediate re-tries. This is incredibly useful in situations where multiple messages are coming in with dependencies on each other (though a Saga would be better here), or in situation where a dependent system is known to go down for a few minutes or hours. This feature could be used to keep error-queue counts down and reduce support burden:
ReturnMessageToErrorQueue is a command-line tool that ships with NServiceBus and returns all messages from an error queue to their source queues.
Unfortunately the directions are sparse, and my first attempt was a flop:
C:\Projects\NServiceBus\tools>ReturnToSourceQueue.exe
Please enter the error queue you would like to use:
.\private$\myNsbApp.error
Please enter the id of the message you'd like to return to its source queue, or 'all' to do so for all messages in the queue.
all
Unhandled Exception: System.Messaging.MessageQueueException: Format name is invalid.
at System.Messaging.MessageQueue.MQCacheableInfo.get_Transactional()
at System.Messaging.MessageQueue.get_Transactional()
at NServiceBus.Tools.Management.Errors.ReturnToSourceQueue.ErrorManager.set_InputQueue(Address value)
at ReturnToSourceQueue.Program.Main(String[] args)
Here is the correct syntax:
C:\Projects\NServiceBus\tools>ReturnToSourceQueue.exe
Please enter the error queue you would like to use:
myNsbApp.error@SERVERNAME
Please enter the id of the message you'd like to return to its source queue, or 'all' to do so for all messages in the queue.
all
Attempting to return message to source queue. Queue: [myNsbApp.error@servername], message id: [all]. Please stand by.
Success.
Success.
Success.
Udi, please update the directions!
If you find yourself publishing a message called IMyObjectUpdated, you are likely violating service boundaries.
Why? Because publishing a message every time your object changes is a sure sign that your other services are saving representations of your object. In other words, you have duplicated data between services, and to stay in sync, you have to publish DTO-style messages every time your object changes. Data duplication indicates that your services are not fulfilling their role of being fully responsible for the business capability they implement.
Instead of publishing an IEmployeeUpdated message containing every field on the Employee entity, you should be publishing events like IEmployeeCreated, IEmployeeFired, and IEmployeePromoted. Each of these event messages should only contain the employee id and one or two other fields.
Pay close attention to how much data is contained in messages that get shared between services. If your services are publishing messages with more data than IDs and dates, then it is high time to re-evaluate your service design.
Here is a link to the Yahoo group for alumni of Udi’s course. Peruse this group for practical SOA discussion material:
http://tech.groups.yahoo.com/group/AdvancedDistributedSystemsDesign/
Webservices often come to mind when we hear Service-Oriented Architecture: webservices implementing interfaces connecting a web tier, an application tier, and a data tier, or some variation. In the SOA world, the concept of a service is an entirely different animal.
An SOA “system” is composed of multiple autonomous “services” that communicate asynchronously. Each service has UI logic, business logic, and some means of storing data. Yes, you heard that right – services to not share a common data store; they are each responsible for their own data. Udi Dahan defines a service as, “the technical authority for a specific business capability”, and specifies that “all data and business rules reside within the service” (Advanced Distributed Systems Design course-slides).
Asynchronous communication in SOA consists of services publishing events to which other services subscribe. If it appears that service A needs to synchronously access service B’s data as part of its business logic, then the service boundaries should be re-evaluated. It is probable that A and B are either managing the same business capability and should be combined, or that we technologists have synchronously chained steps of a business process into a transaction, and we need to split up the steps add re-evaluate the role of time in the business process.
Attributes of a service
Examples of services
System: Online book-sales website
Possible service breakdown:
System: Hotel reservation system
Possible service breakdown:
Summary
Disclaimer: I do not claim to be an expert in the subject of SOA, so these are just thoughts based on my experiences with building my first SOA system over the past year plus some theory assimilated from attending Udi Dahan’s Advanced Distributed Systems Design course.
More to come if I have time!
Recent Comments