Link to Part 1, Part 3, Part 4
Architecture of application
I chose to initially to target a windows smart client application for the project. All the guts would be held in different assemblies as libraries so that it could be migrated quickly to a web app should the need arise.
WCF to access Oracle database
I wanted to abstract the data sources away from the application as much as I could without getting too carried away with it. The Oracle CMS user accesses their data through a WCF service hosted by Windows Activation Service (WAS), part of the goodness that is IIS7. Putting CMS behind this facade allows the protocol to be whatever we feel like and makes for a lovely clean interface to work with.
Thus we end up with a WCF library and hosting service project for CMS together with installer project for actually installing it.
WCF to access the website SQL server database
I following the pattern from the CMS data, I chose to also use a WCF service to font the website database. This service allows the basic CRUD operations on the HTML descriptions for the products.
Thus we end up with a WCF library and hosting service project for the website and installer project for installing it.
We then just add in the library for housing the code that will do the hard work of getting the CMS descriptions and converting them to HTML. Also the installers for the WCF services and hosting.
Windows smart client forms application
Finally we have the project where the windows forms application lives, I was deploying this using click once, as I love that technology especially for internal applications.
This now looks like this:
Getting the CMS data
A couple of WCF services were required.
- Query CMS for the available publications. This would be presented as a drop down list of publications in the application to select from.
- Get the description field for an item group based on the CMS item group ID. Including last modified date.
Coding got bogged down producing the data access code as things didn’t work as expected.
I remembered after working on an internationalised ASP.NET website, a line from Joel’s article came back to my mind, “There Ain't No Such Thing As Plain Text.”. A quick Google for the article;
The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets
, helped a lot in getting to grips with the issues.
The item descriptions in CMS have over the years been pasted in from Linux, Windows and Mac computers, the system itself has been used on both mac and PC clients in the past causing encodings to be all over the place. The byte order mark (BOM) didn’t seem to relate to any reference anywhere on the internet, as you can see from below the byte order mark (BOM) is stripped from the start as it is no good to me. I end up treating the lot as 1250 encoding.
Null strings and string methods
Watch out for null string terminators getting into the description. The character chr(0) is used to indicate the end of a file, delimit what you are interested in. In the Oracle field I didn’t check for it and ended up in my application getting odd behaviour in string methods. Took me ages to cotton on to what had happened.
As you can see below chr(0) is taken out now. Of course the encodings and presence of the chr(0) varied from group to group that delayed tracking it down.
1: Dim DataReaderItemData As OracleDataReader = _
3: If DataReaderItemData.Read() AndAlso Not DataReaderItemData.IsDBNull(0) Then
4: Dim ItemDescription As OracleBinary = DataReaderItemData.GetOracleBinary(1)
5: Dim encGBK As System.Text.Encoding = System.Text.Encoding.GetEncoding(1250)
6: ' Me.ItemGroupData = New ItemGroup(encGBK.GetString(ItemDescription.Value).Remove(0, 4), DataReaderItemData.GetOracleDateTime(2))
7: Me.ItemGroupData = New ItemGroup(System.Text.Encoding.Default.GetString(ItemDescription.Value).Remove(0, 4), DataReaderItemData.GetOracleDateTime(2))
8: If Me.ItemGroupData.ItemGroupDescrption.IndexOf(Chr(0)) <> -1 Then
9: Me.ItemGroupData.ItemGroupDescrption = _
11: End If
14: Me.ItemGroupData = Nothing
15: End If
The above exert is just an example, it needs more resource protection if you are using it in production code (usings).
Has it changed?
The application ultimately has to let the user know if a source item group has changed from the last time it was released to the website. Although there is a last modified date on the ITEMGROUPATTRIBUTES_TEXTDATA table it can’t be trusted as a careless sql script ran against the table or a third party add in to cms might not observe updating this reliably. Call it paranoia, a better indicator was required.
The item group information from the Oracle database is loaded into an itemgroup class. This class calculates an MD5 checksum on the description field from CMS and stores it. This checksum eventually goes to the website when the item group is released to the web. Thus it can be later compared against the database to see if any text has changed.
1: Private Function GetMD5Hash(ByVal SourceText As String) As Integer
2: 'Create an encoding object to ensure the encoding standard for the source text
3: Dim Ue As New UnicodeEncoding()
4: 'Retrieve a byte array based on the source text
5: Dim ByteSourceText() As Byte = Ue.GetBytes(SourceText)
6: 'Instantiate an MD5 Provider object
7: Dim Md5 As New MD5CryptoServiceProvider()
8: 'Compute the hash value from the source
9: Dim ByteHash() As Byte = Md5.ComputeHash(ByteSourceText)
10: 'And convert it to String format for return
11: Return BitConverter.ToInt32(ByteHash, 0)
12: End Function
Getting and Setting the website data
The web site required a service that would allow the current item group description to be retrieved for a particular CMS item group ID so the published html could be viewed in the application. It also had to allow new versions to be saved. The MD5 hash value and the last modified date from CMS are also stored allowing comparisons to indicated changed groups in the future.
Changed groups have to be approved and released. The previous versions are archived by SQL server on a table trigger.
read part 3