asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Application Architecture: An N-Tier Approach - Part 2
By Robert Chartier
Rating: 4.2 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    This article is the second in a two-part series regarding developing the client server model using an N-tier approach. Throughout this article, I will be making many references to Part 1 (see http://www.15seconds.com/issue/011023.htm). You might consider rereading it before proceeding.

    The purpose of this article is to take the theoretical explanation given in Part 1, and show you how to apply it to your future applications. The example I have chosen is a fairly simple one, an address book. I will walk you through the development and integration of an N-tier model, showing you how it can be easy to implement a new Database Management System (DBMS) (unplug your current database and plug in a new one), and finally how to take your existing middle tier and deploy it on to a new platform (Web Services). My platform of choice is the .NET Framework Beta 2, using Visual Studio Release Candidate 1 (RC1), using the new .NET language, C#. It is important for you to not focus on the actual code of this article. That is irrelevant. Instead, put more focus on the way in which I developed and separated each tier of the application. You must also realize that I'm not going to formally walk you through any distinct stages of an application systems development life cycle, just the bare bones needed for the completion of this application. I will also not cover extensive error-handling routines, and security is always a major issue with any application so I have put in place only the minimum needed for this article. Please consult one of the many resources regarding these features and implement them at your leisure.

  • download complete source code

    Section 1: Brief Needs Analysis

    Here's a bit of information about what we are going to develop.

    A. The Business Portion of the Application:

    1. It is going to be an address book.
    2. All users must login in order to gain access to their address book.
    3. An address book has to have one owner, but can be owned and administrated by many.
    4. An owner is a contact of the address book.
    5. An address book can have one to many contacts.
    6. Each address book has a name and a description.
    7. Each user can have and belong to many address books.
    8. A contact can only appear once in any address book.

    B. The Implementation of the Application:

    1. It is going to be available over normal HTTP (a Web site).
    2. It is expected to be available over eXtensible Markup Language (XML) Web Services (via HTTP).
    3. We do initial development with Microsoft Access 2000.
    4. We want to be able to easily upgrade our database from Access to SQL Server 2K after development is done.

    The rest of this article will work down from our Data Tier to the Presentation tier, in that order.

    Section 2: Defining and Creating the Data Tier

    Think of the Data Tier as your DBMS (a.k.a. Access or SQL Server). We must create our database so that it will solve our requirements listed above and at the same time not limit our future development. Usually the database administrator does this work, but since this is a fairly small application, we will assume that role and throw the database together.

    We start by opening up Access and creating a blank database. I called it "addressbook.mdb." Let's begin with the Contact List that will represent your standard user. I decided to use the Email Address as our Primary Key1 (PK) (explanation footnoted at end of section). (I have included the Access database as a part of the download for this article).

    Figure 2.1 Contacts Table

    Key

    name

    type

    size

    defaultvalue

    required

    zero length

    indexed

    PK

    emailaddress

    text

    150

     

    yes

    no

    yes

     

    password

    text

    10

     

    no

    yes

    yes, duplicates ok

     

    first

    text

    50

     

    no

    yes

    no

     

    last

    text

    50

     

    no

    yes

    no

     

    areacode

    Number

    Integer

    0

    no

     

    no

     

    phone

    Number

    Long Integer

    0

    no

     

    no

     

    fax

    Number

    Long Integer

    0

    no

     

    no

     

    address

    text

    200

     

    no

    yes

    no

     

    address1

    text

    200

     

    no

    yes

    no

    city

    text

    50

    no

    yes

    no

     

    zip

    text

    10

     

    no

    yes

    no

     

    state

    text

    75

     

    no

    yes

    no

     

    country

    text

    50

     

    no

    yes

    no

    The second table we will throw together is the Address Book table. This is going to represent each individual address book in the system.

    Figure 2.2 AddressBook Table

    Key

    name

    type

    size

    defaultvalue

    required

    zero length

    indexed

    PK

    bookid

    autonumber

    Long Integer

     

    yes

    no

    yes, no duplicates

     

    name

    text

    50

     

    yes

    no

    yes, duplicates ok

     

    description

    text

    250

     

    no

    yes

    no

    If we take a look at both of these tables, you will see that the there is no relationship between them. What we must do is define what is called an "intersection table." This is simply a table that will contain both of the PKs from each table, which is used to relate each table to each other. We use this "intersection" to assign a contact to an address book, for example, and other things.

    Figure 2.3 AddressBookContacts Table

    Key

    name

    type

    size

    defaultvalue

    required

    zero length

    indexed

    FK (PK)

    bookid

    number

    Long Integer

    0

    yes

     

    yes, duplicates ok

    FK (PK)

    emailaddress

    text

    150

     

    yes

    no

    yes, duplicates ok

     

    relationship

    Number

    byte

    0

    yes

     

    no

    Let me run you through each of the fields for this table. The "bookid" field is the PK from the AddressBook table, represented in this table as a Foreign Key1 (FK). The "emailaddress" field is the PK from the Contacts table and is also represented in this table as a FK. Together, these two make up the PK for this table. This allows us to have any number of contacts belonging to any address book. It also allows us to not allow a contact to appear more than once in a given address book. The last field,"relationship", is used to indicate the relationship between these records. Let's create a new table to describe these relationships. Figure 2.3.1 Relationship Table

    Key

    name

    type

    size

    defaultvalue

    required

    zero length

    indexed

    PK

    id

    AutoNumber

    Long Integer

     

     

     

    yes, no duplicates

     

    name

    text

    100

     

    yes

    no

    no

     

    description

    text

    255

     

    no

    yes

    no

    You may be asking yourself what exactly is a relationship and why do we need it? A relationship, in this case, is how we relate the specific contact to the address book. An example would be "Owner", or "Administrator", or "Contact". Here is some sample data for this table:

    Figure 2.4 Sample Relationship Data

    id

    name

    description

    1

    Owner

    An owner of this address book

    2

    Contact

    A contact belonging to this address book

    3

    Administrator

    An administrator of the address book. These users can edit, add, and remove contacts of the book, but cannot delete the entire book itself.

    To the question "Why do we need this?", I say take a look back at our requirements, specifically A3. It states "An Address Book has to have one owner, but can be owned and administrated by many." This will allow us to enable the system to have any address book with a designated owner, but also an administrator who would have some of the same abilities as the owner. This introduces a model where we can grant access to different users to perform different actions to our address books and contacts.All we need is to have at least one owner defined for each address book, and the rest can be optionally used in order to start associating contacts to each address book. This will also allow us to define more roles in the system and allow for contacts to have different sets of access to any given address book. I have found this to be a very flexible model.

    1 Keys -- primary and foreign -- They are used in establishing relationships between records. The primary key in a table is the data field in each record, which is guaranteed to be unique and which can therefore be used to establish relationships between each record in that table and other records in other tables. The foreign key in a table is a primary key for records in another table. Foreign key values don't have to be unique within a table, thus allowing a one- (primary key) -to-many (foreign key) relationship to exist (see http://magni.grainger.uiuc.edu/lis450ds/introsql.htm).

    Section 3: Setting Up the Development Environment

    Let's start the Visual Studio.NET development environment and create a new ASP.NET application. I chose to start out with an ASP.NET application because our primary interface for this application will be through the Web and ASP.NET. In the Visual Studio.NET start page, we first hit the "New Project" (1) button. Personally, I prefer using C# rather than Visual Basic so I chose "Visual C# Projects (2), and using a "ASP.NET Web Application" (3), I created the application at the location http://localhost/AddressBook (4).

    Figure 3.1 Creating the ASP.NET Application

    (Click here to generate larger version.)

    The next application we will add to our solution will be used to contain the Data Access tier. In the Solution Explorer, right click the "AddressBook" Solution, choose "Add", "New Project". This new project will be a reusable C# (1), Class Library (2), with the name "DataAccess" (3).

    Figure 3.2 Adding the Data Access Tier

    (Click here to generate larger version.)

    Change the name of the Class File Name. This can be found in the Solution Explorer, Click the "Class1.cs" file, and view the Properties pane. You will see the "File Name" attribute. Rename it "AddressBookDA.cs".

    The next application we will add is our Business tier. Follow the same procedure as we did to add the Data Access tier, but give it the name "Business" (3). Don't forget to change the name of its default class to "AddressBookBiz.cs". And finally, since we wish to expose this entire application over Web Services (HTTP), we will also add in a C# ASP.NET Web Service (3) application. Make sure you change its location to http://localhost/AddressBookService. This time rename the "Service1.asmx" file to "AddressBook.asmx".

    The next step will be to set up the dependencies for each application. See Figure 1.1 at http://www.15seconds.com/issue/011023.htm and the dependencies will become pretty clear. The Business tier will depend on the Data Access tier, and above that the Presentation tier (ASP.NET application, and Web Services application) will depend on the Business tier.

    Right click on the "Business" application, and choose "Add Reference", "Projects" tab, and then select the "DataAccess" application. Hit the "Select" button to add it to the "Select Components" section. Repeat this process for both the "Address Book" application and the "AddressBookService" application, but for each of these, choose the "Business" project. The next few steps will guide you through the setup for each application.

    Section 4: Creating the Data Access Tier

    Looking back at Part 1 of this article (see http://www.15seconds.com/issue/011023.htm), we see that the Data Access tier is used to simply interface with our database. It contains no business logic or constraints. It merely provides a way in which to interact with the database (Insert, Select, Delete, and Update). In other words, it allows us to create a set of useful and specific routines to be able to perform those four actions on our tables.

    For this project, we need to create a namespace (see http://stylusinc.com/website/c_sharp.htm#Namespaces) that will encompass our Data Access tier. You will see, later in the article, that we will also be creating separate namespaces for the Business tier, etc...

    Most of this code is standard. To reiterate, the most important thing to remember for this tier is that it is a set of specific methods that are used to perform all of the database interactions. It must NOT contain any business logic or presentation items. If you wish, take a look in the AddressBookDA.cs file (part of download accompanying this article) to see what methods I exposed and how I perform the database operations with it now. Also pay close attention to how this has been created for an Access database. Since each DBMS we use in our careers has different capabilities, our Data Access tier will differ slightly for each. One of the nicest features of an N-tier architecture is the ability to easily switch this layer out when a new DBMS is put in place. I will develop this application based on an Access database. When the time comes to upgrade to SQL Server, or another quality DBMS, we will only need to modify our Data Access tier and the rest of the application will function unchanged. This means no more rooting though thousands of lines of code just to upgrade our in-line SQL to stored procedures.

    Section 5: Creating the Business Tier

    If you recall, the Business tier is where we place all of our custom business logic. For this address book application it will contain logic such as all of the items we find in Section 1, List A of our requirements. There will be no connections to any database and no presentation-specific code.

    In our Business tier we must expose a set of methods that will be useful and that will solve the business rules. It is also important to remember that these objects can be stateful for the lifetime of that specific object instance, but not stateful for the entire application's existence.

    We will create two separate classes within this AddressBookBiz namespace, one to represent an actual address book and another to represent our contacts.

    Let's drill down for each object and determine the methods and properties that we will expose.

    Figure 5.1 List of Objects' Properties and Methods

    AddressBook Object

     

     

     

    Properties

     

     

     

    Name

    Get

    Set

    Public

    bookid

    yes

    no

    yes

    name

    yes

    yes

    yes

    description

    yes

    yes

    yes

    relationshiplist*

    yes

    no

    yes

     

     

     

     

    Methods

     

     

     

    Name

    Description

    Parameters

    Public

    Default Constructor

    Creates an empty instance of the object

    none

    yes

    Overloaded Constructor

    Loads up the appropriate address book

    bookid

    yes

    Overloaded Constructor

    Creates a new address book

    name, description Contact

    yes

    loadAddressBook

    Internal method to load up the book. Called by other methods

    bookid

    no

    CanView

    Used to determine if the supplied user can view the book

    emailaddress

    yes

    GetContactsDataSet

    Gets a DataSet of the contacts for the Address object

    none

    yes

    AddNewContact

    Add a new contact to the current Address object

    Contact

    yes

    getLastError

    Simply returns the last error message

    none

    yes

    Validate

    Validates itself

    none

    yes

    Save

    Persists the current Address book to the database

    none

    yes

    AddContact

    Adds a contact to the current address book

    Contact, Relationship

    yes

     

     

     

     

    Contact Object

     

     

     

    Properties

     

     

     

    Name

    Get

    Set

    Public

    emailaddress

    yes

    no

    yes

    password

    yes

    yes

    yes

    first

    yes

    yes

    yes

    last

    yes

    yes

    yes

    areacode

    yes

    yes

    yes

    phone

    yes

    yes

    yes

    fax

    yes

    yes

    yes

    address

    yes

    yes

    yes

    address1

    yes

    yes

    yes

    city

    yes

    yes

    yes

    zip

    yes

    yes

    yes

    state

    yes

    yes

    yes

    country

    yes

    yes

    yes

     

     

     

     

    Methods

     

     

     

    Name

    Description

    Parameters

    Public

    Default Constructor

    Creates an empty instance of the object

    none

    yes

    Overloaded Constructor

    Loads up the appropriate user (login)

    emailaddress, password

    yes

    Overloaded Constructor

    Loads up the appropriate user

    emailaddress

    yes

    saveContact

    save the specific contact

    none

    no

    isUserValid

    Determines if the supplied user is valid or not

    none

    yes

    getAddressBooks

    Returns all of the user's address books (not preloaded)

    emailaddress

    yes

    getLastError

    Simply returns the last error message

    none

    yes

    Validate

    Validates itself

    none

    yes

    Save

    Persists the current contact to the database (if we can)

    none

    yes

    * I’ve decided to place this method, which merely returns a list of the available relationships, in the Address Book

    object in order to keep this article short and simple. Normally you would probably make the decision to create

    a new, separate object that would perform the needed relationship actions.

    ** Since each Address Book object will contain a collection of Contact Objects, our Contact Pointer is merely

    a reference to the current contact in our list.

    At this time, review my version of "AddressBookBiz.cs" (part of download). Pay attention to the Save method, and how it is first calling its own Validate method in order to ensure that it conforms to the established rules. (This will validate the object at the object level). Calling the Save method in the address book will validate the current address book, and then it will also call the Save method on each of its contacts, which will also call its own Validate method. In the end, if everything validates fine, it will all be persisted to the database.

    I want to quickly review what I did regarding security. In order to represent the current user (who is logged in), I needed a way to easily represent that user based on their login information. I used a basic symmetrical encryption algorithm (borrowed from http://www.4guysfromrolla.com/webtech/090501-1.shtml). This Namespace is based on the Cryptography base classes within .NET. To integrate it within our "business" application, I only needed to add it to our application (see previous examples on how to do this). Now, when the user is logged in, or authenticated against our database, I take their email address and their password, and join them into a single string, and then apply the encryption algorithm. This generates a Universal Unique (user) IDentifier (UUID). And when I need to determine who this UUID belongs to, I simply unencrypt the UUID, and then split the string back into the email address and the password and re-authenticate against my database. I created this in this fashion in order to simplify the security issues for this article. More help on cryptography can be found at http://www.demcom.com/english/steganos/htmlhelp/IDH_TECH_CRYPTOGRAPHY.htm or on many other Internet sites.

    I have completed most of the Business tier for you. I have left out some validation within each of the Property Get/Set methods. You should conform each method to what you expect for this application. For example, the Area Code property could be limited to only valid area codes (001 to 999), or the Country property could be limited to only a set of ISO-standard country codes. This has offered you a place to put all of your server-sided data validation. Another thing you may consider is looking into the validation tools within the .NET architecture. There are plenty of examples of this available on the Internet, just as long as you always keep in mind that validation MUST be performed on the server, and in your middle tier. You can optionally add it on the client with JavaScript or whatever is specific to your deployment platform. Adding validation to a single interface on the Presentation tier will only enable that specific interface to the system to take advantage of that validation. Your other interfaces will not be able to take advantage of this.

    Section 6: The ASP.NET Presentation Interface

    Now that we have a working infrastructure for our Address Book application, it is usually a small task to wrap this functionality within any Presentation interface we wish. It is only a matter of hooking into our Business tier. In order to keep things simple I have created a minimal amount of forms for you to review, each with a slim interface with no fancy pictures or formatting (my excuse -and apologies-- for the ugly design).

    Figure 6.1 List of ASP.NET Web Forms

    File Name

    Purpose

    Links To

    Login.aspx

    Allow user to login

    addressbooklist.aspx

    addressbooklist.aspx

    View list of a user’s address books

    createbook.aspx

     

     

    listusers.aspx

    createbook.aspx

    Create a new address book

    addressbooklist.aspx

    listusers.aspx

    List users within an address book

    createbook.aspx

     

     

    createuser.aspx

    createuser.aspx

    Create a new user within an address book

    listusers.aspx

    Take time now to review each file and the code behind each. First look at the login.aspx page that allows the user to log in and get their UUID, which gives them access to the rest of the application. Also, you may wish to start adding some functionality to the application. One important issue would be a way to initially create a user when no address books are defined. Personally I would create an application administrator (you), and create a global address book. And then each new user who is created (first time in) will be added to this global address book. And then each of these users can create/edit their own address books, etc... Take the logic from the createuser.aspx page and create your own, generic page to allow for this functionality. You could also consider adding in a feature to share contacts between address books. All you would really need is another table to represent the intersection between the shared contacts and the appropriate users.

    You can test this application by hitting the Start button in the Integrated Development Environment (IDE) or by going to "Debug", "Start" menu items.

    Section 7: The XML Web Services Presentation Interface

    By simply exposing the methods in our Business tier within the "AddressBookService" application, we can easily create a new interface into our application based on XML Web Services (or any other way, like Windows Forms, etc...). I have started this interface for you, but left it incomplete on purpose, to allow you to get your hands dirty by trying to port the rest of the Business tier to this new interface. All you really need to know is that each user will start by logging into the application and getting their UUID. Then each subsequent request will also pass this UUID in with the each method call. An alternative to this would be to use SOAP headers so that you do not need to expose the methods with the additional UUID string on each method.

    Conclusion

    During your examination of this article and the supplied code, you must always keep in mind what it was intended for -- a simple demonstration on how one would develop an N-tier application. You should take into consideration the concepts, more than the simple language or my choice of development platform/framework and environment. You can easily take and apply these concepts back to your ASP/COM or JSP/Bean environments; it really doesn't matter, just as long as you understand the usefulness of keeping things separate using this tiered approach.

    Some more things that I do recommend that you look into include:

    1. Refactoring (Extreme Programming -XP)

    2. Object -Orientated Design Patterns

    3. Systems Development Life Cycle

    4. Other

    I did my best to produce code, which is in good working order, but as with most applications, there will always be bugs. If you do find any problems, please send me an email and I'll try to make any fixes that people send in. Also, if you do take the time to create a Data Access tier for any other DBMS than Access, or maybe you finish the ASP.NET or Web Services interfaces, send me the code, and I'll add it to the library (giving you full credits of course!). If you do plan on completing any of this and you need help, drop me a line, and Ill be glad to lend a hand.

    I do have to admit that this is my first exposure to using VS.NET for any type of application. I have spent a lot of time with the .NET Framework and C#, but, specifically with this tool, this application was my first! Usually I take what Microsoft says with a grain of salt. But when they say that the .NET Framework and VS.NET increase productivity, they are very right. I believe I will finally make the switch out of simple Notepad (or IDM Computer Solution's UltraEdit) and into this development environment.

    This article is intended for all levels of developers, and I hope you find it useful.

    About the Author

    Robert Chartier has worked in the information technologies field for more than 7 years. While studying at college he began his career working as a software and hardware technician at the college, supporting a user base of thousands of students and hundreds of instructors. Once his college days were finished he moved on to full-time studies at a university in the lower mainland of British Columbia, and landed a full-time job developing large projects for distribution on many platforms, mediums, and languages.

    His next position enabled him to tap into the Internet development market on a larger and more focused scale. In his spare time he began writing and producing content for developer-specific sites focusing on Microsoft technologies (ASP, COM/COM+, etc.). He has also been a part of many open forums on cutting-edge technologies, such as the .NET Framework and Web Services (SOAP), and has been invited to speak at large developer conferences and contribute to many technical publications.

    His next step was to take a position with a large B2B training marketplace, where he developed many tools, including a very comprehensive search engine with custom business rules for weighting, sorting, and analysis (COM+). This led him into strong development with beta versions of Commerce Server 2000 and BizTalk Server 2000. His next opportunity included a large Internet development effort using technologies such as JSP (Java Beans, J2EE), Oracle, WebLogic, etc. His current position as vice president of technology for Santra Technology (http://www.santra.com) has allowed him to focus more time on the Web Services market space. Santra is an award-winning, industry leader in this cutting-edge technology, focusing on Web Service monitoring, alerting, and performance measurement.

    Robert Chartier can be reached at rob@aspfree.com.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Other Articles
    Jul 7, 2005 - Hosting Indigo Web Services
    In the second article of his series on Indigo web services, Chris Peiris explains how to host an Indigo web service and examines the IIS, self hosting, and Windows Activation Service hosting options. He then provides step-by-step instructions and sample code for an IIS-hosted and self-hosted Indigo web service.
    [Read This Article]  [Top]
    Jun 8, 2005 - Indigo Programming Model
    In the first part of his series on Microsoft Indigo, Chris Peiris examines the basics of SOA, explains how Indigo fits into the picture and the problems it solves. He then introduces Indigo's programming model and finishes by building a sample Indigo web service using the Microsoft .Net Framework 2.0.
    [Read This Article]  [Top]
    Nov 10, 2004 - Business Intelligence with Microsoft SQL Server Reporting Services - Part 3
    Adnan Masood concludes his discussion of Microsoft SQL Server Analysis services and Microsoft SQL Server Reporting services. In the final part, he discusses Reporting Server web services and using custom code in reports.
    [Read This Article]  [Top]
    Jul 8, 2004 - Using IE's Web Service Behavior To Create Rich ASP.NET Applications
    This article explains the features of the IE Web service behavior and shows how to asynchronously communicate with an ASP.NET Web service directly from the client.
    [Read This Article]  [Top]
    Jul 6, 2004 - Using .NET and Excel 2003 To Validate E-Mails
    Calvin Luttrell shows how to validate e-mail addresses stored in Excel 2003 and provides a special function for solving that pesky problem Yahoo! mail servers cause.
    [Read This Article]  [Top]
    Jun 9, 2004 - Modifying Web Services Documentation
    This short article describes a quick and easy way to provide some security to an ASP.NET Web service by modifying its associated documentation file.
    [Read This Article]  [Top]
    Jun 2, 2004 - Kerberos Authentication with Web Services Enhancements 2.0
    Kerberos authentication is the cornerstone of Windows operating system authentication architecture. Web Services Enhancement 2.0 (WSE 2.0) extends Kerberos support to ASP.NET Web services. Chris Peiris explains the support for this new feature in WSE 2.0.
    [Read This Article]  [Top]
    Dec 15, 2003 - Realizing a Service-Oriented Architecture with .NET
    Chip Irek examines the architectural issues and component design issues of building a .NET application in a service-oriented architecture.
    [Read This Article]  [Top]
    Nov 24, 2003 - Consuming Asynchronous Web Services
    Thiru Thangarathinam shows how to use asynchronous Web services, Windows Service applications, server-based timer components and .NET XML API classes to create high-performance, scalable, and flexible applications.
    [Read This Article]  [Top]
    Nov 12, 2003 - Implementing Paging and XSLT Extensions Using XSLT in .NET - Part 2
    Part one showed how to transform XML data into HTML by using an XSL stylesheet from within a .NET application. This part explains how to make use of XSLT Extension objects and invoke a C# class method from an XSL stylesheet.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers