|
|
N-Tier Component ArchitectureOne of the key elements of any component-based application design is the system architecture. The system architecture defines how pieces of the application interact with each other, and what functionality each piece is responsible for performing. There are three main classes of application architecture. They can be characterized by the number of layers between the user and the data. Each layer generally runs on a different system, or in a different process space on the same system than the other layers. The three types of application architecture are single-tier (monolithic), two-tier, and three-tier. The monolithic application consists of a single application layer that supports the user interface, the business rules, and the manipulation of the data all in one. The data itself could be physically stored in a remote location, but the logic for accessing it is part of the application. Microsoft Word is an example of a monolithic application. The user interface is an integral part of the application. The business rules, such as how to paginate and hyphenate, are also part of the application. The file access routines, used to manipulate the data of the document, are also part of the application. Even if there are multiple DLLs that handle the different functionality, it is still a monolithic application. In a two-tier application, the business rules and user interface remain as part of the client application. The data retrieval and manipulation is performed by another separate application, usually found on a physically separate system. This separate application could be something like SQL Server or Oracle, which is functioning as a data storage device for the application. This type of application is widely used in the traditional client-server types of applications. PowerBuilder or Visual Basic both integrating with Oracle are two examples of tools that can be used to create client-server systems. With three-tier applications, the business rules are removed from the client and are executed on a system in-between the user interface and the data storage system. The client application provides user interface for the system. The business rules server ensures that all of the business processing is done correctly. It serves as an intermediary between the client and the data storage. In this type of application, the client would never access the data storage system directly. This type of system allows for any part of the system to be modified without having to change the other two parts. Since the parts of the application communicate through interfaces, then as long as the interface remains the same, the internal workings can be changed without affecting the rest of the system. The transaction processing systems provide an environment in which the business and data objects of a system can operate. The system can manage these objects so that the system can operate at its highest efficiency, no matter what the load on the system. Threading ModelsAn important issue that the developer of MTS applications needs to consider is the threading model that is used by the components of the application. The threading model of a component determines how the methods of the component are assigned to threads in order to be executed. The MTS environment itself manages all of the threads associated with its applications. A component should never create any threads on it's own. A component can use one of four threading models. The threading model for a component is determined at the compile time for the component. There is a registry attribute for each component in MTS called ThreadingModel, which indicates what threading model MTS should use for this component. The four threading models are single threaded, apartment-threaded, free-threaded, and both. Single ThreadedIf a component is marked as Single Threaded, then all methods of that component will execute on the main thread. There is one main thread for each MTS application. Since there is only one thread, it means that only one method can execute at any given time. If there are multiple single threaded components that are being accessed by multiple clients simultaneously, then it is easy to see why a bottleneck will occur. Since only one method can execute at a time, all of the other methods will be sitting around waiting their turn. This can essentially grind a system to a halt. A good rule of thumb to remember is Single Threading = BAD! Apartment ThreadedIn a component that is marked as Apartment Threaded, each method of that component will execute on a thread that is associated with that component. Since each component running inside MTS has its own thread, this separates the methods into their own "Apartments," with each component corresponding to one apartment. While there is only one thread inside of a component, each instance of that component will have its own thread apartment. This will alleviate a great number of the problems that a single threaded component has. Since multiple components can be executing methods in their own apartments simultaneously, the scalability constraints that the single threaded components have are greatly relaxed. Free ThreadedA free threaded object is also known as a multi-threaded or multi-threaded apartment object. An application in MTS can have only one multi-thread apartment (MTA) in it. This MTA can have zero or more threads within it. Any objects that are marked as free threaded will be created in the MTA, regardless of where they were created. Now, all of this may sound great. "Wow, multiple threads! Must mean great scalability!" This is not necessarily the case. To see the problem, we need to look at how calls are made from one component to another. When a call is made from one component to another component within the same apartment, the call is a direct function call. In the Win32 environment, this is a very fast process. If a call has to be made from one apartment to another, then a proxy needs to be created in order to make the call. What this does is add overhead to every function call, and slows down the system. Since a free threaded component will ALWAYS be created in the multi-threaded apartment, even if it was created from a single thread apartment, then all calls to access that component will have to be made by a proxy. This means that even though free-threaded components sound good, they will extract a performance penalty. BothThere is another type of threading model called both. This is a special type of threading model in that the object has the characteristics of an apartment-threaded object as well as a free-threaded object. The advantage that this threading model brings is that no matter where the component is created, it will always be created within the same apartment. If it is created in a single-thread apartment, then it will act like an apartment-threaded component, and be created in the apartment. And likewise, if it is created in the multi-thread apartment, it will act like a free-threaded component and be created in the MTA. In either case, to access this new component, proxy calls are not necessary. The access can be made using fast and efficient direct function calls. NeutralCOM+ introduces neutral apartments to simplify programming in multithreaded environments. Neutral apartments are the preferred model for COM+ for components with no user interface. Components that have user interfaces should continue to use ThreadingModel=Apartment as the preferred model. In the past, to prevent bottlenecks, COM+ developers requiring server scalability had to implement free-threaded components; however, free-threading models are complicated to implement because they must deal with interlocking access. In neutral apartments, objects follow the guidelines for multi-threaded apartments but can execute on any kind of thread. Object ScopeAn important issue when integrating your newly created server components with the ASP scripts themselves is the scope that these components will have. There are three possible levels of scope that are available in ASP. Page-level scope is where the object has a lifetime of a single page, and is only visible to that page. Session-level scope means that the object has the lifetime of an entire user session, and is visible to all pages that may be accessed during that session. Application-level scope means that the object is visible to all sessions accessing a particular application. The session and application-level scope object are created in the global.asa file. Page ScopeA page scope object has the lifetime of a single ASP script page. The component is only visible from that single page. For components at this level, the best types of components to use are apartment-threaded and both-threaded. These components can be accessed directly, rather than by proxy. A free-threaded component is not nearly as good, since the calls must be via a proxy, and also a free threaded object cannot access the ObjectContext, which it needs to participate in the transaction. As in nearly every case, the single threaded object is bad, since all access is serialized across the application. Session ScopeWhen an object is created with session scope, it will be available to all ASP script pages that are referenced during that user's session. Each session that is created in the application will also have its own instances of session-scope objects. To create a session scope object, you will add this entry to the global.asa file for the application. <OBJECT RUNAT=Server SCOPE=Session ID=myID PROGID="objectID"><OBJECT> This <OBJECT> tag must be outside of any <SCRIPT> blocks in the global.asa file. The RUNAT=Server parameter indicates that this object is a server side component. The SCOPE=Session indicates that this object will have session-level scope. It will be accessible to any ASP script that is part of the application, referenced through the myID object reference. The choice of threading models for session-level objects is a bit different. As with the page-scope object, single threaded and free threaded are bad since access to them has to be through a proxy. An apartment threaded object is OK, but when the session-scope object is being accessed, then the session is locked down. This means that no other changes to any session-level variables can be made while the object is being accessed. For a session-level object, the optimal threading model is both. Application ScopeAn application-scoped object is one that can be accessed by any ASP script in the application. There is also only one instance of the object for the entire application. This object is inserted in the same way as the session scoped object, using the <OBJECT> tag. There is one change that needs to be made for an application-scoped object: <OBJECT RUNAT=Server SCOPE=Application ID=myID PROGID="objectID"><OBJECT> By changing the SCOPE parameter to Application, there will be one instance of this object available to all users of the application. This presents some interesting challenges when selecting a threading model for an application-scoped component. If this component is marked as using Apartment threading, then all access to this object will be serialized. That means that only one user out of all users accessing the site can use the object at any one time. All of the other users will have to wait until it is their turn. If a system is going to have very infrequent concurrent use, such as in an intranet that serves 25 people, this may be acceptable. If you are trying to run a production site that has thousands of simultaneous users, using an apartment threaded component would quickly bring the site to a grinding halt. The best component to use is one that is marked both. A both-threaded component will be able to directly accessed through function calls, rather than having to use a slower proxy call. Most of all, the access to the component is not serialized, meaning that multiple users can access the component at one time. This will greatly enhance the scalability of the application over the user of apartment-model application scope components.
Tools for creating components There are three primary choices for languages with which to create server components. These choices are Visual Basic, C++, and JAVA. Each of these languages can produce COM objects that can be used as server components inside of MTS. They also have differing levels of support when it comes to creating components using the various threading models that we have just looked at. Visual Basic is the quickest way to create application components. However, with this ease of use comes a tradeoff. The default threading model for components created in Visual Basic is single-threaded. As you have probably gathered by now, single threading is bad. However, with the release of the Visual Studio Service Pack 2, Visual Basic can now create apartment-threaded components. While these are not optimal in all uses, they will be sufficient for the majority of cases where application components are used. C++ can also be used to create application components. Nearly all of the components that make up IIS and MTS and ASP are built using C++. A new feature of Visual C++, known as the Active Template Library, or ATL, has a wizard that lets you get a lot of the COM plumbing generated for you automatically. All that you will then need to write is your application specific code. C++ and ATL can generate components that use the both threading model, which we will see later is the most versatile threading model for all component uses. JAVA, despite all of its hype, is an excellent programming language. With the COM-specific support that Microsoft has added to Visual J++, you can use JAVA to create server components. Also, a set of JAVA classes is provided with ASP that allows the developer to use standard JAVA to create the components. All of the COM-specific constructs, like variants, are mapped onto native JAVA types. By default, JAVA components are marked as both threaded, making them optimal for use in all parts of an ASP and MTS application. There are some drawbacks to using JAVA to develop COM components. First, one of the nice features of the JAVA run-time is that it can perform automatic garbage collection. This will make sure that any unreferenced objects are deleted so their resources can be reused. The problem is that when the garbage collector runs, it drags the performance of the operating objects down with it. On a production web server, with lots of traffic, this sudden loss in performance can be catastrophic. Another drawback of using JAVA is the uncertainty surrounding Microsoft and the JAVA language itself. While Microsoft has publicly stated their commitment to the language, their extensions to the language that permit the interaction of JAVA and COM have become part of the Sun lawsuit. I would be very leery of future support for COM-enabled JAVA objects given the current legal climate. Using VB for Component Creation Visual Basic and Threading ModelsWith the introduction of Visual Basic 5 and the ability to create ActiveX DLLs, VB could only create Single-Threaded components. They essentially make an ASP system unscalable, since it only permits ASP to process one client at a time. When trying to deploy an Internet site using ASP, limiting the site to one user at a time will generally render your site completely ineffective. Seeing this, Microsoft added the ability for Visual Basic to create Apartment threaded components in the Visual Studio 97 Service Pack 2. While these components do not have the complete flexibility that Both-threaded components do, they do provide a robust solution creating page-scope components. Naturally, Visual Basic 6, which is part of Visual Studio 6, carries this support forward. We can hope that one day in the future, Visual Basic will allow us to create components that use any of the four threading models. Creating a Business Logic Component using VBNext, we will look at how to create and use a component written in Visual Basic. To do this, we will be using Visual Basic 6, which is part of Visual Studio 6. If you want to use VB 5 to do this part, you will need to make sure to install Service Pack 2 for Visual Studio 97 after you have installed Visual Basic 5. This is necessary to allow you to create Apartment-model components. Also, the screen shots shown may be slightly different from Visual Basic 5. Creating the ProjectAs we mentioned earlier, Visual Basic allows us to create different types of projects, depending on the end product we are trying to create. To create an Active Server Component, we will need to create an ActiveX DLL Visual Basic Project. This is done from the New Project window of VB.
Even though there is an entry for IIS Application, you should select the ActiveX DLL project. An IIS application uses HTML to present its user interface and uses compiled Visual Basic code to process requests and respond to events in the browser. It is commonly referred to as a "VB WebClass Application". The ActiveX DLL project will setup the necessary infrastructure to build an Active Server Component. If for some reason this dialog is not displayed when you start Visual Basic, you can get to it by selecting New Project… from the File menu. After you press the Open button, VB will create the empty project with one class module in it. Project SettingsThis new project will be created with the name Project1. This is probably not the name that you want the project to go through life as. To change it, you can select the Project1 Properties… menu selection from the Project menu. Selecting that will display this dialog box:
As you can see, Visual Basic has already set the Threading Model to be "Apartment Threaded". This will give us an Active Server Component that can be used in ASP page scope and not affect scalability. At this time, you should go ahead and change the name of the project to something more meaningful. For this example, we will change the name of the project to Shipping. A class module has also been created for us, named Class1 by default. We will change it to Cost for this example. PropertiesThere are two primary interface types that we will use to interact with the component. The first type, properties, will allow us to store and retrieve values from the component. These properties can be held onto until later, at which time the calling program can retrieve them. You can also use properties to store information that can then be used to process some information. Along with each property of a component, there can also be accessor methods. An accessor method is used to expose the property to the external world, rather than allowing direct access to the property value itself. These can be used to perform some kind of processing whenever a property value is read or set. We will use the VB Add Procedure dialog box to add the property.
The Add Procedure dialog box allows us to add different kinds of procedures to the project. Since we are adding properties to the project right now, you should select the Property radio button. Also, since we want the users of this component to be able to use this property, we need to make sure that the Scope of the property is set to Public. When you press OK, Visual Basic will add two procedures to the class module code. These are the accessor functions that we mentioned earlier. Public Property Get Weight() As Variant End Property Public Property Let Weight(ByVal vNewValue As Variant) End Property Visual Basic just adds the structure for the functions. It does not write any of the code for us. Usually, the first step is to relate a private variable in the class to this property. What good is a property if as soon as the caller sets it, the component forgets what the value was? We will need to create a private variable to hold the property value and then connect the private variable to the accessor functions. Private m_PkgWeight As Long Public Property Get Weight() As Variant Weight = m_PkgWeight End Property Public Property Let Weight(ByVal vNewValue As Variant) m_PkgWeight = vNewValue End Property For this example, we will have 3 other properties: Ř DestinationZip Ř RateOvernight Ř Rate2Day Once you add all of these properties using the class wizard, your class module code will look like this. Note that the class wizard does not add any code to the bodies of the methods that it adds – this is up to the developer. Private m_PkgWeight As Long Private m_DestZip As String Public Property Get Weight() As Variant Weight = m_PkgWeight End Property Public Property Let Weight(ByVal vNewValue As Variant) m_PkgWeight = vNewValue End Property Public Property Get DestinationZip() As Variant DestinationZip = m_DestZip End Property Public Property Let DestinationZip(ByVal vNewValue As Variant) m_DestZip = vNewValue End Property Public Property Get RateOvernight() As Variant End Property Public Property Let RateOvernight(ByVal vNewValue As Variant) End Property Public Property Get Rate2Day() As Variant End Property Public Property Let Rate2Day(ByVal vNewValue As Variant) End Property As you can see, we have also added a private variable to hold the value for the destination zip code. For those of you in the United States, you may be wondering why we are not storing the zip code as a long integer. There are two reasons – first, the new ZIP+4 includes a hyphen, which cannot be stored as part of a long value. Second, nearly all postal codes outside the US contain letters as well as numbers. Read-only PropertiesSome of the properties that we will have for our component are going to be read-only. This means that the calling application can ask the component for the value of the property, but it cannot directly set the value of the property. There are actually three types of properties. You have already seen the common read-write type of properties, such as the Weight and DestinationZip properties. There are also write-only properties. These allow the calling application to set the value, but don't offer any way to read it back. For our component, we will have two read-only properties. To make a property read-only, all you need to do is remove the Property Let function from the class module. This function is the accessor function that allows the caller to set the value. Since the caller cannot directly set the value, the property is considered read-only. Public Property Get RateOvernight() As Variant End Property Public Property Get Rate2Day() As Variant End Property MethodsThe other type of interface supported by components is the method interface. With this interface, we are generally instructing the component to perform some type of function. The information with which the component does this processing can either come from parameters passed into the method call, or from the internal properties of the component. To add a method, we will again use the VB Add Procedure dialog box. This time, we will select Function or Sub as the type. If our method is going to return a value to the caller, then we need to create a Function. For methods that do not return a value, a Sub should be selected.
We will be creating a Function so that we can return a success or error code back to the calling application. When you press OK, the following code will be added to the class module. Public Function CalcShipping() End Sub Adding the CodeNow that we have created the interface (properties and methods) for our component, we need to add the rest of the code. We will be computing the shipping costs for a package based on the weight of the package along with the destination for the package. Once the weight of the package and the destination properties are set, the CalcShipping method can be called. This will determine both the overnight shipping rate was well as the two-day shipping rate. These values will then be available through their corresponding read-only properties. The completed class file will look like this. Option Explicit Private m_PkgWeight As Long Private m_DestZip As String Private m_RateOvernight As Currency Private m_Rate2Day As Currency Public Property Get Weight() As Variant Weight = m_PkgWeight End Property Public Property Let Weight(ByVal vNewValue As Variant) m_PkgWeight = vNewValue End Property Public Property Get DestinationZip() As Variant DestinationZip = m_DestZip End Property Public Property Let DestinationZip(ByVal vNewValue As Variant) m_DestZip = vNewValue End Property Public Property Get RateOvernight() As Variant RateOvernight = m_RateOvernight End Property Public Property Get Rate2Day() As Variant Rate2Day = m_Rate2Day End Property Public Function CalcShipping() If m_PkgWeight = 0 Then CalcShipping = 1 Exit Function End If
If m_DestZip = "" Then CalcShipping = 2 Exit Function End If
If m_PkgWeight < 5 Then m_RateOvernight = 9.95 m_Rate2Day = 6.75 Else m_RateOvernight = 14.95 m_Rate2Day = 11.75 End If CalcShipping = 0 End Function Private Sub Class_Initialize() m_PkgWeight = 0 m_DestZip = "" End Sub There are two important methods in this component. The Class_Initialize method is called when the component is created. This lets us initialize the internal representations of the component's properties. We rely on these initialized values in the CalcShipping method to determine if the required properties of the component have been set. In the CalcShipping method, we first check to see if both the weight and destination zip code have been set. If either has not been set, then we will set a non-zero return value and then exit the function. The calling application can then check this return value to determine why the method call did not complete successfully. Finally, if both values have been set properly, we can compute the shipping cost and then store those values in the internal representations of the two read-only rate properties. The return value of the method is set to 0 to indicate that the method was completed successfully. Building the ComponentNow that all of the code for the component has been written, we need to create the component executable itself. To do this, we will use the Make - Shipping.dll… selection from the Visual Basic File menu.
After typing in the name for the component, the class code will be compiled into the component. The name of the component file itself is not important, since the reference to the component itself will be done through the name of the component as defined in the registry. This name corresponds to the project name and class name that we set earlier. Using the ComponentNow that we have a working component, we need to put it into an ASP page to use it. To use the component in the page, we need to instantiate it with the Server.CreateObject method, set the properties, call the CalcShipping method, and then output the results. In this sample ASP page, we will hardcode the weight and destination zip code. You should easily be able to extend the page to accept these values from FORM input. <% dim objShipping set objShipping = Server.CreateObject("Shipping.Cost") objShipping.weight = 7 objShipping.DestinationZip = "31402-1232" objShipping.CalcShipping() %> The shipping costs to ship a <%= objShipping.weight %> pound package to a location in zip code <%= objShipping.DestinationZip %> is <%= FormatCurrency(objShipping.RateOvernight) %> for overnight shipping and <%= FormatCurrency(objShipping.Rate2Day) %> for 2 Day shipping. The creation of the component is responsibility of the CreateObject method. We pass in the name of the component that we want to create. Notice how the component is named. In our Visual Basic project, the project name was Shipping and the class that made our component was named Cost. This directly translates into the name of the component begin Shipping.Cost. Once we have instantiated the component, we can interact with it in the same way that we interact with the Response or Request objects. That is, we can set and retrieve property values as well as calling methods on the object. When you view this page in your browser, it will look like this.
Now that we have seen how to create a simple ASP component using Visual Basic, we will now move on to adding some more functionality to the component. Using the ASP Intrinsic componentsASP is built around an object model which supports 5 intrinsic components. These components provide access to information and interactions with the request coming from the client, the information to be sent back to the client, the web server itself, along with application and session information. Normally, these components are only accessible to the ASP script itself, but we can also make access to these components available to ASP Components as well. In order to do this, we need a way to reference these components from within the VB component. One of the advantages of working with a tool like Visual Basic is its ability to perform early binding of components. This means that we can identify the component that we are using during development, and Visual Basic will provide us with a wealth of information about that component, including tools tips and auto completion support. Referencing the ASP IntrinsicsIn order to reference the ASP intrinsics from Visual Basic, we must first tell our VB Project that we are going to be using these components. By doing this, VB can provide the programmer with contextual help and other support for these components. To add a reference to the ASP intrinsics, select References… from the Project menu.
In the list of Available References, scroll down to the Ř Microsoft Active Server Pages Object Library selection. Check the box that adjoins this selection. Then scroll to the Microsoft Transaction Server Type Library and check the box that adjoins that selection as well. Press OK to close this dialog box. Obtaining a ReferenceNow that you have told Visual Basic that you will be using the ASP intrinsics in your component, the next step is to obtain a reference to them. We can't, however, simply refer to these components in the same way that we do in VBScript in an ASP page. The first thing we must do is obtain what is called the ObjectContext of the component. This simply allows us to refer to the environment that the component is operating in. Since we are operating inside of Active Server Pages, the ObjectContext will let us access the ASP intrinsics. The easiest way to get the ObjectContext reference is to grab it when the component is initialized. We can then hold on to the reference in a private variable and access it when needed. Private m_OC As ObjectContext … Private Sub Class_Initialize() Set m_OC = GetObjectContext() m_PkgWeight = 0 m_DestZip = "" End Sub Now from any point in our component, we can reference the ObjectContext by referring to the m_OC variable. This gets us halfway to accessing the intrinsics. Remember that the ObjectContext allows access to ALL of the components in the environment. In order to access the component that we are interested in, we must retrieve it from the ObjectContext. Using the ASP intrinsics in VBTo show how this is done, we will create a method of the component that will output the overnight shipping price information to the browser. Rather than just returning a string to the calling ASP script, which would then output the information using Response.Write, we will obtain a reference to the ASP Response object, and then output the information to the browser directly from the component by calling the Write method of the Response reference. Public Sub DisplayOvernight() Dim r As Response Set r = m_OC("Response") r.Write ("<P>The Overnight Shipping Cost is ") r.Write (FormatCurrency(RateOvernight)) r.Write (".") End Sub The first step is to create a local variable that will refer to the ASP Response object. Since we have included the Active Server Pages Reference into this VB project, VB will recognize the Response object data type. It can then provide all of the nice features, such as context-sensitive help and autocompletion, which make VB such a popular development tool. To actually obtain the reference to the Response object, we retrieve it from the default collection of the ObjectContext. We can retrieve each of the VB intrinsic objects this way – all we need to do is substitute the name of the appropriate object. With the reference to the Response object stored in the local variable r, we can now call the same methods on that object that we are used to using from VBScript in ASP file. Testing the new methodWe will now want to test this new code. To do this, we can modify the example we used earlier. We will just add a call to this method after the code that is already in the existing file. This will show that you can utilize this component in multiple ways. The shipping costs to ship a <%= objShipping.weight %> pound package to a location in zip code <%= objShipping.DestinationZip %> is <%= FormatCurrency(objShipping.RateOvernight) %> for overnight shipping and <%= FormatCurrency(objShipping.Rate2Day) %> for 2 Day shipping. <HR> <% objShipping.DisplayOvernight() %> Notice how that the method call to DisplayOvernight is surrounded by <%…%> delimiters. You may be tempted to use the shortcut write method of <%=…%> thinking that this method is outputting information to the browser. But the method handles the output internally, so the calling ASP script does not need to worry about sending the information back to the client. In fact, if you had used the shortcut method by mistake, nothing would have been output to the browser in this case. But if the method call had a return value, then that return value would have been output to the browser |
|
|
|
|
|
| |
Chicago Web Site Design teleconferencing services conference call services Calvin klein sunglasses air freshener odor remover |
Email TopXML
|
|
Front Page Daily Stuff TopXML Forum XML blogs XML Newsgroups BizTalk Biztalk Utilities Biztalk Utilities Tutorial B2B SAP XML Microsoft .NET Dotnet System XML Soapformatter SQLXML XMLserializer XQuery PHP PHP SimpleXML PHP XML Dom PHP XML RPC PHP XSLT Java Java Java XML Xalan Microsoft ASP ASP Schemas XML SQL Server XML XMLDom XSL XSL Tutorial XSLT Stylesheets General Javascript CSS XHTML WAP |