Book Review: Enterprise Application Architecture
We still have one other of sphere toconsider. Let's call that sphere the Client sphere. If you go back toChapter 3 where we talked about the server farm you will see that as wemoved away from the database we were able to add more and more machinesto each sphere. Well, the Client sphere is no different. Depending uponthe number of machines that you have used to create your server farm,you should be able to handle thousands of client machines in the Clientsphere.
As I hinted to earlier although our webapplications can be run in an IE 4.0 or higher browser, there are quitea few reasons for using another vehicle to launch these applications
Even though the web really just givesanother way to handle our job of safeguarding and managing our user'sinformation for them, many people see the web as something inherentlyevil and untrustworthy. It is still a little difficult to convince somepeople that the work we do with a web application is the same as thework they do with a standard client-server interface. I think they havethe perception that all of our customers are out having a good timesurfing the web rather than doing any serious work.
The web-based applications we deliver aredesigned to put an end to this silliness. We understand that we stillneed to battle a lot of misconceptions, so we make an all out effort toremove as much webbiness from our applications as possible. We buildinterfaces that resemble standard client-server applications in everyway. Other than a couple of quirky HTML leftovers like the ability toselect everything on a page, it is nearly impossible to tell thedifference between our web-based applications and a similarly designedVB interface. The only thing that resembles a web page in ourapplications is the browser. And, because we are nothing if notthorough, we even found a way to hide that from the naysayers. We use awrapper for the IE browser that allows us to use its functionalitywithout having to deal with all-of-its standard user interfacecapabilities. This wrapper is called the Launch Pad application, andwe'll see how to build it shortly.
Corral the User
There are really a couple of very soundreasons to direct our users to our applications through a specializedbrowser besides the sentiments of the anti-web group. The most importantis that standard browsers give the users a little too much control overtheir next action. Let me explain while it is fine for Wally the WebSurfer to change his mind in mid-stream and go to another web site, thiskind of indecisiveness is not alright for Eddie the Employee. In aweb-based application, it is important for the user to control thedirection of the application from within the page. If we expect the userto press one of two or three command buttons and the user decides totype in the address of the sports page instead or to press the backbutton 6 or 7 times, we have no way of stopping him. Of courseeverything in our installation is designed to ensure that our dataretains all of its integrity and that it is safe, but we have failed tocreate an application that helps (guides) the user through the processhe is trying to accomplish.
This means we build a client-sideapplication that is really nothing more than a wrapper for MicrosoftInternet Explorer. Of course it is not necessary to use this applicationto deploy web-based applications, but it does offer several advantages.It allows us to remove all external browser-based controls from theuser. This means that we can force the user to use the controls providedon the page to work with the information in the application. This givesus a tighter reign on the user's activities while they are workingwithin the application. This makes it easier for us to control theprocessing order within an application. It also allows us to remove theaddress text box from the user's view. This measure gives an additionallayer of security. It means that rather than allowing the user tonavigate at will throughout our application server (IIS), we can useLaunch Pad to provide them with a standard pull-down menu of optionsthat appears identical to any standard VB application.
The pull-down menu that is used in LaunchPad is populated at run time. In other words, as soon as a user opens upthe Launch Pad application, we perform a query that returns a collectionof applications that this particular user is allowed to access. Thefirst level of security begins here. If the user is not an authorizeduser for the domain or a trusted domain, the query will not be executed.Assuming that the user is authorized and the query does execute, we takethe resulting collection and use it to populate the pull-down menus inLaunch Pad. That means that we can control user access to eachapplication from a centrally managed database application that worksexactly the same way all of the other security measures in Windows NTare managed.
We treat each application as an objectand then we either allow or deny access on a user-by-user basis. This isthe second stage of security. Notice that the levels of security tightenrather than lessen as the user is allowed in closer to the enterprise'sdata. Remember that we learned techniques earlier that allowed us toexercise a control at the user/property user/method level. Remembertoo, that this control is handled in exactly the same way that all otherWindows NT security is handled. In other words, we treat the propertiesand methods as objects and then grant control of those objects on auser-by-user basis.
The Launch Pad Application
Launch Pad gives us a framework uponwhich to deliver a top-notch help system. In my installations, LaunchPad is the preferred medium for delivering applications. It providesstructure and helps the users to learn to work with the application fromwithin the page. However, if the user base is sophisticated enough or have had enough time with the application we also offer theoption of accessing the application directly through the browser. Thisis a boon for those individuals that are on the road and do not havetheir own computers with them.
Building The Launch Pad Application
The Launch Pad application that we'll bebuilding here is really a cut down version of the one I usuallydistribute. This version has all the core functionality but I normallyoffer some additional tricks such as Help and Reporting features.
For the most part, this application isvery simple. The only real trick is the coding or maybe I should saythe concept required to communicate from within an ASP and ahost application written in VB.
Open up a new Standard EXE project in VB.Add one MDI Form, 2 standard forms, and one module to the project. Namethe project LaunchPad.vbp and rename the forms and module as indicated.Set frmBrowser as an MDIChild. When you get finished the ProjectExplorer in the VB IDE should look something like this:
Insert Image 36
Let's work through the code we need toadd to the General module.
General Module Code
This code is really quite simple. All weare doing is creating 5 Public variables that will be used throughoutthe life of the application. The MainForm and UserLogon variables areself-explanatory. strDispatcher is used to hold the address for the ASPfiles on the IIS server so that we don't need to hard code this into ourapplication. We'll be using the registry to set this variable in theSplash form. The additional variables are string arrays that will beused to dynamically create the pull-down menus for the application:
Option ExplicitPublic strDispatcher As StringPublic MainForm As frmMainPublic UserLogon As StringPublic ApplicationAddresses() As StringPublic CurrentApplication() As String
The only other thing in this module isthe Sub Main routine. It is self-explanatory, but what it does is veryimportant. As we will learn in the next section, we use the Splash formto initialize this application.
Sub Main()frmSplash.ShowEnd Sub
You'll also need to specify Sub Main asthe Start Up object in the Project Properties dialog.
The Splash Form
As I said earlier, the Splash form isreally responsible for initializing the Launch Pad Application. The wayit does this is probably the most important concept in this section ofthe book. The Splash form contains a web browser that sends a singleparameter and retrieves a single page from the IIS machine. Theparameter it passes is the user's NT Logon ID and the page that isreturned contains a list of applications that user has permission toaccess.
All you need to do to create a browser isto draw a browser control on any form. How easy is that? The control youneed to add to your project in order to perform this miracle is calledMicrosoft Internet Controls. Microsoft has changed the name of thiscontrol several times, but so far the SHDOCVW.dll has remained more orless constant:
Insert Image 37
Add a timer control, a web browsercontrol, and a picture box to the form:
Insert Image 38
The timer is called tmrTimer and has anInterval of 5; the browser is called brwWebBrowser and the picture boxpicSplash.
The picture box has been added to giveyou a place to put something nice for the user to look at while theapplication is configured. Of course it is also possible, and maybepreferable, to eliminate this picture box control and allow the webbrowser to be visible. Then you could specify what the splash screenwould look like from the server by changing the looks of the ASP pagethat is used for the splash screen. We use a picture box because it isimmediate and as an organization we have decided upon a specific lookfor all our applications.
Remember that this screen calls an ASPpage with a single parameter the currently logged in user's NT LogonID. In order to accomplish this task, the Splash form must learn thename of the authorized user. It does that by using a single API callGetUserName. We covered techniques you can use to make API calls inChapter 14. Remember that the first step was to create a declarationstatement:
Option ExplicitPrivate Declare Function GetUserName Lib "advapi32.dll" _ Alias "GetUserNameA" _ (ByVal lpbuffer As String, nSize As Long) As Long
The next bit of code we have to write isthe Form_Load routine. In this routine we determine the name of the userusing our API call. If we find a valid user name then we proceed, if notwe just end the application. If we have found a user Logon ID we appendthat value to the address of the page designed to return a list ofapplications for this user. Then we load that page into the browser byusing the brwWebBrowser.Navigate strAddress line.
Notice that we have used a registryentry to store the name of the IIS dispatcher machine. This is toallow us to change the name or IP address of the dispatcher server ifthe need ever arises. Of course, if we followed the principles laidout in Chapter 3, our dispatcher will be a redundant machine that willnever go down.
Private Sub Form_Load()Dim strAddress As StringDim i As LongDim strBuffer As StringDim lngSize As LongstrDispatcher = GetSetting(App.Title, "Settings", _ "Dispatcher", "BTVS")strBuffer = Space$(255)lngSize = Len(strBuffer)Call GetUserName(strBuffer, lngSize)If lngSize > 0 Then ' Place the user's logon into the local memory variable UserLogon = Left$(strBuffer, lngSize - 1)Else ' Every User must have and use a valid Windows NT User Logon ' If not, give 'em the boot EndEnd IfstrAddress = "http://" & strDispatcher & _ "/UserLogonScreen.asp?UserLogon=" & UserLogonOn Error Resume NextIf Len(strAddress) > 0 Then tmrTimer.Enabled = True brwWebBrowser.Navigate strAddressElse EndEnd IfEnd Sub
You should notice that before we calledfor the page using the brwWebBrowser.Navigate strAddress line we enabledthe timer we drew on the form earlier. Let's take a look at the code inthe tick event of the timer to see why. This is the code that reallydoes all of the work. As usual, read through the code, we will go overit in detail below:
Private Sub tmrTimer_Timer()Dim i As IntegerDim lngCount As LongDim ThisObject As ObjectDim strMenu As StringDim aryMenu() As StringOn Error Resume NextIf brwWebBrowser.Busy = False Then tmrTimer.Enabled = False Me.Caption = brwWebBrowser.LocationName For Each ThisObject In brwWebBrowser.Document.All If Left(ThisObject.id, 1) = "A" Then lngCount = lngCount + 1 ReDim Preserve aryMenu(lngCount) strMenu = Right(ThisObject.id, Len(ThisObject.id) - 4) aryMenu(lngCount - 1) = strMenu End If Next ThisObject ReDim ApplicationAddresses(lngCount) ReDim CurrentApplication(lngCount) With frmMain For i = 0 To lngCount - 1 If i = .mnuApplicationOption.Count Then Load .mnuApplicationOption(i) End If .mnuApplicationOption(i).Caption = aryMenu(i) ApplicationAddresses(i) = "Http://" & strDispatcher & _ "//Applications/" & aryMenu(i) & _ "/MainMenu.asp" CurrentApplication(i) = aryMenu(i) Next .Show End With Unload MeElse Me.Caption = "Performing Security Check for ..."End IfEnd Sub
The first real work in this procedure isdone in the following block of code:
For Each ThisObject In brwWebBrowser.Document.All If Left(ThisObject.id, 1) = "A" Then lngCount = lngCount + 1 ReDim Preserve aryMenu(lngCount) strMenu = Right(ThisObject.id, Len(ThisObject.id) - 7) aryMenu(lngCount - 1) = strMenu End IfNext ThisObject
What is happening here is that we areexamining the ASP file that has been delivered in response to therequest we made during the Form_Load. Remember we asked for a list ofapplications that the authorized user has permission to access. The wayit works is as follows: Every tag in a HTML document is considered anobject. All of those objects are stored in the Document.All collection.That means that we can iterate through each object in the document injust the same way we would iterate through any other collection. We haveformatted the ASP Response page by creating a text box for eachapplication. This text box is named by combining the name of theapplication with the prefix "Address". That means that as weiterate through the collection we can identify the objects that containthe name of applications that the authorized user has permission toaccess. As we iterate through the collection, we increase the size ofthe aryMenu string array each time we find a new application. Then weextract the name of the application from the name of the text box andadd it to the array.
After we have finished iterating throughthe document's All collection we re-dimension each of the four arrays wedeclared earlier so that they can hold the necessary information foreach application:
ReDim ApplicationAddresses(lngCount)ReDim CurrentApplication(lngCount)
The next block of code uses theinformation we collected from the document to build the menu options forthe user. In case you are unfamiliar with the VB Load method, the Loadmethod is used to add new controls to a control array at run-time. Inthis case, we are adding a menu option to the Application menu for eachapplication that the authorized user has permission to access:
With frmMain For i = 0 To lngCount - 1 If i = .mnuApplicationOption.Count Then Load .mnuApplicationOption(i) End If
In addition to adding the menu option, wealso build a string that represents a valid address for each menuoption. Then we stuff that string into the appropriate array. This hasthe effect of synchronizing the menu option indexes with the indexes ofthe array containing the addresses. In other words, if I select the menuoption with the Index 3 from the Application menu, the address for theapplication I selected would be stored as the fourth element of theApplicationAddresses array the arrays are zero based so the 4thelement has the Index 3. The same thing holds true for the CurrentApplication array.
.mnuApplicationOption(i).Caption = aryMenu(i) ApplicationAddresses(i) = "Http://" & strDispatcher & _ "//Applications/" & aryMenu(i) & _ "/MainMenu.asp" CurrentApplication(i) = aryMenu(i) Next .ShowEnd With
We'll take a look at what the ASP willbe doing in the next chapter.
The only other routine in this form is atiny block of code that just responds to the NavigateComplete event forthe browser control. When the navigation is complete, it simply sets thecaption on the form equal to the name of the ASP document.
Private Sub brwWebBrowser_NavigateComplete2 _ (ByVal pDisp As Object, URL As Variant)On Error Resume NextMe.Caption = brwWebBrowser.LocationNameEnd Sub
At this point in time, we have configuredthe Launch Pad application for an individual authorized user. It has themenu options that can point this user to the applications that the userhas permission to access. That means that all the user has to do to workwith a web application is to select it from the Applications menu optionin Launch Pad. Let's take a look at the code in the MDI form that causesthis to happen.
The Main MDI Form
The MDI form for Launch Pad is reallyquite simple. It has three main menu options:
- File This is really just used to give us a place to put the exitcommand
- Applications This option is used to allow the user to navigate to a particularapplication.
- Window This just contains all of the standard Window Management commands.It is especially useful because it allows users to flip betweendifferent applications, reports, etc.
Insert Image 39
These three menus also contain thefollowing sub-items:
Insert Image 40
As you can see the Applications menucontains one sub-menu that is a control array. This is the array thatwe loaded from the Splash form.
The first real work gets done in theForm_Load routine. All that we are doing here is retrieving thesettings for the size of the MDI form exactly as the user left themwhen they closed the application the last time. Just in case this isthe first time the program was run and there are no last known valuesstored in the registry, we provide some reasonable defaults as thelast parameter:
Private Sub MDIForm_Load()Me.Left = GetSetting(App.Title, "Settings", _ "MainLeft", 1000)Me.Top = GetSetting(App.Title, "Settings", _ "MainTop", 1000)Me.Width = GetSetting(App.Title, "Settings", _ "MainWidth", 6500)Me.Height = GetSetting(App.Title, "Settings", _ "MainHeight", 6500)End Sub
The flip side of the Form_Load is theForm_Unload subroutine. What we are doing here is either creating orupdating the registry values that represent the size state of theapplication when the user shuts down the application:
Private Sub MDIForm_Unload(Cancel As Integer)If Me.WindowState <> vbMinimized Then SaveSetting App.Title, "Settings", _ "MainLeft", Me.Left SaveSetting App.Title, "Settings", _ "MainTop", Me.Top SaveSetting App.Title, "Settings", _ "MainWidth", Me.Width SaveSetting App.Title, "Settings", _ "MainHeight", Me.HeightEnd IfEnd Sub
Most of the work we need to do in thisapplication is handled in the menu event handlers.
In the ApplicationOptions menu itemcontrol array, we accept an Index that tells us what option the userselected. Then we use that value to retrieve an appropriate addressfrom the ApplicationAddresses array. For housekeeping purposes, we setthe value of the browser's CurrentApplication property to the value ofthe current application as indicated by the index. The last thing wedo before we show the display this instance of the Browser form is tonavigate to the address that the user selected:
Private Sub mnuApplicationOption_Click(Index As Integer)Dim frmAppBrowser As New frmBrowserWith frmAppBrowser .CurrentApplication = CurrentApplication(Index) .DocumentAddress = ApplicationAddresses (Index) .brwWebBrowser.Navigate .DocumentAddress .ShowEnd WithEnd Sub
The following Window handlingsubroutines are self-explanatory:
Private Sub mnuWindowTileVertical_Click()Me.Arrange vbTileVerticalEnd SubPrivate Sub mnuWindowTileHorizontal_Click()Me.Arrange vbTileHorizontalEnd SubPrivate Sub mnuWindowCascade_Click()Me.Arrange vbCascadeEnd Sub
Finally, this is used to close theLaunch Pad Application:
Private Sub mnuFileExit_Click()' Unload the formUnload MeEnd Sub
The Browser Form
This next form we will look at is thefrmBrowser form. This form is incredibly simple. It is really not muchmore than a MDI child form with a browser control placed upon it.
We've already added the browser controlcomponent to the project so just draw a browser on the form. It doesn'tmatter what it looks like. We have to handle the sizing of this controlin a dynamic fashion:
Insert Image 41
As always, we use the declaration sectionof any module to declare the private variables that we will use to storethe property values. In this case we need two property storagevariables. One for the DocumentAddress property another for theCurrentApplication property. I have also added another variablemblnDontNavigateNow that is used to indicate that the browser control iscurrently busy:
Option ExplicitPrivate mblnDontNavigateNow As BooleanPrivate mstrDocumentAddress As StringPrivate mstrCurrentApplication As String
As usual after the declaration section,we need to provide the necessary property handlers. In this case that isgiven by the following code:Public Property Get DocumentAddress() As StringDocumentAddress = mstrDocumentAddressEnd PropertyPublic Property Let DocumentAddress _ (strDocumentAddress As String)mstrDocumentAddress = Trim(strDocumentAddress)End PropertyPublic Property Get CurrentApplication() As StringCurrentApplication = mstrCurrentApplicationEnd PropertyPublic Property Let CurrentApplication _ (strCurrentApplication As String)mstrCurrentApplication = strCurrentApplicationEnd Property
The Form_Load for this form is simple,all we do is to show the form and call the Form_Resize routine:Private Sub Form_Load()On Error Resume NextMe.ShowForm_ResizeEnd Sub
This subroutine just updates the captionof the form to reflect its contents:Private Sub brwWebBrowser_DownloadComplete()On Error Resume NextMe.Caption = brwWebBrowser.Document.All("Title")End Sub
The Form_Resize routine is also amazinglysimple. All we need to do here is to make sure that the browser isessentially the same size as the form it rests on:Private Sub Form_Resize()On Error Resume NextbrwWebBrowser.Width = Me.ScaleWidth - 100brwWebBrowser.Height = Me.ScaleHeightEnd Sub
That is really all of the work we have todo in order to create an application that provides a controlledenvironment for our applications' ASPs.
All we have really achieved in thischapter is to stand back a little and take a look at all the pieces froma different perspective. We started the book off by examining theintrastructure that we would be using to house our data objects and thenwe spend a long time focussing in on the particulars of how to build theobjects. Finally, in this chapter we have been able to combine the twoand look at how we can deploy our carefully constructed data objectsacross the enterprise's arhcitecture.
We looked at our physical architeturefrom a more software oriented approach, so that we saw the the Datasphere as SQL Server, the Data and User Centric spheres as MTS and thePresentation sphere as IIS. Finally, we talked about limiting theclient's actions by constructing a typical desktop interface but withembedded components. There's only one thing left to do now and that'stake a look at the ASP going on behind the scenes.
Page 4 of 4