Creating multi-tiered applications

来源:互联网 发布:油皮适合的面霜知乎 编辑:程序博客网 时间:2024/05/17 07:54
 

From:Delphi 5 Developer’s Guide》

Chapter 14
Creating multi-tiered applications

this chapter describes how to create a multi-tiered, client/server database application. A multi-tiered client/server application is partitioned into logical units which run in conjunction on separate machines. Multi-tiered applications share data and communicate with one another over a local-area network or even over the Internet. They provide many benefits, such as centralized business logic and thin client applications.

In its simplest form, sometimes called the "three-tiered model," a multi-tiered application is partitioned into thirds:

  • Client application: provides a user interface on the user's machine.
  • Application server: resides in a central networking location accessible to all clients and provides common data services.
  • Remote database server: provides the relational database management system (RDBMS).

In this three-tiered model, the application server manages the flow of data between clients and the remote database server, so it is sometimes called a "data broker." With Delphi you usually only create the application server and its clients, although, if you are really ambitious, you could create your own database back end as well.

In more complex multi-tiered applications, additional services reside between a client and a remote database server. For example, there might be a security services broker to handle secure Internet transactions, or bridge services to handle sharing of data with databases on platforms not directly supported by Delphi.

Delphi support for multi-tiered applications is based on the Multi-tier Distributed Application Services Suite (MIDAS). This chapter focuses on creating a three-tiered database application using the MIDAS technology. Once you understand how to create and manage a three-tiered application, you can create and add additional service layers based on your needs.

Advantages of the multi-tiered database model

The multi-tiered database model breaks a database application into logical pieces. The client application can focus on data display and user interactions. Ideally, it knows nothing about how the data is stored or maintained. The application server (middle tier) coordinates and processes requests and updates from multiple clients. It handles all the details of defining datasets and interacting with the remote database server.

The advantages of this multi-tiered model include the following:

  • Encapsulation of business logic in a shared middle tier. Different client applications all access the same middle tier. This allows you to avoid the redundancy (and maintenance cost) of duplicating your business rules for each separate client application.
  • Thin client applications. Your client applications can be written to make a small footprint by delegating more of the processing to middle tiers. Not only are client applications smaller, but they are easier to deploy because they don't need to worry about installing, configuring, and maintaining the database connectivity software (such as the Borland Database Engine). Thin client applications can be distributed over the internet for additional flexibility.
  • Distributed data processing. Distributing the work of an application over several machines can improve performance because of load balancing, and allow redundant systems to take over when a server goes down.
  • Increased opportunity for security. You can isolate sensitive functionality into tiers that have different access restrictions. This provides flexible and configurable levels of security. Middle tiers can limit the entry points to sensitive material, allowing you to control access more easily. If you are using HTTP, CORBA, or MTS, you can take advantage of the security models they support.

Understanding MIDAS technology

MIDAS provides the mechanism by which client applications and application servers communicate database information. Using MIDAS requires MIDAS.DLL, which is used by both client and server applications to manage datasets stored as data packets. Building MIDAS applications may also require the SQL explorer to help in database administration and to import server constraints into the Data Dictionary so that they can be checked at any level of the multi-tiered application.

Note: You must purchase server licenses for deploying your MIDAS applications.

MIDAS-based multi-tiered applications use the components on the MIDAS page of the component palette, plus a remote data module that is created by a wizard on the Multitier page of the New Items dialog. These components are described in Table 14.1:

Table 14.1   MIDAS components

Component

Description

remote data modules

Specialized data modules that can act as a COM Automation server or CORBA server to give client applications access to any providers they contain. Used on the application server.

provider component

A data broker that provides data by creating data packets and resolves client updates. Used on the application server.

client dataset component

A specialized dataset that uses MIDAS.DLL to manage data stored in data packets.

connection components

A family of components that locate the server, form connections, and make the IAppServer interface available to client datasets. Each connection component is specialized to use a particular communications protocol.

Overview of a MIDAS-based multi-tiered application

The following numbered steps illustrate a normal sequence of events for a MIDAS-based multi-tiered application:

  1. A user starts the client application. The client connects to the application server (which can be specified at design time or runtime). If the application server is not already running, it starts. The client receives an IAppServer interface from the application server.
  2. The client requests data from the application server. A client may request all data at once, or may request chunks of data throughout the session (fetch on demand).
  3. The application server retrieves the data (first establishing a database connection, if necessary), packages it for the client, and returns a data packet to the client. Additional information, (for example, about data constraints imposed by the database) can be included in the metadata of the data packet. This process of packaging data into data packets is called "providing."
  4. The client decodes the data packet and displays the data to the user.
  5. As the user interacts with the client application, the data is updated (records are added, deleted, or modified). These modifications are stored in a change log by the client.
  6. Eventually the client applies its updates to the application server, usually in response to a user action. To apply updates, the client packages its change log and sends it as a data packet to the server.
  7. The application server decodes the package and posts updates in the context of a transaction. If a record can't be posted to the server (for example, because another application changed the record after the client requested it and before the client applied its updates), the application server either attempts to reconcile the client's changes with the current data, or saves the records that could not be posted. This process of posting records and caching problem records is called "resolving."
  8. When the application server finishes the resolving process, it returns any unposted records to the client for further resolution.
  9. The client reconciles unresolved records. There are many ways a client can reconcile unresolved records. Typically the client attempts to correct the situation that prevented records from being posted or discards the changes. If the error situation can be rectified, the client applies updates again.
  10. The client refreshes its data from the server.

The structure of the client application

To the end user, the client application of a multi-tiered application looks and behaves no differently than a traditional two-tiered application that uses cached updates. Structurally, the client application looks a lot like a flat-file single-tiered application. User interaction takes place through standard data-aware controls that display data from a client dataset component. For detailed information about using the properties, events, and methods of client datasets, see "Creating and using a client dataset."

Unlike in a flat-file application, the client dataset in a multi-tiered application obtains its data through the IAppServer interface on the application server. It uses this interface to post updates to the application server as well. For more information about the IAppServer interface, see "Using the IAppServer interface". The client gets this interface from a connection component.

The connection component establishes the connection to the application server. Different connection components are available for using different communications protocols. These connection components are summarized in the following table:

Table 14.2   Connection components

Component

Protocol

TDCOMConnection

DCOM

TSocketConnection

Windows sockets (TCP/IP)

TWebConnection

HTTP

TOLEnterpriseConnection

OLEnterprise (RPCs)

TCorbaConnection

CORBA (IIOP)

Note: Two other connection components, TRemoteServer and TMIDASConnection, are provided for backward compatibility.

For more information about using connection components, see "Connecting to the application server".

The structure of the application server

The application server includes a remote data module that provides an IAppServer interface, which client applications use to communicate with data providers. There are three types of remote data modules:

  • TRemoteDataModule: This is a dual-interface Automation server. Use this type of remote data module if clients use DCOM, HTTP, sockets, or OLEnterprise to connect to the application server, unless you want to install the application server with MTS.
  • TMTSDataModule: This is a dual-interface Automation server. Use this type of remote data module if you are creating the application server as an Active Library (.DLL) that is installed with MTS. You can use MTS remote data modules with DCOM, HTTP, Sockets, or OLEnterprise.
  • TCorbaDataModule: This is a CORBA server. Use this type of remote data module to provide data to CORBA clients.

As with any data module, you can include any nonvisual component in the remote data module. In addition, the remote data module includes a dataset provider component for each dataset the application server makes available to client applications. A dataset provider

  • Receives data requests from the client, fetches the requested data from the database server, packages the data for transmission, and sends the data to the client dataset. This activity is called "providing."
  • Receives updated data from the client dataset, applies updates to the database or source dataset, and logs any updates that cannot be applied, returning unresolved updates to the client for further reconciliation. This activity is called "resolving."

Often, the provider uses BDE- or ADO-enabled datasets such as you find in a two-tiered application. You can add database and session components as needed, just as in a BDE-based two-tiered application, or ADO connection components as in an ADO-based two-tiered application.

Note: Do not confuse the ADO connection component, which is analogous to a database component in a BDE-based application, with the connection components used by client applications in a multitiered application.

For more information about two-tiered applications, see "Building one- and two-tiered applications".

If the application server is MTS-enabled, the MTS data module includes events for when the application server is activated or deactivated. This permits the application server to acquire database connections when activated and release them when deactivated.

Using MTS

Using MTS lets your remote data module take advantage of

  • MTS security. MTS provides role-based security for your application server. Clients are assigned roles, which determine how they can access the MTS data module's interface. The MTS data module implements the IsCallerInRole method, which you lets you check the role of the currently connected client and conditionally allow certain functions based on that role. For more information about MTS security, see "Role-based security".
  • Database handle pooling. MTS data modules automatically pool database connections so that when one client is finished with a database connection, another client can reuse it. This cuts down on network traffic, because your middle tier does not need to log off of the remote database server and then log on again. When pooling database handles, your database component should set the KeepConnection property to False, so that your application maximizes the sharing of connections.
  • MTS transactions. When using MTS, you can integrate your own MTS transactions into the application server to provide enhanced transaction support. MTS transactions can span multiple databases, or include functions that do not involve databases at all. For more information about MTS transactions, see "Managing transactions in multi-tiered applications".
  • Just-in-time activation and as-soon-as-possible deactivation. You can write your MTS server so that remote data module instances are activated and deactivated on an as-needed basis. When using just-in-time activation and as-soon-as-possible deactivation, your remote data module is instantiated only when it is needed to handle client requests. This prevents it from tying up database handles when they are not in use.

    Using just-in-time activation and as-soon-as-possible deactivation provides a middle ground between routing all clients through a single remote data module instance, and creating a separate instance for every client connection. With a single remote data module instance, the application server must handle all database calls through a single database connection. This acts as a bottleneck, and can impact performance when there are many clients. With multiple instances of the remote data module, each instance can maintain a separate database connection, thereby avoiding the need to serialize database access. However, this monopolizes resources because other clients can't use the database connection while it is associated with another client's remote data module.

