ASP.NET and Struts: Web Application Architectures
Microsoft .NET Framework
Summary: Learn about the similarities and differences between ASP.NET on the .NET Framework and Struts on Java 2 Enterprise Edition; and the features that each provides to solve common developer problems. Learn about the advantages and disadvantages of each, and the utility that they bring to next-generation Web development. (26 printed pages)
The last decade has reshaped the way that we do business and access information. Since the emergence of simple information portals in the early nineties, the Internet has rapidly evolved into a major and profitable forum for business and trade. This evolution has spawned two major standards for building enterprise Web applications—Microsoft® ASP.NET (and the Microsoft .NET Framework) and Apache's Struts (along with the J2EE framework).
Architects and developers from both the .NET and Java communities are now asking how these two technologies compare in scope, functionality and underlying architecture. In this paper we will provide an overview of the platforms, a rundown of the similarities and differences, and a review of the features that each framework provides to solve common developer problems. We will show how the industry standard patterns implemented in ASP.NET and Struts have helped reduce code complexity and accelerate development times. We will also explore the advantages and disadvantages of each offering, and the utility that they bring to the next generation of development.
Brief Evolutionary History of Web Development
In the early days of the Web, most business Web sites were simply a form of advertisement, providing user manuals, brochures and catalogues to customers. The dot-com revolution showed businesses that they could also provide online services for their customers. Now, instead of just viewing inventory, customers could also purchase it. This new phase of the evolution created many new requirements. Web sites had to be reliable, available, secure, and, if it was at all possible, fast.
These early e-commerce Web sites were often powered by Java-based technologies, including JSPs, EJBs, Servlets, and JDBC; or Microsoft-based technologies, including ASP, VBScript, MTS, ADO, COM and COM+. Although these technologies worked, most of the sites were built quickly without giving much thought to scalability, reliability or security.
The Java 2 Enterprise Edition
Toward the end of the nineties, the dot-com phenomenon was in full force. However, architects on both sides realized that the natural evolution of Web development would only be achieved through standardization of the mishmash of technologies that were being used.
In the spring of 1999, Sun released the J2EE standard, which united the numerous disparate Java technologies under a single banner. This standardization allowed numerous third-party companies to develop tools geared specifically to building J2EE applications, and significantly accelerate the enterprise Web development process.
However, even with the advent of the J2EE standard, several key problems remained. Most notably, new and improved Web applications often failed to integrate with back-end ordering, processing and billing systems. Although the front end was well architected and scalable, the system still broke down because the back-end business logic and data persistence often wasn't designed to support the influx of new orders from the Web. The need for more integrated business tools and Web services became apparent. Additionally, another trend was becoming more evident; the larger the scale of the application, the more difficult it was becoming to maintain and extend. The code base for a reliable, scalable application was becoming terribly complex and hard to manage.
This background laid the framework for the pattern revolution in J2EE. How could a developer, or architect, design a system that was built on these established technologies, yet avoid the problems with complexity?
The origins of ASP.NET
During the same time frame, Microsoft decided to pursue a different course, and chose to re-architect the technologies rather than simply regroup them under a different name. Microsoft decided to create a completely integrated environment. The result was the Microsoft .NET Framework, which was a radical shift from earlier application models. To combat code complexity, this new flexible framework naturally incorporated the Model View Controller (MVC) design pattern, and was built around the open standards of the Internet (HTML, XML and SOAP) to support the new paradigm of Web services. During the reengineering process, significant attention was paid to core enterprise requirements such as security, performance, availability, reliability, maintainability, extensibility, and interoperability.
The pattern revolution reaches J2EE
In the spring of 2000, just around the time that Bill Gates and Steve Ballmer were unveiling .NET, Craig McClanahan began working on the Struts Project in an effort to reduce the code complexity in enterprise-scale Web applications on the Java platform. The Struts extension is an implementation of the Model View Controller (MVC) Pattern, essentially utilizing the Front Controller and Intercepting filter patterns to these ends. We will discuss the differences in MVC implementations later on in this paper.
The third age of Web applications
At the moment, ASP.NET and Struts have taken the stage to provide powerful solutions to everyday development problems. The enterprise application frameworks that they provide have proven invaluable, and ensure that the development cycle for businesses become shorter and more reliable. While there remain significant differences in both the technology and methodology behind the J2EE and .NET platforms, ASP.NET and Struts make Web development of past days look like pre-industrial labor.
There are many similarities, and several major differences, between the .NET and Struts/J2EE development and execution environments. Figure 1 illustrates how the two environments relate to one another.
Figure 1. ASP.NET and J2EE (with Struts) platform stacks
In the next sections, we will examine the key similarities and differences in each area.
Language and IDE
The key difference in the language and development environment stack is a philosophical one. With J2EE, you are limited to a single language (Java), but may choose from a wide variety of development environments. Each IDE offers a different level of support for the language and Web toolkits. You can, for example, work in a simple text editor with keyword color coding, or a full environment that supports debugging. Naturally, the level of integration between the IDE and the other components (OS, Application Server, and so on) is highly vendor-dependent.
In ASP.NET, you have many choices for development language, but one primary choice for development environment. Although you can develop ASP.NET in a text editor, using the lightweight Web Matrix product, or another third-party editor, the primary development environment is Microsoft Visual Studio® .NET. Visual Studio .NET is a very sophisticated IDE that provides not only for the development of console, wireless, Web, Web service and windows applications, but also serves as the IDE for other Microsoft products, such as Biztalk® Server 2004, Office 2003, and Content Management Server. This makes Visual Studio .NET a highly leveraged environment, ensuring continuous improvement and evolution in the long term. Third-party tool developers can also leverage the IDE and create tools that run within Visual Studio .NET. For example Crystal Reports and Rational XDE have incorporated modules in Visual Studio .NET to accelerate .NET development specifically related to those products. This level of integration makes Visual Studio .NET a very powerful development tool.
Application Servers and Operating Systems
Both J2EE and the .NET Framework rely on one or more servers to host the Web application and provide essential services. These servers, and the runtimes themselves, also rely on the base operating system.
The J2EE framework was designed for platform independence. As such, many vendors have built J2EE application servers that run on a variety of operating systems. Although platform independence is valuable, it also adds significant overhead and complexity. The abstraction layer required to support multiple operating systems (OS) forces restrictions on how many OS features may be accessed. Each vendor may provide a different level of integration, and the performance may vary wildly depending on which application server you choose and which operating system you run it on. Finally, mixing vendors often results in support complexity, as each vendor's upgrade/patch cycle must be monitored, and changes by any vendor must be checked against all of the other products. Although platform independence is a benefit, it does have associated costs.
Unlike J2EE, in which the Application Server is completely independent from the OS, the .NET Application Server is the Microsoft Windows® Operating System (and the services provided by Windows). The .NET Framework was designed to integrate closely with IIS and COM+ to provide reliable application hosting services and native support for Web services standards like XML, SOAP, UDDI and WSDL. This integration provides a significant performance boost to .NET applications, but requires platform monogamy. The level of integration is much tighter than with any J2EE server, and results in more features and faster performance.
Both J2EE and the .NET Framework run on top of a runtime engine. The runtime interprets and compiles the code and provides critical services such as memory management. The goals of the two runtimes differ significantly due to the differences in design philosophy.
The Java runtime was engineered for portability between operating systems. Although this is advantageous in situations where portability is required, it limits the performance of the application, because the code can be optimized only for execution and not for a specific operating system or application server. Third-party products are often introduced to provide OS specific optimizations, often at the cost of portability.
The Common Language Runtime (CLR), on the other hand, was engineered for two purposes: optimized performance on the Windows operating system and multiple programming language support. The CLR supports any number of different programming languages through a common type system, and helps isolate an application from changes in the operating system.
Struts draws heavily upon both the J2EE and J2SE class libraries, which have approximately 135 packages, divided along a variety of base package groups (
org). The Struts framework broadens the library set slightly to incorporate functionality through new base classes inherent to the workings of Struts. Although the J2EE and J2SE libraries are fully functional, the continual expansion of the library (the J2EE library has more than doubled since version 1.2) has added complexity and obfuscated functionality that should otherwise be intuitive. For example, deciding when to use
java.xml for XML access, or when to use
org.omg.stub.java.rmi for remote method functionality is not always obvious. Although some of the libraries are separated based on versions for compatibility, the ordering does not make much sense and is not intuitive to newcomers.
The .NET Framework provides a rich and extensive base class library (BCL). Classes are organized by functionality namespaces, which are logical groupings that are similar to packages. For example,
System.Web.UI contains all of the classes for the user interfaces for the Web. The
System.Web.UI.Page class contains the methods and properties needed for an ASP.NET page. The .NET Framework (1.1) BCL contains just over 200 core
One of the major differences between the Java platform and the .NET Framework is that .NET has an inherent and rigidly-enforced versioning scheme that applies to both the .NET Framework and to any application built on the Framework. In Java, the framework version is noted in the documentation and only partially enforced through special "deprecation" tags. Quite often, new Java packages will be created to allow for backward compatibility. Some additional versioning can be employed at the application level, using XML tags in the Web deployment descriptor. However, the versioning system is not rigidly enforced and side-by-side deployment is difficult to control.
In .NET, versioning is much less of a problem because of the way the .NET Framework handles version identification. Because each assembly in the BCL is tagged with a strong name that includes the version number, different versions can run side by side without conflicting. In other words, a new version of .NET does not need to include new namespaces to modify existing classes. Both versions of an assembly can be loaded on the computer, and the CLR will automatically bind an application to the version of the library that it was compiled against. If you want to redirect an application to a newer version, you can use either graphical or command-line tools to configure the redirect. The same system is employed at the application level. That is, any assembly can be signed with a strong name and take advantage of the versioning enforced by the CLR.
Both J2EE and the .NET Framework offer a rich variety of Web application technologies. In J2EE, you have the choice of Servlets, JavaServer Pages and tag libraries. Many tag library sets have been created to perform browser detection and generate browser specific output. However, browser detection is not currently built into the J2EE framework. The JavaServer Faces specification may provide this functionality in the future.
On the .NET side, Web applications are built using ASP.NET. ASP.NET pages contain Web controls, which are often graphical "drag and drop" components that encapsulate functions such as text boxes, buttons and other form elements. Web controls offer a rich set of functionality, including browser detection and auto-output generation. A developer can also extend any of the existing Web controls, or write a new one, to perform any custom functionality.
Both the J2EE Framework and the .NET Framework contain data access libraries. Although the core features of each library allow similar direct database access (for example, submit a command and receive results), the extended features and fundamental paradigms are very different.
The core Java JDBC library supports a very traditional approach to database access. The user submits a statement (usually a query or stored procedure call) and may receive results in the form of a result set with a live connection to the database. The database calls and results depend on an open connection at all times. Although several advanced classes support disconnected data sets, the JDBC library was not built for intermittent connection scenarios, which are very common in Web applications. In addition, JDBC does not directly support reading or writing XML data. Although several vendors have created JDBC wrappers for XML-based database access, the core J2EE classes do not support it.
ADO.NET, while able to provide the same abilities as ADO and JDBC, is based on a disconnected paradigm. Data retrieved from databases is placed in an object known as a DataSet, and the connection to the database is closed. Data in the DataSet can then be accessed and manipulated without a live database connection. Once the changes are complete, the DataSet can be synchronized back with the database through ADO.NET as a single instantaneous transaction. This disconnected behavior is extremely advantageous, efficient, and often essential when dealing with distributed environments such as Web applications.
ADO.NET is also engineered to work with XML. Not only can ADO.NET automatically translate any data set into XML, it can also abstract an XML schema based on a data set. These features are particularly useful for Web services and other XML-based data-delivery mechanisms.
In addressing the challenges of the Internet evolution, both .NET and J2EE created framework components to address common application problems. ASP.NET and Struts expand on these components and contribute more robust Web development mechanisms. This next section contrasts the various framework components, with specific focus on validation, caching, state management, security, configuration, internationalization and tracing/instrumentation.
Almost every significant application requires some form of validation for user input. Without data validation, the application is open to numerous attacks, and may break easily (for example, the user types "not a number" into a field expecting a numeric value). Validating user input not only prevents common problems, it also increases application security.
Both Struts and ASP.NET provide comprehensive validation structures to evaluate user input and accelerate the implementation of validation logic. However, each framework approaches the problem in a very different way, producing a similar result, but unique solutions and toolsets.
As of Struts 1.1, the Struts framework has included the Struts Validator. The Validator is a new data form super class that can be configured through an XML file. The XML file contains rules for standard validations, such as data type (for example, integer or string) and string format (such as email address, credit card number). The basic rules can easily be customized and new rules can be added. Every form action which requires validation must inherit from the Struts Validator, and the format rules in the XML must be in the form of regular expressions. When the form is submitted, any invalid values and error messages are returned to the view, which must have custom code written to display the validation error messages.
Adding validation to a Struts form requires rewriting the form class, editing an XML file and writing JSP code to display any validation error messages back to the user. As such, validation is best added during the initial design. Adding validation to an existing application requires changing inheritance trees and may break the application.
ASP.NET validation controls
Configuring validation in ASP.NET can be done by simply dragging and dropping server-side controls onto the page at design time and then setting a few properties on the control. ASP.NET provides out-of-the-box validation controls for checking required fields, comparing values, validating ranges and pattern matching. The pattern-matching control contains an extensive library of standard regular expression checks (such as phone number and email address) and can be configured to use any regular expression. Validation can also be done programmatically, but requires a bit more time to create the right events and invoke the validation controls manually.
Web applications frequently use caching to speed up client access times. In Struts, caching content is left up to the developer, or to third-party caching systems such as JCACHE. In other words, the developer has to build a custom caching system or integrate a third-party product into any application. Quite often, a developer will build caching functionality into custom tag libraries. Although some J2EE servers offer native caching features, the Java community has yet to develop a standard caching interface. In the future, the J2EE Tiles functionality will provide a built-in system for page and object level caching.
ASP.NET, however, has numerous built-in caching functions, including object-level caching (storing code objects), input parameter-based caching (storing a dynamic page based on input parameters), and time-based caching (storing content for a specified time). Depending on your needs, you can cache data objects or an entire dynamic page. If your display depends on user input, but the actual display changes infrequently, you can even cache the page based on input parameters from the HTTP Request. In other words, you can cache a product page that depends on the product ID. ASP.NET will automatically cache one page for each input parameter (product) that is requested. Subsequent requests for the same product will pull the cached page rather than regenerate it from scratch. All of the ASP.NET caching mechanisms can be configured declaratively with attributes (that is to say, no coding required). Or you can access the cache programmatically through a simple API. The ASP.NET caching systems offer a wide range of options for improving the user response and decreasing access times for static or semi-static content.
State management allows a Web application to keep track of both the user and the user's data during multiple Web request/response cycles. State management can be performed either on the client side (for example, with cookies or hidden fields) or on the server (for example, in the Session object). However, both client- and server-side state management require some mechanism to identify the user between requests. This mechanism usually consists of either a cookie or a session ID embedded in the URL.
Both Struts and ASP.NET rely on similar hosted objects (Session and Application) to maintain state between requests. However, ASP.NET offers three separate mechanisms for storing the session information:
- In Memory—If you choose this option, state will be stored in memory on the machine which receives the request. While this might be okay for a small application that runs on a single server, it is not an acceptable solution for Web farms, since there is no guarantee that subsequent requests from the same user will be end up on the same server.
- Session-State Store—To use session state in a Web farm, you can use something called a state store. This state store is set up on one of the members of the Web farm. It is essentially a service that can be called locally or from other members of the Web farm. All members of the Web farm need to be configured to work with this central state store and they store/retrieve their session information from here. Although this is a very efficient way of storing session state, it is not bulletproof. If the machine hosting the state store crashes, all state information is lost, since it is stored in memory only.
- Database—By far the most robust and reliable way of storing session state is in a database. Visual Studio .NET provides database scripts for setting up the state store in Microsoft SQL™ Server.
Although similar features are offered by some J2EE vendors, these features are not inherent in the J2EE or Struts frameworks.
In contrast to J2EE, the ASP.NET framework has a single mechanism to automatically manage client identities through cookies and hidden fields, and exposes this to developers through a simple-to-use programming interface. Cookies are stored in collections, and allow developers to use the usual collection handling syntax. ASP.NET also supports cookieless session state, which can be configured through the application's configuration file, and which uses query strings to persist the session ID between requests.
ASP.NET also offers a unique form of client-side state management in the form of the View State property. View State is a property of each ASP.NET control and can be used to retain the value of the control between round trips to the server. This storage makes retention of user input between multiple requests very simple. Developers need to keep in mind, though, that every control has a view state property, and that its value is populated, whether you need it or not. Because view state is implemented as a hidden field and sent to the client with every request, you need to make sure it is enabled only for fields that really require memory.
Security plays a key role in every stage of an application, from the design, to development, to deployment, and into daily use. Struts does not offer any specific security features, as it relies on the security built into the JSP/Servlet and J2EE frameworks. The J2EE framework does offer basic authentication and authorization services; however the exact implementation is often dependent on the J2EE server. On the other hand, the .NET Framework, and ASP.NET in particular, offer many advanced security features that go above and beyond such simple tasks as authentication and authorization.
Out of the box, ASP.NET provides Windows, Microsoft Passport, and Forms authentication. Windows and Passport authentication are performed outside of the ASP.NET runtime and are out of the scope of this paper.
Forms Authentication is a mechanism whereby ASP.NET redirects unauthenticated requests to a login form. When the user-supplied credentials are verified, the user is directed back to the originally-requested page. Subsequent request are automatically authenticated without prompting the user. For simple applications, user credentials can be authenticated against user, role and authorization entries in the ASP.NET application's configuration file. For more complex applications, Forms Authentication is usually extended to use an external credential store (see Extending Forms Authentication).
Once the user is authenticated, URL authorization determines whether or not to grant access to the requested resource. URL authorization is entirely configuration-based and uses entries in the application's config file to determine which users/roles have access to what resources. Authorization can easily be extended to a full Role-Based Security model.
Impersonation allows ASP.NET applications to execute with the identity of the user who requested the page. Impersonation pushes authentication and authorization out to IIS, as ASP.NET will just use the token received from IIS whether it is authenticated or not. When impersonating, ASP.NET relies on standard NTFS permissions on files and folders in order to determine whether it should allow or deny access to a particular resource.
Both Struts and ASP.NET are highly configurable and have centralized, XML-based configuration files for application settings. Some J2EE application servers also support XML-based server configuration. However, each J2EE vendor has a different mechanism for when and how both the server and application configurations are read. Because J2EE uses so many different configuration files (such as struts-config.xml, web.xml, server config files, and so on), debugging configuration can be very difficult. Also, J2EE applications do not have a common mechanism for reading configuration files. Some objects (such as the
ServletContext) have automatic access, whereas others (such as the Struts Controller) require custom code.
ASP.NET, on the other hand, has one XML file for each application, and one XML file for the machine as a whole. Every ASP.NET page has native access to anything stored in the application-level XML file. This simplified configuration structure is much easier to debug and access. In addition, ASP.NET will automatically pick up any changes to a configuration file and apply them on the fly. When the runtime detects a change in a config file, it holds incoming request in a queue while it recycles the ASP.NET worker process and loads the new configuration. No requests are lost, and the process is very quick. Although some J2EE servers work this way, many require a full reboot to pick up configuration changes.
Providing international formatting and language features to any site requires considerable effort. Not only do you have to evaluate each piece of text, you also have to evaluate each piece of displayed data (such as prices and address information), each image, and possibly even the color scheme of the entire application. Fortunately, both Struts and ASP.NET offer frameworks to help you with internationalization efforts.
In Struts, and Java in general, the user's geographical location and language are defined as a locale. The locale is passed to other classes, and used to parse information into the proper format for the user's country. The words, phrases and pictures for that language-location pair are stored in a resource bundle in key value pairs. A key word such as "greeting" could be mapped in US-English locales as "hello world!" or in DK-Danish locales as "goddag verden!" The resource bundle can be either a text file or a Java class, depending on which types of resources are required.
In ASP.NET, and the .NET Framework, a comprehensive description of the user's locale is contained in a culture object. Culture-specific formatting is provided automatically by culture-specific instances of date/time, number and text information classes. To obtain culture-specific resources such as text and images, the runtime uses Resource Managers, which read the resource files and return the appropriate resource based on the current culture.
Resources are held in an XML file during development, but can be compiled into multiple satellite assemblies for optimum performance in production. The default culture is compiled into the main assembly, and there is an additional satellite assembly for each culture.
Tracing and Instrumentation
Tracing provides informative messages about the execution of an application at runtime, facilitating fault diagnosis. Instrumentation, on the other hand, is critical in order to measure and monitor an application's performance by means of performance counters.
Struts offers logging through the Log4J components, and limited tracing through features offered by several application servers. These features are often dependent on the actual application server, and vary wildly between systems. Neither J2EE nor Struts implicitly offer performance counters, runtime tracing, or other built-in instrumentation. The developer is left to either choose third-party products or write their own systems for tracing and instrumentation.
Trace listeners and switches
Using the .NET base class library, ASP.NET offers comprehensive tracing capabilities based on the concept of trace listeners and switches. The Trace and Debug classes provide many overloaded methods to generate traces which are received and processed by trace listeners. The runtime provides several different types of listeners to write trace messages to a file, to an event log, or to the debug window of the IDE. Developers can create their own trace listeners to write trace messages to different destinations, such as queues, databases, and so on. Trace switches allow you to enable, disable or filter tracing, based on the value of the trace switch. Out of the box, the framework provides four trace levels (Error, Warning, Info and Verbose), but allows for the creation of your own trace switch hierarchy. Trace listeners and trace switches are configured through the application's web.config file. Whether or not the debug and trace statements are compiled into a given build is controlled by compiler switches.
Another key tracing feature of ASP.NET is its configuration-based, application-level tracing. By simply enabling this feature in the web.config file, ASP.NET collects a large amount of diagnostic information and appends it to the requested page. Information collected includes execution timing, the control tree, session and application state, cookies, headers, forms, query string, server variables, and so on.
ASP.NET also offers a large set of performance counters. These provide detailed metrics about the runtime performance of ASP.NET, with regards to requests, sessions, errors, caching, and so on. In addition to these built-in counters, you can create your own performance counters to report application-specific metrics such as the number of orders received, approved or rejected.
On the error diagnosis side, identifying the source of problems in an application has never been easier. In addition to providing developers with the ability to set breakpoints and step through both pages and backend components, when an error occurs, ASP.NET generates detailed error messages, including a complete stack trace. This significantly reduces the time and effort spent finding the source of problems.
Before Struts and ASP.NET, neither the Java nor the Microsoft developer had guidelines for combating application complexity. Many large J2EE and ASP applications are an excellent example of this. Dependencies between the numerous layers of an application made it difficult to change, update or extend. Adding database result caching to an existing J2EE or ASP application would require searching through each page of the application and repeatedly changing every relevant instance of the code to include data caching.
Incorporating code in pages also made it difficult to target multiple markets (like client-server or PDA) with the same application, because code would have had to be duplicated and significantly reengineered across these platforms. Many companies began developing their own frameworks to address this problem; but up until the emergence of Struts and .NET, no comprehensive framework was available.
This section discusses how Struts and ASP.NET combat application complexity using the MVC pattern, and identifies the unique advantages of each implementation.
The MVC Pattern
Long before the Web, software architects grappled with the very same user interface problem for console applications. By dissecting the problem, software architects were able to define the Model-View-Controller (MVC) Pattern. The MVC was first commercially implemented in the Smalltalk MVC framework, which was used to standardize Smalltalk-80 user interfaces.
The MVC pattern essentially separates an application into three parts: a Model, a View, and a Controller. In the MVC definition, the Model encapsulates all of the available actions and business logic for an application, the View represents the presentation layer of an application, and the Controller represents the mechanism that ensures the appropriate actions are taken when an event occurs.
To combat the user interface problem, the MVC pattern has since been adopted, adapted, and incorporated into the J2EE and .NET enterprise application platforms. In the next two sections, we will go over the Struts and ASP.NET implementations of the MVC pattern.
MVC in J2EE and ASP.NET
The J2EE MVC: Struts
The Struts architecture is essentially derived from a combination of the Front Controller and Intercepting Filter patterns. The Struts framework provides a single controller that governs the application events, while filters catch and process incoming and outgoing events to ensure that each of the MVC components receive exactly the information they need. For example, the Struts Validator is a filter that ensures that the controller receives only validated requests.
The Struts framework acts as a façade for Java applications, providing a framework to divide the code of an application into the Model, View and Controller components defined in the MVC pattern. The Struts framework also provides custom tags for communication between these layers, and a centralized controller to manage events and actions. For more details, refer to the Struts MVC implementation section.
The ASP.NET MVC: Page Controller
ASP.NET implements MVC using the Page Controller pattern. In contrast to the Struts implementation, which allows complete application-level control, the Page Controller pattern applies the controller at the level of individual pages. The ASP.NET runtime intercepts page requests, invokes the requested actions on the model, and determines the correct view to use for the resulting page. The interception and dispatching logic is automated and hidden from developers. ASP.NET divides each page into two parts: a View that contains the various controls, and the code-behind, which contains event handlers for every event that the ASP.NET Controller can raise as it processes the page. Developers don't need to interact with the actual control mechanism; they simply write event handlers to wire up the model and view components. For more details, refer to the ASP.NET MVC Implementation section.
Very complex and large applications usually require more flexibility in page-to-page and page-to-process navigation. To provide this flexibility, the ASP.NET controller mechanism can be improved in two different ways: either by centralizing the Page Controller, or by implementing the Front Controller pattern on ASP.NET.
Centralizing the Page Controller
Developers not familiar with object-oriented programming tend to overlook the power of inheritance and the Page Controller. As applications grow, this can often lead to application complexity problems. However, this need not be the case. Using inheritance, each page can inherit controller and view components from a base page class. Inheritance allows for cascading changes to occur from a centralized point, and reduces code complexity without having to implement a more complex and elaborate controller pattern. However, if you are implementing a large-scale application from scratch, you may want to consider implementing your own Front Controller or investigate the User Interface Process (UIP).
Implementing front controller in ASP.NET
One alternative to the Page Controller involves creating a single application level controller based on a custom
HttpHandler object. The
HttpHandler intercepts requests before the ASP.NET page receives them. The controller can then perform similar actions to the Struts controller. A similar approach can be used to provide pre- and post-processing in the form of an Intercepting Filter. Unlike the controller, the filter simply modifies the incoming request or the outgoing response.
In .NET, implementing a Front Controller with the Intercepting Filter is fairly straightforward. (See the frontcontrollerdemo.msi.) The Controller part of the implementation is usually broken into two parts, a Handler and a Command Processor. The Handler object receives the requests from the Web server, processes them, and selects an appropriate command based on XML parameters stored in the Web.Config file. The Command Processor performs the specific commands required to satisfy the request. When finished, the commands are forwarded through a mechanism so that the appropriate pages can be displayed.
For more details on implementing Front Controller and Intercepting Filter in ASP.NET, see the Patterns and Practices Web site. The Patterns and Practices (P&P) group at Microsoft provides guidance and recommendation on how to design, build, deploy and operate architecturally-sound solutions. P&P provides guidance on patterns, reference architectures, building blocks and lifecycle practices. In addition, the P&P Web site provides a comprehensive description, and implementation guidance, for all the patterns discussed in this paper, including the Front Controller and Intercepting Filter patterns:
- Front Controller pattern
- Implementing the Front Controller pattern in ASP.NET using HTTP Handler
- Intercepting Filter pattern
- Implementing Intercepting Filter in ASP.NET Using HTTP Module
An implementation of the Front Controller pattern will be included in the next version of ASP.NET (code name ASP.NET "Whidbey," after the code name for the upcoming release of Microsoft Visual Studio® .NET). However, if timing is an issue, you may wish to consider using the UIP application block.
User Interface Process (UIP)
The User Interface Process (UIP) is very similar to the Front Controller pattern. The UIP Application Block, created by the Patterns and Practices group, is an extension of the Page Controller pattern. It provides a framework for a more robust controller mechanism, and removes the need for building a front controller for very complex applications.
UIP abstracts away the control flow and state management from the user interface into a separate layer, which is essentially a configuration-driven state machine. The UIP is architected to be utilized from any number of user interfaces, such as Windows, Web or PDA applications. It enables you to write generic code for the front-end control flow and state management for these different types of applications. The result is a more versatile application that can easily be switched between target clients. Key elements of UIP (in gray) are shown in Figure 2 below.
Figure 2. Key elements of the User Interface Process (in gray)
The UIP Application Block comes with full source code, Starter Kit samples and comprehensive documentation to help you learn about the code provided. It can be downloaded from the Patterns and Practices Web site.
The evolution of the Web continues, putting the adaptive J2EE and .NET frameworks to the test. So what does the future hold for Struts and ASP.NET? In this section, we will briefly go over some of the emerging technologies on the J2EE and .NET fronts.
The Future of Struts and J2EE
The Struts framework is quite mature, and many of its key components have been absorbed into the main J2EE framework. The future growth of Struts may take advantage of new J2EE systems, such as Tiles and JavaServer Faces. The framework itself, however, is unlikely to change in any significant way.
Struts plug-ins and IDE integration
Several efforts are currently underway to both extend the Struts framework and to integrate it into development environments. Several beta plug-ins are currently available for auto-generating the event code used by the Struts framework. These plug-ins should greatly increase productivity for Struts developers. In addition, several IDE vendors have developed, or are working on, IDE plug-ins that incorporate the Struts framework inside of the development environment. These plug-ins effectively provide an integrated system for using Struts directly out of the development environment in a fashion that is far more efficient.
A list of struts extensions and plug-ins can be found on the Struts Applications Web site.
JSP and tiles
Tiles help you build pages from smaller fragments. Tiles are built on the "include" feature provided by the JavaServer Pages (JSP) specification to provide a full-featured, robust framework for assembling presentation pages from component parts. Each part ("tile") can be reused as often as needed throughout your application. This reduces the amount of markup that needs to be maintained, and makes it easier to change the look and feel of a Web site.
The framework uses an XML configuration file to organize those tiles. This framework not only enables you to reuse tiles, but also the layouts that organize them.
Essentially, tiles are very similar to ASP.NET custom tags and Controls, although ASP.NET tags are more powerful in the sense that you can manipulate them in the code-behind.
For more information about tiles, you can visit:
The goal of the JavaServer Faces technology is to simplify the building of user interfaces for JSP applications. Developers of various skill levels will be able to quickly and easily build applications by assembling reusable UI components in a page, connecting these components to an application data source, and wiring client-generated events to server-side event handlers. Using the JavaServer Faces technology, these applications will handle all of the complexity of managing the user interface on the server, allowing the application developer to focus on application code.
JavaServer Faces technology includes:
- A set of APIs for representing UI components and managing their state, handling events and input validation, defining page navigation, and supporting internationalization and accessibility
- A JavaServer Pages (JSP) custom tag library for expressing a JavaServer Faces interface within a JSP page
This is similar to the functionality that ASP.NET provides.
The Future of ASP.NET and the .NET Framework
Microsoft has strong commitment to .NET. It is a cornerstone of their long-term strategy. The .NET Framework is being incorporated into the operating system, and will be released as part of the next version of Windows, codenamed Longhorn. Microsoft is also moving their product line to .NET and utilizing Visual Studio .NET as the development environment for all of these products.
ASP.NET is a key component of .NET. Microsoft is working on significant improvements and new features for the next release, including:
- Built-in support for membership and role management services
- New personalization service for setting and retrieving user preferences
- New site navigation system
- More than 45 new server controls
- Automatic rendering to mobile devices
- Graphical configuration file editing
- Simplified application management
In addition, Microsoft is also planning to change the MVC implementation to include the Front Controller pattern. For more information on the next version of ASP.NET, codenamed Whidbey, refer to the ASP.NET Web page.
Numerous third parties are developing tools to extend the functionality of .NET. Among the most prominent is Avanade's product, ACA .NET, which complements Visual Studio .NET by providing both a framework and a set of pre-configured Web service and application components geared for accelerating the development process.
If you have a Struts-based application that you wish to migrate to C# and ASP.NET, then you can take advantage of the Java Language Conversion Assistant (JLCA) and the materials available in the JSP to ASP.NET Migration Guide. This guide, available in the MSDN® Library, documents a complete conversion of a Struts-based Web site into a C# and ASP.NET Web site. Not only will you find sample code for the Front Controller (complete with XML lookup and the
CommandFactory), you will see how your existing Struts Actions can be converted almost directly, thanks to the JLCA.
The primary difficulty in converting a Struts application to .NET involves replacing the Controller functionality. As mentioned earlier, ASP.NET does not have a direct equivalent to the concept of an
ActionServlet. Therefore, in order to do a one-to-one conversion of your Struts classes, you have to leverage the Front Controller pattern and write an
HttpHandler that will perform the functions of the Struts
ActionServlet. Once you have this component developed (or downloaded from the JSP to ASP.NET Migration guide), you can use the JLCA to directly migrate the rest of your Action classes into equivalent C# classes. The whole process is fairly simple and well-documented in the migration guide. The result of your conversion will be a Struts-like application in ASP.NET; however, you will still have a Java-centric solution running in ASP.NET. The next phase of the migration is to start leveraging ASP.NET features and best practices. You should, for example, replace the Struts validation code with ASP.NET validation controls. You may also want to rewrite specific actions and views to take advantage of ASP.NET controls (for example, the
DataGrid) and .NET Framework features.
As Web applications mature, the need for a robust, fully-integrated development system increases. Both J2EE and the .NET Framework offer such systems. Each framework is built on a stack that starts with the operating system and moves up through the runtime, language and API layers. Each framework offers a wide range of features and benefits for application development. As we have discussed in this paper, the addition of patterns to these stacks further enhances the ability to deliver quality applications.
The .NET Framework stack offers several significant advantages over its J2EE counterpart. Because .NET is fully integrated with the Windows operating system, and the OS provides the application server, .NET applications can take advantage of much closer integration, richer feature content and faster performance than most J2EE systems. On a development level, developing with ASP.NET involves less code than JSP. In addition, many features, such as validation, caching and tracing, are built into ASP.NET, whereas JSP requires third-party components.
In the long term, ASP.NET, the .NET Framework, and the User Interface Process provide a complete, powerful framework for developing, deploying and supporting Web applications. Because Microsoft is committed to improving the Web development experience, ASP.NET and the .NET Framework will continue to meet the needs for enterprise development in both the near term and the future.
The authors of this white paper drew information from many different sources. Several of the primary references are listed here.
- For more info on the ASP.NET processing pipeline, see the MSDN magazine article, Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET
- The ASP.NET Page Object Model
Visual Basic and Visual C# Concepts: Introduction to Web Forms State Management
- CodeNotes for J2EE—Random House Publishing Inc., 2001, New York, New York
- CodeNotes for Java—Random House Publishing Inc., 2001, New York, New York
ASP.NET validation controls: Web Forms Validation
- Struts home page
- Mastering Jakarta Struts—James Goodwill. Wiley Publishing Inc., 2002, Indianapolis, Indiana
Microsoft Patterns and Practices
- Enterprise Solution Patterns Using Microsoft .NET
- FrontController Sample Code is found on the ASP.NET Sample Code page
- JSP to ASP.NET Migration Guide
User interface process
- Microsoft Application Blocks for .NET
- .NET Rocks—April 28, 2003 interview with Michael Stuart, Senior Consultant with Microsoft Consulting Services (MCS), about the next generation of Microsoft Application Blocks, or BlueBricks.
About the authors
M. Keith Mortensen is a Senior Software Developer and Consultant for Infusion Development. Rob McGovern is Senior Project Manager for Infusion Development, and author of CodeNotes for Java, CodeNotes for J2EE, CodeNotes for ASP.NET, CodeNotes for J#, CodeNotes for Web Services and CodeNotes for Oracle 9i. Charles Liptaak is a Partner Solution Architect in Microsoft's Platform Strategy and Partner Group.
The authors would like to acknowledge the help of Brian Goldfarb, Steve Ellis, Brent Williams, Greg Brill, Cortez La Palme and Dale Baik.
Forms authentication is performed by the
FormsAuthenticationModule, which intercepts the incoming requests before they are passed to the
HttpHandler (for more information on ASP.NET modules and handlers, see the ASP.NET MVC Implementation section).
The authentication module raises an
OnAuthentication event, which allows developers to perform authentication against some sort of identity store (such as a database) and then create an authentication ticket. Subsequent requests examine the validity of this ticket (in the same event handler) and redirect to the login page if the ticket expired. Based on the cookie submitted in the header of the request, ASP.NET takes care of re-acquiring the user's identity and of the creation of the authentication ticket between requests.
Users and roles in URL Authorization map to the Identity and Principal objects, which are attached to the current thread by the runtime at the time of authentication. In the case of Windows authentication, these objects map directly to Windows users and groups.
In addition to the role-based security provided by URL Authorization, ASP.NET supports custom-made Role-Based Security. In Role-Based Security, you can create your own 'Principal' object with the appropriate roles and replace the default object on the thread with your own. You would usually apply this type of security in the
OnAuthentication event, after you have retrieved the custom role from your data store.
The MVC pattern is essentially a way of sorting the user experience for your application into three components: the Model, the View, and the Controller. These terms are often overloaded in the development world. To clarify, when we refer to an MVC framework, this is how the application is segmented:
- The Model is the gateway to the business logic, and manages the user experience from the perspective of functionality.
- The View is the presentation layer, providing the user with the available actions and the information needed to use them.
- The Controller is the bridge between the Model and the View, interpreting requests from the user and commanding the Model and/or View to change as appropriate.
The general data flow is diagrammed in Figure 3:
Figure 3. The Model-View Controller (MVC) Paradigm in Web Context
When an event (for example, a page request) occurs, the MVC paradigm handles it as follows:
- The Event is intercepted by the Controller.
- The Controller evaluates the event and maps it to the appropriate handler in the Model.
- The Model carries out the action or state change, and the result is returned to the Controller.
- The Controller determines the appropriate View to be displayed, and causes the application to forward to that View.
- The View, when loading, may retrieve data from the Model through a data interface, but it is unable to execute actions (such as a database call) directly.
- The View is displayed to the user.
The separation of components created by the MVC pattern helps eliminate UI dependencies, as each component is responsible for a separate set of logically grouped tasks.
The Struts project's sole purpose was to bring the MVC pattern to the J2EE platform, to provide developers with a flexible framework for building extensible applications. After a year of development, Struts arose to fill the MVC void in the J2EE world. The Struts architecture acts as a wrapper for Java applications and divides its code into the Model, View, and Controller defined in the MVC pattern.
Struts implements MVC so that a single controller governs the application events. These incoming and outgoing events can be intercepted by filters that modify and mold the incoming data for specific uses. This is very similar to the Front Controller with Intercepting Filter variation of the MVC pattern.
The Model is fairly open-ended, allowing for a variety of third-party implementations. One of the most common implementations leverages Enterprise JavaBeans (EJBs) for data persistence, transactional support, and a standard framework. Other Model implementations include Data Access Objects (DAO), Plain Old Java Objects (POJO's), and other forms of logic encapsulation and data persistence (for example, Object Relational Mapping, or ORM, tools). All of these Model implementations must be developed in Java, or have a Java-based interface.
The View components are usually implemented as JavaServer Pages (JSPs). JSP is primarily a markup language that extends on HTML and Servlets. The JSP markup language supports the development of custom tag libraries. A custom tag library represents an HTML-like tag that, when used in a JSP, accesses a back-end class and performs some specific function. Custom tag libraries are used in Struts to extend JSP and facilitate the seamless interaction between the View and the Model.
The default controller is centralized into a single Servlet instance, which handles all client requests and executes the appropriate action for each request. The Struts framework provides a default implementation for the Controller. The developer's primary task is to register and implement the actions that occur as the result of an event. Actions are defined as a common base class that must be extended, and all events and responses are registered in an XML configuration file. For example, action mapping must be entered so that the incoming URI of a request can be properly mapped to the appropriate action class, and action classes have to be implemented to handle the incoming request. Also, action forwarding conditions must also be registered so that the Controller knows which View is needed when an Action is completed.
Struts page load-processing sequence
A typical request goes through the following multi-stage process.
Figure 4. Struts Page Processing Sequence
- A browser request is submitted to and handled by the Action Servlet class, which has one physical instance per server.
- The Action Servlet calls the Request Processor to prepare the request and response objects, determine action mapping etc.
- The Request Processor creates (or re-uses) the corresponding Action Form bean, populates it with the input fields from the html form and invokes form validation (if required).
- Using the action mapping in its config file, the Request Processor passes control to the appropriate Action Class. The Struts framework pools instances of Action classes; therefore, if the action has already been requested, it will be retrieved from the instance pool, as opposed to being created with every request. However, without multi-threading, this can lead to significant collisions when multiple users are accessing the same action.
- The Action Class invokes business logic beans and passes the Action Form (or a portion of it) to the model. The model may be EJB-based, or consist of plain Java objects.
- After the action completes execution, it is returned as an Action Forward object which determines the target of the request.
- Control is finally returned to the Request Processor, which forwards the request to the appropriate target.
- The JSP page may get data from the Model through a set of tag libraries.
Although this process may look complicated at first glance, most of these steps happen automatically, thanks to the Struts framework. As a developer, you are primarily concerned with setting up the mappings and writing the action classes.
ASP.NET MVC Implementation
In an effort to increase productivity and help developers build reliable and scaleable solutions, ASP.NET provides a default, out-of-the-box MVC framework. MVC implementation for ASP.NET has one controller per page, and is known as the Page Controller pattern.
The Page Controller pattern in ASP.NET is implemented in such a fashion that capturing an event and relaying it from the client to the appropriate handler remains unseen and automatic for developers. Developers can concern themselves with implementing only the actions necessary to deal with the events, rather than developing a more complicated handler.
Each ASP.NET page is divided into two parts: a View that contains the various controls, and the code-behind, which acts as the Controller. The code-behind contains functions for every action that the Controller can take when events are generated. These functions can be tied to the Model and resulting View as required.
The Model can consist of simple objects (such as C# classes) or managed components (such as Enterprise Services, or COM+). In addition, a wide variety of services and systems are natively accessible from the framework, including transactional support (ADO.NET and COM+), data persistence (ADO.NET) and caching. The ASP.NET Model differs from the Struts Model in that it can be implemented using any of the many languages that are supported by the .NET Framework, including C#, VB.NET, J#, and Perl.
The View in a .NET Framework-based application is implemented using ASP.NET and any of the .NET languages. ASP.NET extends ASP by providing support for compiled languages (instead of script) and automatically generating browser-specific output. ASP.NET provides an extensive and customizable control library for seamless interaction between the Controller and the View. ASP.NET offers several advantages over JSP, including a "drag and drop" development interface (Visual Studio .NET or Web Matrix), improved controls, and automatic browser-dependent HTML generation. That is, a control in ASP.NET will automatically determine the browser type and generate browser specific code. Similar functionality does not exist in the J2EE framework; however, some Java vendors have developed tag libraries with similar functionality.
The Page Controller approach is far simpler for developers to implement than Struts, as the controller is built directly into the page. The developer only has to deal with the events of a single page, rather than mapping the events for an entire application. This can be a very powerful implementation, as it allows for Views and Controllers to take advantage of inheritance. However, if this implementation is not architected properly, the application can also become extremely complex and difficult to maintain, reintroducing some of the configuration issues mentioned earlier. We will discuss preventative measures in the "Centralizing the Page Controller" section, and alternatives in the "Front Controller" and "User Interface Process" sections.
ASP.NET page processing—under the hood look
Much of the ASP.NET Page processing is hidden to the developer; however. it is modular and available in the event that a developer needs to use it (for instance, Security). In this section, we will go through the behind-the-scenes actions that occur when an ASP.NET page is processed.
Figure 5. ASP.NET page processing sequence
When IIS receives a request, it forwards it to the ASP.NET worker process called aspnet_wp.exe. At the moment, there is only one worker process per machine, but this is changing with ASP.NET version 2.0. You will be able to run multiple worker processes on the same machine, allowing you to completely isolate Web sites running on the same physical hardware.
Once the worker process receives the request, it uses an instance of the
HttpRuntime to handle it. The
HttpRuntime examines the incoming request to figure out which application it is for, and uses an object factory to create an instance of
HttpApplication contains a collection of
HttpModules, which inspect the incoming request and perform common tasks such as authentication and state management. Once the
HttpModules have performed their task, the
HttpApplication uses another object factory to create an instance of the
HttpHandler. To improve performance,
HttpHandler objects are pooled.
This is the point where your actual ASP.NET pages come in. By default, all pages extend the
System.Web.UI.Page class, which implements the
IHttpHandler interface. When your page starts loading, various events are fired and your code-behind starts executing. From your code-behind you can then make calls to your Business Façade or Business Logic layer to perform various business functions, such as change of address, adding new contact names, and so on.