Midware Theory
Have you heard the term - MIDDLEWARE? It's the Application Layer you put in the "middle" of your multi-tiered architecture software, allowing the various layers or "Tiers" to talk to each other. Middleware is the "glue" used to build efficient and scalable N-Tier Client/Server programs.
I have developed a middleware framework called MidWare. It consists of a set of components and units which allow you to create an Application Server and related thin Client application, in just a few minutes. All without worrying about Client connections, data formatting and other details which make a multi-tiered Client/Server program so difficult to write. MidWare also include CGI, ISAPI and ASP modules for web applications.See MidWare Web Technology page.
How does it work ?
A MidWare Application Server is based on: - A Server component - An Object Request Broker (ORB) - A set of Server objects you write
A MidWare Client Application is based on: - One or more application Server Client objects - One or more TMWBuffer object - Zero or more TMWTable objects
Although there is not much documentation, you DO get full source code and some sample applications. Source code is heavily commented, so you should read the source carefully and try to understand how it works. If you have questions, there is a related mailing list available for support.
How to build an Application Server
How to build a Client
About Multi-tasking
About Global Data
Components
Applications
Server side demo Apps
There are two Server demo programs: SrvTst and SrvDemo. The first one uses a TServerObject object added to the the ORB at runtime, using AddServerObject.
The second is more visual: A TServerObject is dropped on the main Server form and linked with the object inspector to the ORB. (Using AddServerObject results in a bit smaller programs while dropping an object on a form is more RAD, but can be annoying for large projects where you have a lot of TServerObject.)
Description for each TServerObject used by the sample Servers:
TServerObjectUPPER (SrvObj1) simply converts data received to uppercase and sends it back to the Client
TServerObjectTHREAD (SrvObj2) has a thread which does a lengthy request (just sleeps for 5 seconds)
TServerObjectGETClient (SrvObj3) will access a TTable, fetch some data based on a key sent by the Client and return the fetched data to the Client. See TServerObjectSETClient for reverse operation: update data table.
TServerObjectGETClientLIKE (SrvObj4) uses a TQuery and returns the entire result set to the Client.
TServerObjectGETFILE (SrvObj5) will get a file and return a single blob field to the Client. See TSObjSENDFILE for reverse operation.
TSObjGETIMAGE (SrvObj6) is much like TServerObjectGETClient and uses a TTable to fetch data from a table.
TSObjGETIMAGELIKE (SrvIbj7) is much like TServerObjectGETClientLIKE and uses a TQuery to fetch data using a SQL statement.
TSObjSENDFILE (SrvObj8) receives a file name and data from a Client and saves data into the specified filename at the Server side. The Filename is the first field from the Client and theData is the second field from the Client (a single field can hold MB of data !). See TServerObjectGETFILE for reverse operation.
TServerObjectSETClient (SrvObj9) receives data from a Client and updates a TTable with that data. See TServerObjectGETClient for reverse operation: fetch data from table.
TServerObjectSQL (SObjSQL) uses a TQuery to execute a SQL statement sent by the Client and returns the result set to the Client. It also demonstrates how to retrieve data set in the object inspector at design time (CopyFromReference).
- Remember that you dropped a single TServerObjectSQL on the form, but many Clients can be served and therefore many instances may be used, so data from the reference object dropped on the form has to be copied to instanciated objects. This object also demonstrates how to create properties visible in the object inspector at design time: (a DatabaseName property is created and used.) TServerObject are not different from any other component.
TServerObjectSQLTHREAD (SObjThrd) is much like TServerObjectSQL except that it uses a thread to execute the request in the background. Without the thread, the Server is blocked from servicing a Client while the query is executing. With a thread, the query is done in the background while other Clients are being serviced. Note: Using threads with TServerObject is easy (see TServerObjectTHREAD for a trivial example), but using BDE within a thread is quite complex. Using the BDE, you need different TSession and TDatabase components for each thread, which in turn require unique identifications, a private directory for temporary files and a shared directory for Paradox lock files, etc.
Client side demo apps
The first demo Client ( CliTst ) is very simple: just a few edit boxes to let the user enter some data and a few button to act on that data.
The TblDemo Client uses a DBGrid to show data retrieved from the Server.
BufTst is a demo which shows how to use TMWBuffer alone.
The BioLife demo uses BioLife data from Delphi demo database. It shows how to retrieve data from a MidWare Server, including memo fields and images. BioLife uses SrvDemo as the application Server. You must copy biolife.db, biolife.mb, biolife.px from delphi\demos\data to the directory where the SrvDemo is run.
ChatSrv and ChatCli are the two parts of a multi-user Server based Chatting application. This demonstrates that MidWare can be used for any kind of application, including groupware applications.
How to build an Application Server
An Application Server is just a program like any other Delphi program. It contains a main form and code to handle the related processing.
In a MidWare Application Server, most of hard work is done by the MidWare components. The effective application work is done by specialized components derived from TServerObject. You may use the existing TServerObject or design new ones yourself. The MidWare request broker will instanciate your TServerObject as needed when requests are received from Clients.
To build an Application Server, you have to design TServerObject descendant which will receive the request sent by the Client, then do the processing, database access and whatever else is needed to built the reply. You make a TServerObject for each request. Each request has a name or function code which is used by the Client to identify which request they want to execute.
Once you made the TServerObject, you build a new form, then drop a TAppServer and a TRequestBroker object on it. At runtime, you link all you Server objects to the Request Broker (just a function call). And that's it! Your Server is ready!! (This is how the SrvDemo is made)
How to build a Client
To build a Client, open a new form, drop a TAppSvrClient, a TMBuffer and other standard components to build a user interface. Then on some event like a button click, build a TAppSvrClient request (just a function code with parameters) and call the Send method to send it to the application Server. You'll get an event back when the result has arrived from the Server. Extract the data from the TMWBuffer and update your user interface. (This is how the CliTst demo application is made)
Alternatively, (if you want to use standard data aware components to build your user interface) you can link the TMBuffer to a TMWTable, link the TMBuffer to a TDataSource and the TDataSource to a TDBGrid and a TDBNavigator. (This is how the demoTblTst is made)
About multitasking
MidWare Server is based on an event driven architecture. Internally, it uses an asynchronous TWSocket component (See ICS package) to do all the communication related operations. MidWare communication operations never block the Server. They will take place in the background as long as windows message pump is operating correctly.
When a request is received, the MidWare request broker will locate a TServerObject class that has the responsibility of executing Client requests. It then will instanciate a new TServerObject and call its related Execute method. When the Execute method returns, the MidWare request broker will process the next request.
When the TServerObject Finish method is called, the MidWare request broker will pass the result set to the main Server for sending it to the Client. This will not block the Server, even if result set has several megabytes !
While a TServerObject is executing, nothing else can happen in the Server. A good TServerObject is written so that the Execute method, is as fast as possible. When processing takes a long time, it is not a good idea to do it in the Execute method. Rather, use a thread or an event driven component to execute it in the background. Start the thread or event driven component from the Execute method and call the Finish method from thread termination code or the component request done event.
You can find a good example in TServerObjectSQLTHREAD where a thread is launched to execute a lengthy SQl request. Moving the processing from the Execute method to a thread is not difficult at all, but depending on what processing is involved, it may require some additional work. For example, BDE is very easy to use in single threaded programs, but there is more work required to make it works in a multithreaded environment. This is a BDE issue, not a MidWare issue! The BDE requires a separate TSession and separate TDatabase component for each thread.