To take advantage of transactions, just-in-time activation, and as-soon-as-possible deactivation, remote data module instances must be stateless. This means you must provide additional support if your client relies on state information. For example, the client must pass information about the current record when performing incremental fetches. For more information about state information and remote data modules in multi-tiered applications, see "Supporting state information in remote data modules".

By default, all automatically generated calls to an MTS data module are transactional (that is, they assume that when the call exits, the MTS data module can be deactivated and any current transactions can be committed or rolled back). You can write an MTS data module that depends on persistent state information by setting the AutoComplete property to False, but it will not support transactions, just-in-time activation, or as-soon-as-possible deactivation.

Warning: When using MTS, database connections should not be opened until the remote data module is activated. While developing your application, be sure that all datasets are not active and the database is not connected before running your application. In the application itself, you must add code to open database connections when the data module is activated and to close them when the data module is deactivated.

Pooling remote data modules

Object pooling allows you some of the benefits available from MTS when you are not using DCOM. Under object pooling, you can limit the number of instances of your remote data module that are created. This limits the number of database connections that you must hold, as well as any other resources used by the remote data module.

When the server receives client requests, it passes them on to the first available remote data module in the pool. If there is no available remote data module, it creates a new one (up to a maximum number that you specify). This provides a middle ground between routing all clients through a single remote data module instance (which can act as a bottleneck), and creating a separate instance for every client connection (which can consume many resources).

If a remote data module instance in the pool does not receive any client requests for a while, it is automatically freed. This prevents the pool from monopolizing resources unless they are used.

Because a single instance of a remote data module potentially handles requests from several clients, it must not rely on persistent state information. See "Supporting state information in remote data modules" for more information on how to ensure that your remote data module is stateless.

To take advantage of object pooling, your remote data module must override the UpdateRegistry method. In the overridden method, you can call RegisterPooled when the remote data module registers and UnregisterPooled when the remote data module unregisters.

You can only take advantage of object pooling when the connection is formed using HTTP.

Using the IAppServer interface

Remote data modules on the application server support the IAppServer interface. Connection components on client applications look for this interface to form connections.

IAppServer provides the bridge between client applications and the provider components in the application server. Most client applications do not use IAppServer directly, but invoke it indirectly through the properties and methods of the client dataset. However, when necessary, you can make direct calls to the IAppServer interface by using the AppServer property of the client dataset.

Table 14.3 lists the methods of the IAppServer interface, as well as the corresponding methods and events on the provider component and the client dataset. These IAppServer methods include a Provider parameter to indicate which provider on the application server should provide data or resolve updates. In addition, most methods include an OleVariant parameter called OwnerData that allows the client application and application server to pass custom information back and forth. OwnerData is not used by default, but is passed to all event handlers so that you can write code that allows your application server to adjust for this information before and after each client call.

Table 14.3   AppServer interface members

IAppServer

provider component

TClientDataSet

AS_ApplyUpdates method

ApplyUpdates method,
BeforeApplyUpdates event,
AfterApplyUpdates event

ApplyUpdates method,
BeforeApplyUpdates event,
AfterApplyUpdates event.

AS_DataRequest method

DataRequest method,
OnDataRequest event

DataRequest method.

AS_Execute method

Execute method,
BeforeExecute event,
AfterExecute event

Execute method,
BeforeExecute event,
AfterExecute event.

AS_GetParams method

GetParams method,
BeforeGetParams event,
AfterGetParams event

FetchParams method,
BeforeGetparams event,
AfterGetParams event.

AS_GetProviderNames method

Used to identify all available providers.

Used to create a design-time list for ProviderName property.

AS_GetRecords method

GetRecords method,
BeforeGetRecords event,
AfterGetRecords event

GetNextPacket method,
Data property,
BeforeGetRecords event,
AfterGetRecords event

AS_RowRequest method

RowRequest method,
BeforeRowRequest event,
AfterRowRequest event

FetchBlobs method,
FetchDetails method,
RefreshRecord method,
BeforeRowRequest event,
AfterRowRequest event

Choosing a connection protocol

Each communications protocol you can use to connect your client applications to the application server provides its own unique benefits. Before choosing a protocol, consider how many clients you expect, how you are deploying your application, and future development plans.

Using DCOM connections

DCOM provides the most direct approach to communication, requiring no additional runtime applications on the server. However, because DCOM is not included with Windows 95, client machines may not have DCOM installed.

DCOM provides the only approach that lets you use MTS security. MTS security is based on assigning roles to the callers of MTS objects. When calling into MTS using DCOM, DCOM informs MTS about the client application that generated the call. MTS can then accurately determine the role of the caller. When using other protocols, however, there is a runtime executable, separate from the application server, that receives client calls. This runtime executable makes COM calls into the application server on behalf of the client. MTS can't assign roles to separate clients because, as far as MTS can tell, all calls to the application server are made by the runtime executable. For more information about MTS security, see "Role-based security".

Using Socket connections

TCP/IP Sockets let you create lightweight clients. For example, if you are writing a Web-based client application, you can't be sure that client systems support DCOM. Sockets provide a lowest common denominator that you know will be available for connecting to the application server. For more information about Sockets, see "Working with sockets".

Instead of instantiating the remote data module directly from the client (as happens with DCOM), sockets use a separate application on the server (ScktSrvr.exe), which accepts client requests and instantiates the remote data module using COM. The connection component on the client and ScktSrvr.exe on the server are responsible for marshaling IAppServer calls.

Note: ScktSrvr.exe can run as an NT service application. Register it with the Service manager by starting it using the -install command line option. You can unregister it using the -uninstall command line option.

Before you can use a socket connection, the application server must register its availability to clients using a socket connection. By default, all new remote data modules automatically register themselves by adding a call to EnableSocketTransport in the UpdateRegistry method. You can remove this call to prevent socket connections to your application server.

Note: Because older servers did not add this registration, you can disable the check for whether an application server is registered by unchecking the Connections|Registered Objects Only menu item on ScktSrvr.exe.

When using sockets, there is no protection on the server against client systems failing before they release a reference to interfaces on the application server. While this results in less message traffic than when using DCOM (which sends periodic keep-alive messages), this can result in an application server that can't release its resources because it is unaware that the client has gone away.

Using Web connections

HTTP lets you create clients that can communicate with an application server that is protected by a "firewall". HTTP messages provide controlled access to internal applications so that you can distribute your client applications safely and widely. Like Socket connections, HTTP messages provide a lowest common denominator that you know will be available for connecting to the application server. For more information about HTTP messages, see "Creating Internet server applications".

Instead of instantiating the remote data module directly from the client (as happens with DCOM), HTTP-based connections use a Web server application on the server (httpsrvr.dll) which accepts client requests and instantiates the remote data module using COM. Because of this, they are also called Web connections. The connection component on the client and httpsrvr.dll on the server are responsible for marshaling IAppServer calls.

Web connections can take advantage of the SSL security provided by wininet.dll (a library of internet utilities that runs on the client system). Once you have configured the Web server on the server system to require authentication, you can specify the user name and password using the properties of the Web connection component.

As an additional security measure, the application server must register its availability to clients using a Web connection. By default, all new remote data modules automatically register themselves by adding a call to EnableWebTransport in the UpdateRegistry method. You can remove this call to prevent Web connections to your application server.

Web connections can take advantage of object pooling. This allows your server to create a limited pool of remote data module instances that are available for client requests. By pooling the remote data modules, your server does not consume the resources for the data module and its database connection except when they are needed. For more information on object pooling, see "Pooling remote data modules".

Unlike other connection components, you can't use callbacks when the connection is formed via HTTP.

Using OLEnterprise

OLEnterprise lets you use the Business Object Broker instead of relying on client-side brokering. The Business Object Broker provides load-balancing, fail-over, and location transparency.

When using OLEnterprise, you must install OLEnterprise runtime on both client and server systems. OLEnterprise runtime handles the marshalling of Automation calls and communicates between the client and server system using remote procedure calls (RPCs). For more information, see the OLEnterprise documentation.

Using CORBA connections

CORBA lets you integrate your multi-tiered database applications into an environment that is standardized on CORBA. For example, the MIDAS client for Java components rely on a CORBA connection. Because CORBA (and Java) is available on multiple platforms, this allows you to write cross-platform MIDAS applications. For more information about using CORBA in Delphi, see "Writing CORBA applications".

By using CORBA, your application automatically gets the benefits of load-balancing, location transparency, and fail-over from the ORB runtime software. In addition, you can add hooks to take advantage of other CORBA services.

Building a multi-tiered application

The general steps for creating a multi-tiered database application are

  1. Create the application server.
  2. Register or install the application server.

    • If the application server uses DCOM, HTTP, sockets, or OLEnterprise as a communication protocol, it acts as an Automation server and must be registered like any other ActiveX or COM server. For information about registering an application, see "Registering an application as an Automation server".
    • If you are using MTS, the application server must be an Active Library rather than an .EXE. Because all COM calls must go through the MTS proxy, you do not register the application server. Instead, you install it with MTS. For information about installing libraries with MTS, see "Installing MTS objects into an MTS package".
    • When the application server uses CORBA, registration is optional. If you want to allow client applications to use dynamic binding to your interface, you must install the server's interface in the Interface Repository. In addition, if you want to allow client applications to launch the application server when it is not already running, it must be registered with the OAD (Object Activation Daemon). For more information about registering a CORBA server, see "Registering server interfaces".

  3. Create a client application.

The order of creation is important. You should create and run the application server before you create a client. At design time, you can then connect to the application server to test your client. You can, of course, create a client without specifying the application server at design time, and only supply the server name at runtime. However, doing so prevents you from seeing if your application works as expected when you code at design time, and you will not be able to choose servers and providers using the Object Inspector.

Note: If you are not creating the client application on the same system as the server, and you are not using a Web connection or socket connection, you may want to register or install the application server on the client system. This makes the connection component aware of the application server at design time so that you can choose server names and provider names from a drop-down list in the Object Inspector. (If you are using a Web connection or socket connection, the connection component fetches the names of registered servers from the server machine.)

Creating the application server

You create an application server very much as you create most database applications. The major difference is that the application server includes a dataset provider.

To create an application server, start a new project, save it, and follow these steps:

  1. Add a new remote data module to the project. From the main menu, choose File|New. Choose the Multitier page in the new items dialog, and select

    • Remote Data Module if you are creating a COM Automation server that clients access using DCOM, HTTP, sockets, or OLEnterprise.
    • MTS Data Module if you are creating an Active Library that clients access using MTS. Connections can be formed using DCOM, HTTP, sockets, or OLEnterprise.
    • CORBA Data Module if you are creating a CORBA server.

    For more detailed information about setting up a remote data module, see "Setting up the remote data module".

    Note: Remote data modules are more than simple data modules. The CORBA remote data module acts as a CORBA server. Other data modules are COM Automation objects.

    • Place the appropriate dataset components on the data module and set them up to access the database server.
    • Place a TDataSetProvider component on the data module for each dataset. This provider is required for brokering client requests and packaging data.
    • Set the DataSet property for each provider component to the name of the dataset to access. There are additional properties that you can set for the provider. For more detailed information about setting up a provider, see "Using provider components".
    • Write application server code to implement events, shared business rules, shared data validation, and shared security. You may want to extend the application server's interface to provide additional ways that the client application can call the server. For more information about extending the application server's interface, see "Extending the application server's interface".
    • Save, compile, and register or install the application server.

      • When the application server uses DCOM, HTTP, sockets, or OLEnterprise as a communication protocol, it acts as an Automation server and must be registered like any other ActiveX or COM server. For information about registering an application, see "Registering an application as an Automation server".
      • If you are using MTS, the application server must be an Active Library rather than an .EXE. Because all COM calls must go through the MTS proxy, you do not register the application server. Instead, you install it with MTS. For information about installing libraries with MTS, see "Installing MTS objects into an MTS package".
      • When the application server uses CORBA, registration is optional. If you want to allow client applications to use dynamic (late) binding to your interface, you must install the server's interface in the Interface Repository. In addition, if you want to allow client applications to launch the application server when it is not already running, it must be registered with the OAD (Object Activation Daemon). For more information about registering CORBA servers, see "Registering server interfaces".

    • If your server application does not use DCOM, you must install the runtime software that receives client messages, instantiates the remote data module, and marshals interface calls.

      • For TCP/IP sockets this is a socket dispatcher application, Scktsrvr.exe.
      • For HTTP connections this is httpsrvr.dll, an ISAPI/NSAPI DLL that must be installed with your Web server.
      • For OLEnterprise, this is the OLEnterprise runtime.
      • For CORBA, this is the VisiBroker ORB.

    Setting up the remote data module

    When you set up and run an application server, it does not establish any connection with client applications. Instead, connection is maintained by client applications. The client application uses its connection component to establish a connection to the application server, which it uses to communicate with its selected provider. All of this happens automatically, without your having to write code to manage incoming requests or supply interfaces.

    When you create the remote data module, you must provide certain information that indicates how it responds to client requests. This information varies, depending on the type of remote data module. See "The structure of the application server" for information on what type of remote data module you need.

    Configuring TRemoteDataModule

    To add a TRemoteDataModule component to your application, choose File|New and select Remote Data Module from the Multitier page of the new items dialog. You will see the Remote Data Module wizard.

    You must supply a class name for your remote data module. This is the base name of a descendant of TRemoteDataModule that your application creates. It is also the base name of the interface for that class. For example, if you specify the class name MyDataServer, the wizard creates a new unit declaring TMyDataServer, a descendant of TRemoteDataModule, which implements IMyDataServer, a descendant of IAppServer.

    Note: You can add your own properties and methods to the new interface. For more information, see "Extending the application server's interface".

    If you are creating a DLL (Active Library), you must specify the threading model in the Remote Data Module wizard. You can choose Single-threaded, Apartment-threaded, Free-threaded, or Both.

    • If you choose Single-threaded, COM ensures that only one client request is serviced at a time. You do not need to worry about client requests interfering with each other.
    • If you choose Apartment-threaded, COM ensures that any instance of your remote data module services one request at a time. When writing code in an Apartment-threaded library, you must guard against thread conflicts if you use global variables or objects not contained in the remote data module. This is the recommended model if you are using BDE-enabled datasets. (Note that you will need a session component with its AutoSessionName property set to True to handle threading issues on BDE-enabled datasets)
    • If you choose Free-threaded, your application can receive simultaneous client requests on several threads. You are responsible for ensuring your application is thread-safe. Because multiple clients can access your remote data module simultaneously, you must guard your instance data (properties, contained objects, and so on) as well as global variables. This is the recommended model if you are using ADO datasets.
    • If you choose Both, your library works the same as when you choose Free-threaded, with one exception: all callbacks (calls to client interfaces) are serialized for you.

    If you are creating an EXE, you must specify what type of instancing to use. You can choose Single instance or Multiple instance (Internal instancing applies only if the client code is part of the same process space.)

    • If you choose Single instance, each client connection launches its own instance of the executable. That process instantiates a single instance of the remote data module, which is dedicated to the client connection.
    • If you choose Multiple instance, a single instance of the application (process) instantiates all remote data modules created for clients. Each remote data module is dedicated to a single client connection, but they all share the same process space.

    Configuring TMTSDataModule

    To add a TMTSDataModule component to your application, choose File|New and select MTS Data Module from the Multitier page of the new items dialog. You will see the MTS Data Module wizard.

    You must supply a class name for your remote data module. This is the base name of a descendant of TMTSDataModule that your application creates. It is also the base name of the interface for that class. For example, if you specify the class name MyDataServer, the wizard creates a new unit declaring TMyDataServer, a descendant of TMTSDataModule, which implements IMyDataServer, a descendant of IAppServer.

    Note: You can add your own properties and methods to your new interface. For more information, see "Extending the application server's interface".

    MTS applications are always DLLs (Active Libraries). You must specify the threading model in the MTS Data Module wizard. Choose Single, Apartment, Free, or Both.

    • If you choose Single, MTS ensures that only one client request is serviced at a time. You do not need to worry about client requests interfering with each other.
    • If you choose Apartment or Free, you get the same thing: MTS ensures that any instance of your remote data module services one request at a time, but calls do not always use the same thread. You can't use thread variables, because there is no guarantee that subsequent calls to the remote data module instance will use the same thread.You must guard against thread conflicts if you use global variables or objects not contained in the remote data module. Instead of using global variables, you can use the shared property manager. For more information on the shared property manager, see "Shared property manager".
    • If you choose Both, MTS calls into the remote data module's interface in the same way as when you choose Apartment or Free. However, any callbacks you make to client applications are serialized, so that you don't need to worry about them interfering with each other.

    Note: The Apartment and Free models under MTS are different than the corresponding models under DCOM.

    You must also specify the MTS transaction attributes of your remote data module. You can choose from the following options:

    • Requires a transaction. When you select this option, every time a client uses your remote data module's interface, that call is executed in the context of an MTS transaction. If the caller supplies a transaction, a new transaction need not be created.
    • Requires a new transaction. When you select this option, every time a client uses your remote data module's interface, a new transaction is automatically created for that call.
    • Supports transactions. When you select this option, your remote data module can be used in the context of an MTS transaction, but the caller must supply the transaction when it invokes the interface.
    • Does not support transactions. When you select this option, your remote data module can't be used in the context of MTS transactions.

    Configuring TCorbaDataModule

    To add a TCorbaDataModule component to your application, choose File|New and select CORBA Data Module from the Multitier page of the new items dialog. You will see the CORBA Data Module wizard.

    You must supply a class name for your remote data module. This is the base name of a descendant of TCorbaDataModule that your application creates. It is also the base name of the interface for that class. For example, if you specify the class name MyDataServer, the wizard creates a new unit declaring TMyDataServer, a descendant of TCorbaDataModule, which implements IMyDataServer, a descendant of IAppServer.

    Note: You can add your own properties and methods to your new interface. For more information on adding to your data module's interface, see "Extending the application server's interface".

    The CORBA Data Module wizard lets you specify how you want your server application to create instances of the remote data module. You can choose either shared or instance-per-client.

    • When you choose shared, your application creates a single instance of the remote data module that handles all client requests. This is the model used in traditional CORBA development.
    • When you choose instance-per-client, a new remote data module instance is created for each client connection. This instance persists until its timeout period elapses with no messages from the client. This allows the server to free instances when they are no longer used by clients, but holds the risk that the server may be freed prematurely if the client does not use the server's interface for a long time.

    Note: Unlike instancing for COM servers, where the model determines the number of instances of the process that run, with CORBA, instancing determines the number of instances created of your object. They are all created within a single instance of the server executable.

    In addition to the instancing model, you must specify the threading model in the CORBA Data Module wizard. You can choose Single- or Multi-threaded.

    • If you choose Single-threaded, each remote data module instance is guaranteed to receive only one client request at a time. You can safely access the objects contained in your remote data module. However, you must guard against thread conflicts when you use global variables or objects not contained in the remote data module.
    • If you choose Multi-threaded, each client connection has its own dedicated thread. However, your application may be called by multiple clients simultaneously, each on a separate thread. You must guard against simultaneous access of instance data as well as global memory. Writing Multi-threaded servers is tricky when you are using a shared remote data module instance, because you must protect all use of objects contained in your remote data module.

    Creating a data provider for the application server

    Each remote data module on an application server contains one or more provider components. Each client dataset uses a specific provider, which acts as the bridge between the client dataset and the data it represents. A provider component (TDataSetProvider) takes care of packaging data into data packets that it sends to clients and applying updates received from the client.

    Most of the data logic in the application server is handled by the provider components contained in the remote data module. Event handlers that respond to client requests implement your business and data logic, while properties on the provider component control what information is included in data packets. See "Using provider components" for details on how to use a provider component to control the interaction with client applications.

    Extending the application server's interface

    Client applications interact with the application server by creating or connecting to an instance of the remote data module. They use its interface as the basis of all communication with the application server.

    You can add to your remote data module's interface to provide additional support for your client applications. This interface is a descendant of IAppServer and is created for you automatically by the wizard when you create the remote data module.

    To add to the remote data module's interface, you can

    • Choose the Add to Interface command from the Edit menu in the IDE. Indicate whether you are adding a procedure, function, or property, and enter its syntax. When you click OK, you will be positioned in the code editor on the implementation of your new interface member.
    • Use the type library editor. Select the interface for your application server in the type library editor, and click the tool button for the type of interface member (method or property) that you are adding. Give your interface member a name in the Attributes page, specify parameters and type in the Parameters page, and then refresh the type library. For more information about using the type library editor, see "Working with type libraries". Note that many of the features you can specify in the type library editor (such as help context, version, and so on) do not apply to CORBA interfaces. Any values you specify for these in the type library editor are ignored.

    What Delphi does when you add new entries to the interface depends on whether you are creating a COM-based (TRemoteDataModule or TMTSDataModule) or CORBA (TCorbaDataModule) server.

    • When you add to a COM interface, your changes are added to your unit source code and the type library file (.TLB).
    • When you add to a CORBA interface, your changes are reflected in your unit source code and the automatically generated _TLB unit. The _TLB unit is added to the uses clause of your unit. You must add this unit to the uses clause in your client application if you want to take advantage of early binding. In addition, you can save an .IDL file from the type library editor using the Export to IDL button. The .IDL file is needed for registering the interface with the Interface Repository and Object Activation Daemon.

    Note: You must explicitly save the TLB file by choosing Refresh in the type library editor and then saving the changes from the IDE.

    Once you have added to your remote data module's interface, locate the properties and methods that were added to your remote data module's implementation. Add code to finish this implementation.

    Client applications call your interface extensions using the AppServer property of their connection component. For more information on how to do this, see "Calling server interfaces".

    Adding callbacks to the application server's interface

    You can allow the application server to call your client application by introducing a callback. To do this, the client application passes an interface to one of the application server's methods, and the application server later calls this method as needed. However, if your extensions to the remote data module's interface include callbacks, you can't use an HTTP-based connection. TWebConnection does not support callbacks. If you are using a socket-based connection, client applications must indicate whether they are using callbacks by setting the SupportCallbacks property. All other types of connection automatically support callbacks.

    Extending the application server's interface when using MTS

    When using transactions or just-in-time activation under MTS, you must be sure all new methods call SetComplete to tell MTS when it is finished. This allows transactions to complete and so permits the remote data module to be deactivated. Furthermore, you can't return any values from your new methods that allow the client to communicate directly with objects or interfaces on the application server. This is because any communication that does not go through the remote data module's interface bypasses the MTS proxy, which can invalidate transactions. If you are using a stateless MTS data module, bypassing the MTS proxy can lead to crashes because you can't guarantee that the remote data module is active.

    Creating the client application

    In most regards, creating a multi-tiered client application is similar to creating a traditional two-tiered client. The major differences are that a multi-tiered client uses

    • A connection component to establish a conduit to the application server.
    • One or more TClientDataSet components to link to a data provider on the application server. Data-aware controls on the client are connected through data source components to these client datasets instead of TTable, TQuery, TStoredProc or TADODataSet components.

    To create a multi-tiered client application, start a new project and follow these steps:

    1. Add a new data module to the project.
    2. Place a connection component on the data module. The type of connection component you add depends on the communication protocol you want to use. See "The structure of the client application" for details.
    3. Set properties on your connection component to specify the application server with which it should establish a connection. To learn more about setting up the connection component, see "Connecting to the application server".
    4. Set the other connection component properties as needed for your application. For example, you might set the ObjectBroker property to allow the connection component to choose dynamically from several servers. For more information about using the connection components, see "Managing server connections"
    5. Place as many TClientDataSet components as needed on the data module, and set the RemoteServer property for each component to the name of the connection component you placed in Step 2. For a full introduction to client datasets, see "Creating and using a client dataset."
    6. Set the ProviderName property for each TClientDataSet component. If your connection component is connected to the application server at design time, you can choose available application server providers from the ProviderName property's drop-down list.
    7. Create the client application in much the same way you would create any other database application. You will probably want to use some of the special features of client datasets that support their interaction with the provider components on the application server. These are described in "Using a client dataset with a data provider".

    Connecting to the application server

    To establish and maintain a connection to an application server, a client application uses one or more connection components. You can find these components on the MIDAS page of the Component palette.

    Use a connection component to

    • Identify the protocol for communicating with the application server. Each type of connection component represents a different communication protocol. See "Choosing a connection protocol" for details on the benefits and limitations of the available protocols.
    • Indicate how to locate the server machine. The details of identifying the server machine vary depending on the protocol.
    • Identify the application server on the server machine.

      If you are not using CORBA, identify the server using the ServerName or ServerGUID property. ServerName identifies the base name of the class you specify when creating the remote data module on the application server. See "Setting up the remote data module" for details on how this value is specified on the server. If the server is registered or installed on the client machine, or if the connection component is connected to the server machine, you can set the ServerName property at design time by choosing from a drop-down list in the Object Inspector. ServerGUID specifies the GUID of the remote data module's interface. You can look up this value using the type library editor.

      If you are using CORBA, identify the server using the RepositoryID property. RepositoryID specifies the Repository ID of the application server's factory interface, which appears as the third argument in the call to TCorbaVCLComponentFactory.Create that is automatically added to the initialization section of the CORBA server's implementation unit. You can also set this property to the base name of the CORBA data module's interface (the same string as the ServerName property for other connection components), and it is automatically converted into the appropriate Repository ID for you.

    • Manage server connections. Connection components can be used to create or drop connections and to call application server interfaces.

    Usually the application server is on a different machine from the client application, but even if the server resides on the same machine as the client application (for example, during the building and testing of the entire multi-tier application), you can still use the connection component to identify the application server by name, specify a server machine, and use the application server interface.

    Specifying a connection using DCOM

    When using DCOM to communicate with the application server, client applications include a TDCOMConnection component for connecting to the application server. TDCOMConnection uses the ComputerName property to identify the machine on which the server resides.

    When ComputerName is blank, the DCOM connection component assumes that the application server resides on the client machine or that the application server has a system registry entry. If you do not provide a system registry entry for the application server on the client when using DCOM, and the server resides on a different machine from the client, you must supply ComputerName.

    Note: Even when there is a system registry entry for the application server, you can specify ComputerName to override this entry. This can be especially useful during development, testing, and debugging.

    If you have multiple servers that your client application can choose from, you can use the ObjectBroker property instead of specifying a value for ComputerName. For more information, see "Brokering connections".

    If you supply the name of a host computer or server that cannot be found, the DCOM connection component raises an exception when you try to open the connection.

    Specifying a connection using sockets

    You can establish a connection to the application server using sockets from any machine that has a TCP/IP address. This method has the advantage of being applicable to more machines, but does not provide for using any security protocols. When using sockets, include a TSocketConnection component for connecting to the application server.

    TSocketConnection identifies the server machine using the IP Address or host name of the server system, and the port number of the socket dispatcher program (Scktsrvr.exe) that is running on the server machine. For more information about IP addresses and port values, see "Describing sockets".

    Three properties of TSocketConnection specify this information:

    • Address specifies the IP Address of the server.
    • Host specifies the host name of the server.
    • Port specifies the port number of the socket dispatcher program on the application server.

    Address and Host are mutually exclusive. Setting one unsets the value of the other. For information on which one to use, see "Describing the host".

    If you have multiple servers that your client application can choose from, you can use the ObjectBroker property instead of specifying a value for Address or Host. For more information, see "Brokering connections".

    By default, the value of Port is 211, which is the default port number of the socket dispatcher programs supplied with Delphi. If the socket dispatcher has been configured to use a different port, set the Port property to match that value.

    Note: You can configure the port of the socket dispatcher while it is running by right-clicking the Borland Socket Server tray icon and choosing Properties.

    Although socket connections do not provide for using security protocols, you can customize the socket connection to add your own encryption. To do this, create and register a COM object that supports the IDataIntercept interface. This is an interface for encrypting and decrypting data. Next, set the InterceptGUID property of the socket connection component to the GUID for this COM object. Finally, right click the Borland Socket Server tray icon, choose Properties, and on the properties tab set the Intercept GUID to the same GUID. This mechanism can also be used for data compression and decompression.

    Specifying a connection using HTTP

    You can establish a connection to the application server using HTTP from any machine that has a TCP/IP address. Unlike sockets, however, HTTP allows you to take advantage of SSL security and to communicate with a server that is protected behind a firewall. When using HTTP, include a TWebConnection component for connecting to the application server.

    The Web connection component establishes a connection to the Web server application (httpsrvr.dll), which in turn communicates with the application server. TWebConnection locates httpsrvr.dll using a Uniform Resource Locator (URL). The URL specifies the protocol (http or, if you are using SSL security, https), the host name for the machine that runs the Web server and httpsrvr.dll, and the path to the Web server application (Httpsrvr.dll). Specify this value using the URL property.

    Note: When using TWebConnection, wininet.dll must be installed on the client machine. If you have IE3 or higher installed, wininet.dll can be found in the Windows system directory.

    If the Web server requires authentication, or if you are using a proxy server that requires authentication, you must set the values of the UserName and Password properties so that the connection component can log on.

    If you have multiple servers that your client application can choose from, you can use the ObjectBroker property instead of specifying a value for URL. For more information, see "Brokering connections".

    Specifying a connection using OLEnterprise

    When using OLEnterprise to communicate with the application server, client applications should include a TOLEnterpriseConnection component for connecting to the application server. When using OLEnterprise, you can either connect directly to the server machine, or you can use the Business Object Broker.

    • To use OLEnterprise without going through a Business Object Broker, set the ComputerName property to the name of the server machine, just as you would use the ComputerName property for a DCOM connection.
    • To use the load-balancing and fail-over services of the Business Object Broker, set the BrokerName property to the name of the Business Object Broker.

    ComputerName and BrokerName are mutually exclusive. Setting the value of one unsets the value of the other.

    For more information about using OLEnterprise, see the OLEnterprise documentation.

    Specifying a connection using CORBA

    Only the RepositoryID property is necessary in order to specify a CORBA connection. This is because a Smart Agent on the local network automatically locates an available server for your CORBA client.

    However, you can limit the possible servers to which your client application connects by the other properties of the CORBA connection component. If you want to specify a particular server machine, rather than letting the CORBA Smart Agent locate any available server, use the HostName property. If there is more than one object instance that implements your server interface, you can specify which object you want to use by setting the ObjectName property.

    The TCorbaConnection component obtains an interface to the CORBA data module on the application server in one of two ways:

    • If you are using early (static) binding, you must add the _TLB.pas file (generated by the type library editor) to your client application. Early binding is highly recommended, both for compile-time type checking and because it is much faster than late (dynamic) binding.
    • If you are using late (dynamic) binding, the interface must be registered with the Interface Repository. For more information about registering an interface with the Interface Repository, see "Writing CORBA clients".

    For more information on early vs. late binding, see "Calling server interfaces".

    Brokering connections

    If you have multiple servers that your client application can choose from, you can use an Object Broker to locate an available server system. The object broker maintains a list of servers from which the connection component can choose. When the connection component needs to connect to an application server, it asks the Object Broker for a computer name (or IP address, host name, or URL). The broker supplies a name, and the connection component forms a connection. If the supplied name does not work (for example, if the server is down), the broker supplies another name, and so on, until a connection is formed.

    Once the connection component has formed a connection with a name supplied by the broker, it saves that name as the value of the appropriate property (ComputerName, Address, Host, RemoteHost, or URL). If the connection component closes the connection later, and then needs to reopen the connection, it tries using this property value, and only requests a new name from the broker if the connection fails.

    Use an Object Broker by specifying the ObjectBroker property of your connection component. When the ObjectBroker property is set, the connection component does not save the value of ComputerName, Address, Host, RemoteHost, or URL to disk.

    Note: Do not use the ObjectBroker property with OLEnterprise connections or CORBA connections. Both of these protocols have their own brokering services.

    Managing server connections

    The main purpose of connection components is to locate and connect to the application server. Because they manage server connections, you can also use connection components to call the methods of the application server's interface.

    Connecting to the server

    To locate and connect to the application server, you must first set the properties of the connection component to identify the application server. This process is described in "Connecting to the application server". In addition, before opening the connection, any client datasets that use the connection component to communicate with the application server should indicate this by setting their RemoteServer property to specify the connection component.

    The connection is opened automatically when client datasets try to access the application server. For example, setting the Active property of the client dataset to True opens the connection, as long as the RemoteServer property has been set.

    If you do not link any client datasets to the connection component, you can open the connection by setting the Connected property of the connection component to True.

    Before a connection component establishes a connection to an application server, it generates a BeforeConnect event. You can perform any special actions prior to connecting in a BeforeConnect handler that you code. After establishing a connection, the connection component generates an AfterConnect event for any special actions.

    Dropping or changing a server connection

    A connection component drops a connection to the application server when you

    • set the Connected property to False.
    • free the connection component. A connection object is automatically freed when a user closes the client application.
    • change any of the properties that identify the application server (ServerName, ServerGUID, ComputerName, and so on). Changing these properties allows you to switch among available application servers at runtime. The connection component drops the current connection and establishes a new one.

    Note: Instead of using a single connection component to switch among available application servers, a client application can instead have more than one connection component, each of which is connected to a different application server.

    Before a connection component drops a connection, it automatically calls its BeforeDisconnect event handler, if one is provided. To perform any special actions prior to disconnecting, write a BeforeDisconnect handler. Similarly, after dropping the connection, the AfterDisconnect event handler is called. If you want to perform any special actions after disconnecting, write an AfterDisconnect handler.

    Calling server interfaces

    Applications do not need to call the IAppServer interface directly because the appropriate calls are made automatically when you use the properties and methods of the client dataset. However, while it is not necessary to work directly with the IAppServer interface, you may have added your own extensions to the remote data module's interface. When you extend the application server's interface, you need a way to call those extensions using the connection created by your connection component. You can do this using the AppServer property of the connection component. For more information about extending the application server's interface, see "Extending the application server's interface".

    AppServer is a Variant that represents the application server's interface. You can call an interface method using AppServer by writing a statement such as

    MyConnection.AppServer.SpecialMethod(x,y);

    However, this technique provides late (dynamic) binding of the interface call. That is, the SpecialMethod procedure call is not bound until runtime when the call is executed. Late binding is very flexible, but by using it you lose many benefits such as code insight and type checking. In addition, late binding is slower than early binding, because the compiler generates additional calls to the server to set up interface calls before they are invoked.

    When you are using DCOM or CORBA as a communications protocol, you can use early binding of AppServer calls. Use the as operator to cast the AppServer variable to the IAppServer descendant you created when you created the remote data module. For example:

    with MyConnection.AppServer as IMyAppServer do  SpecialMethod(x,y);

    To use early binding under DCOM, the server's type library must be registered on the client machine. You can use TRegsvr.exe, which ships with Delphi to register the type library.

    Note: See the TRegSvr demo (which provides the source for TRegsvr.exe) for an example of how to register the type library programmatically.

    To use early binding with CORBA, you must add the _TLB unit that is generated by the type library editor to your project. To do this, add this unit to the uses clause of your unit.

    When you are using TCP/IP or OLEnterprise, you can't use true early binding, but because the remote data module uses a dual interface, you can use the application server's dispinterface to improve performance over simple late-binding. The dispinterface has the same name as the remote data module's interface, with the string 'Disp' appended. You can assign the AppServer property to a variable of this type to obtain the dispinterface. Thus:

    var  TempInterface: IMyAppServerDisp;begin  TempInterface := MyConnection.AppServer;...  TempInterface.SpecialMethod(x,y);...end;

    Note: To use the dispinterface, you must add the _TLB unit that is generated when you save the type library to the uses clause of your client module.

    Managing transactions in multi-tiered applications

    When client applications apply updates to the application server, the provider component automatically wraps the process of applying updates and resolving errors in a transaction. This transaction is committed if the number of problem records does not exceed the MaxErrors value specified as an argument to the ApplyUpdates method. Otherwise, it is rolled back.

    In addition, you can add transaction support to your server application by adding a database component or using passthrough SQL. This works the same way that you would manage transactions in a two-tiered application. For more information about this sort of transaction control, see "Using transactions" and "Working with (connection) transactions."

    If you are using MTS, you can broaden your transaction support by using MTS transactions. MTS transactions can include any of the business logic on your application server, not just the database access. In addition, because they support two-phase commits, MTS transactions can span multiple databases.

    Warning: Two-phase commit is fully implemented only on Oracle7 and MS-SQL databases. If your transaction involves multiple databases, and some of them are remote servers other than Oracle7 or MS-SQL, your transaction runs a small risk of only partially succeeding. Within any one database, however, you will always have transaction support.

    To use MTS transactions, extend the application server's interface to include method calls that encapsulate the transaction, if necessary. When configuring the MTS remote data module indicate that it must participate in transactions. When a client calls a method on your application server's interface, it is automatically wrapped in a transaction. All client calls to your application server are then enlisted in that transaction until you indicate that the transaction is complete. These calls either succeed as a whole or are rolled back.

    Note: Do not combine MTS transactions with explicit transactions created by a database component or using passthrough SQL. When your remote data module is enlisted in an MTS transaction, it automatically enlists all of your database calls in the transaction as well.

    For more information about using MTS transactions, see "MTS transaction support".

    Supporting master/detail relationships

    You can create master/detail relationships between client datasets in your client application in the same way you set up master/detail forms in one- and two-tiered applications. For more information about setting up master/detail forms, see "Creating master/detail forms".

    However, this approach has two major drawbacks:

    • The detail table must fetch and store all of its records from the application server even though it only uses one detail set at a time. This problem can be mitigated by using parameters. For more information, see "Limiting records with parameters".
    • It is very difficult to apply updates, because client datasets apply updates at the dataset level and master/detail updates span multiple datasets. Even in a two-tiered environment, where you can use the database to apply updates for multiple tables in a single transaction, applying updates in master/detail forms is tricky. See "Applying updates for master/detail tables" for more information on applying updates in traditional master/detail forms.

    In multi-tiered applications, you can avoid these problems by using nested tables to represent the master/detail relationship. To do this, set up a master/detail relationship between the tables on the application server. Then set the DataSet property of your provider component to the master table.

    When clients call the GetRecords method of the provider, it automatically includes the detail datasets as a DataSet field in the records of the data packet. When clients call the ApplyUpdates method of the provider, it automatically handles applying updates in the proper order.

    See "Representing master/detail relationships" for more information on using nested datasets to support master/detail relationships in client datasets.

    Supporting state information in remote data modules

    The IAppServer interface, which controls all communication between client datasets and providers on the application server, is mostly stateless. When an application is stateless, it does not "remember" anything that happened in previous calls by the client. This stateless quality is useful if you are pooling database connections under MTS, because your application server does not need to distinguish between database connections for persistent information such as record currency. Similarly, this stateless quality is important when you are sharing remote data module instances between many clients, as occurs with MTS just-in-time activation, object pooling, or typical CORBA servers.

    However, there are times when you want to maintain state information between calls to the application server. For example, when requesting data using incremental fetching, the provider on the application server must "remember" information from previous calls (the current record).

    This is not a problem if the remote data module is configured so that each client has its own instance. When each client has its own instance of the remote data module, there are no other clients to change the state of the data module between client calls.

    However, it is reasonable to want the benefits of sharing remote data module instances while still managing persistent state information. For example, you may need to use incremental fetching to display a dataset that is too large to fit in memory at one time.

    Before and after any calls to the IAppServer interface that the client dataset sends to the application server (AS_ApplyUpdates, AS_Execute, AS_GetParams, AS_GetRecords, or AS_RowRequest), it receives an event where it can send or retrieve custom state information. Similarly, before and after providers respond to these client-generated calls, they receive events where they can retrieve or send custom state information. Using this mechanism, you can communicate persistent state information between client applications and the application server, even if the application server is stateless. For example, to enable incremental fetching in a stateless application server, you can do the following:

    • Use the client dataset's BeforeGetRecords event to send the key value of the last record to the application server:
      TDataModule1.ClientDataSet1BeforeGetRecords(Sender: TObject; var OwnerData: OleVariant);var  CurRecord: TBookMark;begin  with Sender as TClientDataSet do  begin    CurRecord := GetBookmark; { save the current record }    try      Last; {locate the last record in the new packet }      OwnerData := FieldValues['Key']; { Send key value to the application server }      GotoBookmark(CurRecord); { return to current record }    finally      FreeBookmark(CurRecord);    end;  end;end;
    • On the server, use the provider's BeforeGetRecords event to locate the appropiate set of records:
      TRemoteDataModule1.Provider1BeforeGetRecords(Sender: TObject; var OwnerData: OleVariant);begin  with Sender as TProvider do    DataSet.Locate('Key', OwnerData, []);end;

    Note: The previous example uses a key value to mark the end of the record set rather than a bookmark. This is because bookmarks may not be valid between IAppServer calls if the server application pools database handles.

    Writing MIDAS Web applications

    If you want to create Web-based clients for your multi-tiered database application, you must replace the client tier with a special Web applications that acts simultaneously as a client to the application server and as a Web server application that is installed with a Web server on the same machine. This architecture is illustrated in Figure 14.1.

    Figure 14.1   Web-based multi-tiered database application

    There are two approaches that you can take to build the MIDAS Web application:

    • You can combine the MIDAS architecture with ActiveX support to distribute a client application as an ActiveX control. This allows any browser that supports ActiveX to run your client application as an in-process server.
    • You can use XML data packets to build an InternetExpress application. This allows browsers that supports javascript to interact with your client application through html pages.

    These two approaches are very different. Which one you choose depends on the following considerations:

    • Each approach relies on a different technology (ActiveX vs. javascript and XML). Consider what systems your end-users will use. The first approach requires a browser to support ActiveX (which limits clients to a Windows platform). The second approach requires a browser to support javascript and the DHTML capabilities introduced by Netscape 4 and Internet Explorer 4.
    • ActiveX controls must be downloaded to the browser to act as an in-process server. As a result, the clients using an ActiveX approach require much more memory than the clients of an html-based application.
    • The InternetExpress approach can be integrated with other HTML pages. An ActiveX client must run in a separate window.
    • The InternetExpress approach uses standard HTTP, thereby avoiding any firewall issues that confront an ActiveX application.
    • The ActiveX approach provides greater flexibility in how you program your application. You are not limited by the capabilities of the javascript libraries. The client datasets used in the ActiveX approach surface more features (such as filters, ranges, aggregation, optional parameters, delayed fetching of BLOBs or nested details, and so on) than the XML brokers used in the InternetExpress approach.

    Caution: Your Web client application may look and act differently when viewed from different browsers. Test your application with the browsers you expect your end-users to use.

    Distributing a client application as an ActiveX control

    The MIDAS architecture can be combined with Delphi's ActiveX features to distribute a client application as an ActiveX control.

    When you distribute your client application as an ActiveX control, create the application server as you would for any other multi-tiered application. The only limitation is that you will want to use DCOM, HTTP, or sockets as a communications protocol, because you can't count on client machines having installed the OLEnterprise or CORBA runtime software. For details on creating the application server, see "Creating the application server".

    When creating the client application, you must use an Active Form as the basis instead of an ordinary form. See "Creating an Active Form for the client application", below, for details.

    Once you have built and deployed your client application, it can be accessed from any ActiveX-enabled Web browser on another machine. For a Web browser to successfully launch your client application, the Web server must be running on the machine that has the client application.

    If the client application uses DCOM to communicate between the client application and the application server, the machine with the Web browser must be enabled to work with DCOM. If the machine with the Web browser is a Windows 95 machine, it must have installed DCOM95, which is available from Microsoft.

    Creating an Active Form for the client application

    1. Because the client application will be deployed as an ActiveX control, you must have a Web server that runs on the same system as the client application. You can use a ready-made server such as Microsoft's Personal Web server or you can write your own using the socket components described in "Working with sockets."
    2. Create the client application following the steps described in "Creating the client application", except start by choosing File|New|Active Form, rather than beginning the client project as an ordinary Delphi project.
    3. If your client application uses a data module, add a call to explicitly create the data module in the active form initialization.
    4. When your client application is finished, compile the project, and select Project | Web Deployment Options. In the Web Deployment Options dialog, you must do the following:

      1. On the Project page, specify the Target directory, the URL for the target directory, and the HTML directory. Typically, the Target directory and the HTML directory will be the same as the projects directory for your Web Server. The target URL is typically the name of the server machine that is specified in the Windows Network|DNS settings.
      2. On the Additional Files page, include midas.dll with your client application.

    5. Finally, select Project|WebDeploy to deploy the client application as an active form.

    Any Web browser that can run Active forms can run your client application by specifying the .HTM file that was created when you deployed the client application. This .HTM file has the same name as your client application project, and appears in the directory specified as the Target directory.

    Building Web applications using InternetExpress

    MIDAS clients can request that the application server provide data packets that are coded in XML instead of OleVariants. By combining XML-coded data packets, special javascript libraries of database functions, and Delphi's Web server application support, you can create thin client applications that can be accessed using a Web browser that supports javascript. These applications make up Delphi's InternetExpress support.

    Before building an InternetExpress application, you should understand Delphi's Web server application architecture. This is described in "Creating Internet server applications".

    On the InternetExpress page of the component palette, you can find a set of components that extend this Web server application architecture to act as a MIDAS client. Using these components, the Web application generates HTML pages that contain a mixture of HTML, XML, and javascript. The HTML governs the layout and appearance of the pages seen by end users in their browsers. The XML encodes the data packets and delta packets that represent database information. The javascript allows the HTML controls to interpret and manipulate the data in these XML data packets.

    If the InternetExpress application uses DCOM to connect to the application server, you must take additional steps to ensure that the application server grants access and launch permissions to its clients. See "Granting permission to access and launch the application server" for details.

    Tip: You can use the components on the InternetExpress page to build Web server applications with "live" data even if you do not have an application server. Simply add the provider and its dataset to the Web module.

    Building an InternetExpress application

    The following steps describe how to build a Web application that creates HTML pages for allowing users to interact with the data from an application server via a javascript-enabled Web browser.

    1. Choose File|New to display the New Items dialog box, and on the New page select Web Server application. This process is described in "Creating Web server applications".
    2. From the MIDAS page of the component palette, add a connection component to the Web Module that appears when you create a new Web server application. The type of connection component you add depends on the communication protocol you want to use. See "Choosing a connection protocol" for details.
    3. Set properties on your connection component to specify the application server with which it should establish a connection. To learn more about setting up the connection component, see "Connecting to the application server".
    4. Instead of a client dataset, add an XML broker from the InternetExpress page of the component palette to the Web module. Like TClientDataSet, TXMLBroker represents the data from a provider on the application server and interacts with the application server through its IAppServer interface. However, unlike client datasets, XML brokers request data packets as XML instead of as OleVariants and interact with InternetExpress components instead of data controls.
    5. Set the RemoteServer property of the XML broker to point to the connection component you added in step 2. Set the ProviderName property to indicate the provider on the application server that provides data and applies updates. For more information about setting up the XML broker, see "Using an XML broker".
    6. Add a MIDAS page producer to the Web module for each separate page that users will see in their browsers. For each MIDAS page producer, you must set the IncludePathURL property to indicate where it can find the javascript libraries that augment its generated HTML controls with data management capabilities.
    7. Right-click a Web page and choose Action Editor to display the Action editor. Add action items for every message you want to handle from browsers. Associate the page producers you added in step 6 with these actions by setting their Producer property or writing code in an OnAction event handler. For more information on adding action items using the Action editor, see "Adding actions to the dispatcher".
    8. Double-click each Web page to display the Web Page editor. (You can also display this editor by clicking the ellipses button in the Object Inspector next to the WebPageItems property.) In this editor you can add Web Items to design the pages that users see in their browsers. For more information about designing Web pages for your InternetExpress application, see "Creating Web pages with a MIDAS page producer".
    9. Build your Web application. Once you install this application with your Web server, browsers can call it by specifying the name of the application as the scriptname portion of the URL and the name of the Web Page component as the pathinfo portion.

    Using the javascript libraries

    The HTML pages generated by the InternetExpress components and the Web items they contain make use of several javascript libraries that ship with Delphi:

    Table 14.4   The javascript libraries

    Library

    functions

    xmldom.js

    This library is a DOM-compatible XML parser written in javascript. It allows parsers that do not support XML to use XML data packets.

    xmldb.js

    This library defines data access classes analogous to TClientDataSet and TField.

    xmlbind.js

    This library defines classes that associate the data access classes in xmldb with HTML controls in the HTML page.

    These libraries can be found in the Source/Webmidas directory. Once you have installed these libraries, you must set the IncludePathURL property of all MIDAS page producers to indicate where they can be found.

    It is possible to write your own HTML pages using the javascript classes provided in these libraries instead of using Web items to generate your Web pages. However, you must ensure that your code does not do anything illegal, as these classes include minimal error checking (so as to minimize the size of the generated Web pages).

    The classes in the javascript libraries are an evolving standard, and are updated regularly. If you want to use them directly rather than relying on Web items to generate the javascript code, you can get the latest versions and documentation of how to use them from CodeCentral at www.borland.com/CodeCentral.

    Granting permission to access and launch the application server

    Requests from the InternetExpress application appear to the application server as originating from a guest account with the name IUSR_computername, where computername is the name of the system running the Web application. By default, this account does not have access or launch permission for the application server. If you try to use the Web application without granting these permissions, when the Web browser tries to load the requested page it times out with EOLE_ACCESS_ERROR.

    Note: Because the application server runs under this guest account, it can't be shut down by other accounts.

    To grant the Web application access and launch permissions, run DCOMCnfg.exe, which is located in the System32 directory of the machine that runs the application server. The following steps describe how to configure your application server:

    1. When you run DCOMCnfg, select your application server in the list of applications on the Applications page.
    2. Click the Properties button. When the dialog changes, select the Security page.
    3. Select Use Custom Access Permissions, and press the Edit button. Add the name IUSR_computername to the list of accounts with access permission, where computername is the name of the machine that runs the Web application.
    4. Select Use Custom Launch Permissions, and press the Edit button. Add IUSR_computername to this list as well.
    5. Click the Apply button.

    Using an XML broker

    The XML broker serves two major functions:

    • It fetches XML data packets from the application server and makes them available to the Web Items that generate HTML for the InternetExpress application.
    • It receives updates in the form of XML delta packets from browsers and applies them to the application server.

    Fetching XML data packets

    Before the XML broker can supply XML data packets to the components that generate HTML pages, it must fetch them from the application server. To do this, it uses the IAppServer interface of the application server, which it acquires through a connection component. You must set the following properties so that the XML producer can use the application server's IAppServer interface:

    • Set the RemoteServer property to the connection component that establishes the connection to the application server and gets its IAppServer interface. At design time, you can select this value from a drop-down list in the object inspector.
    • Set the ProviderName property to the name of the provider component on the application server that represents the dataset for which you want XML data packets. This provider both supplies XML data packets and applies updates from XML delta packets. At design time, if the RemoteServer property is set and the connection component has an active connection, the Object Inspector displays a list of available providers. (If you are using a DCOM connection the application server must also be registered on the client machine).

    Two properties let you indicate what you want to include in data packets:

    • If the provider on the application server represents a query or stored procedure, you may want to provide parameter values before obtaining an XML data packet. You can supply these parameter values using the Params property.
    • You can limit the number of records that are added to the data packet by setting the MaxRecords property.

    The components that generate HTML and javascript for the InternetExpress application automatically use the XML broker's XML data packet once you set their XMLBroker property. To obtain the XML data packet directly in code, use the RequestRecords method.

    Note: When the XML broker supplies a data packet to another component (or when you call RequestRecords), it receives an OnRequestRecords event. You can use this event to supply your own XML string instead of the data packet from the application server. For example, you could fetch the XML data packet from the application server using GetXMLRecords and then edit it before supplying it to the emerging Web page.

    Applying updates from XML delta packets

    When you add the XML broker to the Web module (or data module containing a TWebDispatcher), it automatically registers itself with the Web dispatcher as an auto-dispatching object. This means that, unlike other components, you do not need to create an action item for the XML broker in order for it to respond to update messages from a Web browser. These messages contain XML delta packets that should be applied to the application server. Typically, they originate from a button that you create on one of the HTML pages produced by the Web client application.

    So that the dispatcher can recognize messages for the XML broker, you must describe them using the WebDispatch property. Set the PathInfo property to the path portion of the URL to which messages for the XML broker are sent. Set MethodType to the value of the method header of update messages addressed to that URL (typically mtPost). If you want to respond to all messages with the specified path, set MethodType to mtAny. If you don't want the XML broker to respond directly to update messages (for example, if you want to handle them explicitly using an action item), set the Enabled property to False. For more information on how the Web dispatcher determines which component handles messages from the Web browser, see "Dispatching request messages".

    When the dispatcher passes an update message on to the XML broker, it passes the updates on to the application server and, if there are update errors, receives an XML delta packet describing all update errors. Finally, it sends a response message back to the browser, which either redirects the browser to the same page that generated the XML delta packet or sends it some new content.

    A number of events allow you to insert custom processing at all steps of this update process:

    1. When the dispatcher first passes the update message to the XML broker, it receives a BeforeDispatch event, where you can preprocess the request or even handle it entirely. This event allows the XML broker to handle messages other than update messages.
    2. If the BeforeDispatch event handler does not handle the message, the XML broker receives an OnRequestUpdate event, where you can apply the updates yourself rather than using the default processing.
    3. If the OnRequestUpdate event handler does not handle the request, the XML broker applies the updates and receives a delta packet containing any update errors.
    4. If there are no update errors, the XML broker receives an OnGetResponse event, where you can create a response message that indicates the updates were successfully applied or sends refreshed data to the browser. If the OnGetResponse event handler does not complete the response (does not set the Handled parameter to True), the XML broker sends a response that redirects the browser back to the document that generated the delta packet.
    5. If there are update errors, the XML broker receives an OnGetErrorResponse event instead. You can use this event to try to resolve update errors or to generate a Web page that describes them to the end user. If the OnGetErrorResponse event handler does not complete the response (does not set the Handled parameter to True), the XML broker calls on a special content producer called the ReconcileProducer to generate the content of the response message.
    6. Finally, the XML broker receives an AfterDispatch event, where you can perform any final actions before sending a response back to the Web browser.

    Creating Web pages with a MIDAS page producer

    Each MIDAS page producer generates an HTML document that appears in the browsers of your application's clients. If your application includes several separate Web documents, use a separate MIDAS page producer for each of them.

    The MIDAS page producer is a special page producer component. As with other page producers, you can assign it as the Producer property of an action item or call it explicitly from an OnAction event handler. For more information about using content producers with action items, see "Responding to request messages with action items". For more information about page producers, see "Using page producer components".

    Unlike most page producers, the MIDAS page producer has a default template as the value of its HTMLDoc property. This template contains a set of HTML-transparent tags that the MIDAS page producer uses to assemble an HTML document (with embedded javascript and XML) including content produced by other components. Before it can translate all of the HTML-transparent tags and assemble this document, you must indicate the location of the javascript libraries used for the embedded javascript on the page. This location is specified by setting the IncludePathURL property.

    You can specify the components that generate parts of the Web page using the Web page editor. Display the Web page editor by double-clicking the Web page component or clicking the ellipsis button next to the WebPageItems property in the Object Inspector.

    The components you add in the Web page editor generate the HTML that replaces one of the HTML-transparent tags in the MIDAS page producer's the default template. These components become the value of the WebPageItems property. After adding the components in the order you want them, you can customize the template to add your own HTML or change the default tags.

    Using the Web page editor

    The Web page editor lets you add Web items to your MIDAS page producer and view the resulting HTML page. Display the Web page editor by double-clicking on a MIDAS page producer component.

    Note: You must have Internet Explorer 4 or better installed to use the Web page editor.

    The top of the Web page editor displays the Web items that generate the HTML document. These Web items are nested, where each type of Web item assembles the HTML generated by its subitems. Different types of items can contain different subitems. On the left, a tree view displays all of the Web items, indicating how they are nested. On the right, you can see the Web items included by the currently selected item. When you select a component in the top of the Web page editor, you can set its properties using the Object Inspector.

    Click the New Item button to add a subitem to the currently selected item. The Add Web Component dialog lists only those items that can be added to the currently selected item.

    The MIDAS page producer can contain one of two types of item, each of which generates an HTML form:

    • TDataForm, which generates an HTML form for displaying data and the controls that manipulate that data or submit updates.

      Items you add to TDataForm display data in a multi-record grid (TDataGrid) or in a set of controls each of which represents a single field from a single record (TFieldGroup). In addition, you can add a set of buttons to navigate through data or post updates (TDataNavigator), or a button to apply updates back to the Web client (TApplyUpdatesButton). Each of these items contains subitems to represent individual fields or buttons. Finally, as with most Web items, you can add a layout grid (TLayoutGroup), that lets you customize the layout of any items it contains.

    • TQueryForm, which generates an HTML form for displaying or reading application-defined values. For example, you can use this form for displaying and submitting parameter values.

      Items you add to TQueryForm display application-defined values(TQueryFieldGroup) or a set of buttons to submit or reset those values (TQueryButtons). Each of these items contains subitems to represent individual values or buttons. You can also add a layout grid to a query form, just as you can to a data form.

    The bottom of the Web page editor displays the generated HTML code and lets you see what it looks like in a browser (IE4).

    Setting Web item properties

    The Web items that you add using the Web page editor are specialized components that generate HTML. Each Web item class is designed to produce a specific control or section of the final HTML document, but a common set of properties influences the appearance of the final HTML.

    When a Web item represents information from the XML data packet (for example, when it generates a set of field or parameter display controls or a button that manipulates the data), the XMLBroker property associates the Web item with the XML broker that manages the data packet. You can further specify a dataset that is contained in a dataset field of that data packet using the XMLDataSetField property. If the Web item represents a specific field or parameter value, the Web item has a FieldName or ParamName property.

    You can apply a style attribute to any Web item, thereby influencing the overall appearance of all the HTML it generates. Styles and style sheets are part of the HTML 4 standard. They allow an HTML document to define a set of display attributes that apply to a tag and everything in its scope. Web items offer a flexible selection of ways to use them:

    • The simplest way to use styles is to define a style attribute directly on the Web item. To do this, use the Style property. The value of Style is simply the attribute definition portion of a standard HTML style definition, such as
      color: red.
    • You can also define a style sheet, that defines a set of style definitions. Each definition includes both a style selector (either the name of a tag to which the style always applies or a user-defined style name) and the attribute definition in curly braces:
      H2 B  {color: red}.MyStyle  {font-family: arial; font-weight: bold; font-size: 18px }

      The entire set of definitions is maintained by the MIDAS page producer as its Styles property. Each Web item can then reference the styles with user-defined names by setting its StyleRule property.

    • You can also define a style sheet, that includes a set of style definitions. Each definition consists of a style selector (either the name of a tag to which the style applies or a user-defined style name) and an attribute definition in curly braces:
      H2 B  {color: red}.MyStyle  {font-family: arial; font-weight: bold; font-size: 18px }

      The entire set of definitions is maintained by the MIDAS page producer as its Styles property. Each Web item can then reference the styles with user-defined names by setting its StyleRule property.

    • If you are sharing a style sheet with other applications, you can supply the style definitions as the value of the MIDAS page producer's StylesFile property instead of the Styles property. Individual Web items still reference styles using the StyleRule property.

    Another common property of Web items is the Custom property. Custom includes a set of options that you add to the generated HTML tag. HTML defines a different set of options for each type of tag. The VCL reference for the Custom property of most Web items gives an example of possible options. For more information on possible options, use an HTML reference.

    Customizing the MIDAS page producer template

    The template of a MIDAS page producer is an HTML document with extra embedded tags that your application translates dynamically. Initially, the MIDAS page producer generates a default template as the value of the HTMLDoc property. This default template has the form

    <HTML><HEAD></HEAD><BODY><#INCLUDES> <#STYLES> <#WARNINGS> <#FORMS> <#SCRIPT></BODY></HTML>

    The HTML-transparent tags in the default template are translated as follows:

    <#INCLUDES> generates the statements that include the javascript libraries. These statements have the form

    <SCRIPT language=Javascript type="text/javascript" SRC="IncludePathURL/xmldom.js"> </SCRIPT><SCRIPT language=Javascript type="text/javascript" SRC="IncludePathURL/xmldb.js"> </SCRIPT><SCRIPT language=Javascript type="text/javascript" SRC="IncludePathURL/xmlbind.js"> </SCRIPT>

    <#STYLES> generates the statements that defines a style sheet from definitions listed in the Styles or StylesFile property of the MIDAS page producer.

    <#WARNINGS> generates nothing at runtime. At design time, it adds warning messages for problems detected while generating the HTML document. You can see these messages in the Web page editor.

    <#FORMS> generates the HTML produced by the components that you add in the Web page editor. The HTML from each component is listed in the order it appears in WebPageItems.

    <#SCRIPT> generates a block of javascript declarations that are used in the HTML generated by the components added in the Web page editor.

    You can replace the default template by changing the value of HTMLDoc or setting the HTMLFile property. The customized HTML template can include any of the HTML-transparent tags that make up the default template. The MIDAS page producer automatically translates these tags when you call the Content method. In addition, The MIDAS page producer automatically translates three additional tags:

    <#BODYELEMENTS> is replaced by the same HTML as results from the 5 tags in the default template. It is useful when generating a template in an HTML editor when you want to use the default layout but add additional elements using the editor.

    <#COMPONENT Name=WebComponentName> is replaced by the HTML that the component named WebComponentName generates. This component can be one of the components added in the Web page editor, or it can be any component that supports the IWebContent interface and has the same Owner as the MIDAS page producer.

    <#DATAPACKET XMLBroker=BrokerName> is replaced with the XML data packet obtained from the XML broker specified by BrokerName. When, in the Web page editor, you see the HTML that the MIDAS page producer generates, you see this tag instead of the actual XML data packet.

    In addition, the customized template can include any other HTML-transparent tags that you define. When the MIDAS page producer encounters a tag that is not one of the seven types it translates automatically, it generates an OnHTMLTag event, where you can write code to perform your own translations. For more information about HTML templates in general, see "HTML templates".

    Tip: The components that appear in the Web page editor generate static code. That is, unless the application server changes the metadata that appears in data packets, the HTML is always the same no matter when it is generated. You can avoid the overhead of generating this code dynamically at runtime in response to every request message by copying the generated HTML in the Web page editor and using it as a template. Because the Web page editor displays a <#DATAPACKET> tag instead of the actual XML, using this as a template still allows your application to fetch data packets from the application server dynamically.



    原创粉丝点击