Massimo,
Except from the threading part of this, it will be easier under (x)Harbour
to implement a special RDD for this. Also if I remember correctly Phil asked
for xBase++ RDD documentation but it was too complex to implement a real RDD
for xBase++.

Regards
Mike Evans

-----Original Message-----
From: [EMAIL PROTECTED]
[mailto:[EMAIL PROTECTED] On Behalf Of Massimo Belgrano
Sent: Thursday, January 24, 2008 2:38 PM
To: Harbour Project Main Developer List.
Subject: RE: [Harbour] Re: Licence question

DBFSERVER
DBF Client/Server Suite for Xbase++Server SoftwareClient Connectivity
Library
This document as a whole is copyrighted © 2001 by Phil Ide. The software
package (comprising dbfServer.exe, dbSocketc.dll, header [.ch] files, sample
programs, source code) is copyright © Bjørn Kaarigstad & Phil Ide, 2002. All
Rights Reserved.

The package is released under the terms of the GNU GENERAL PUBLIC LICENCE 
Disclaimer
This software is supplied free of charge, and so there is no warranty for
the software or accompanying documentation (herinafter called the
"package"), to the extent permitted by applicable law. Except when otherwise
stated in writing the copyright holders and/or other parties provide the
package "as is," without warranty of any kind, either expressed or implied,
including, but not limited to, the implied warranties of merchantability and
fitness for a particular purpose. The entire risk as to the quality of this
package is with you.

In no event unless required by applicable law or agreed to in writing will
any copyright holder, or any other party who may modify and/or redistribute
the package as permitted above, be liable to you for damages, including any
general, special, incidental or consequential damages arising out of the use
or inability to use this package (including but not limited to loss of data
or data being rendered inaccurate or losses sustained by you or third
parties).

Any contributions made by others to this package have been acknowledged
where possible. Commercial products, retailers, or distributors mentioned
here are not necessarily endorsed by me or any other particular party. 
Authors
Phil Ide    code and documentation 
Bjørn Kaarigstad  concept, ideas, inspiration 

Acknowledgements
Many thanks to those who tested and broke everything. Special thanks goes to
the following people who made particularly important contributions.

Edgar Borger 
Jan Dirk Schuitmaker 
Mike Evans 
Mike Grace 
Jose Luis Otermin 

Thanks to everyone else who made comments, gave encouragement etc. 
Documentation
Description
The package consists of a server program which accepts requests via TCP/IP
to perform operations on .DBF database tables. All Xbase++ operations are
supported with the exception of dacSession (which includes XbpQuickBrowse)
and compound legacy UI superfunctions (e.g. dbEdit()). The results of these
operations are returned to the client program.

There are many reasons why you might want to remove database operations to a
remote program, but in Xbase++ probably the most compelling reason is the
way that DBF access times deteriorate rapidly as more and more users connect
to the database in a multi-user environment under Windows.

dbfServer still opens the table once for each client connection, spawning a
new thread to service each connecting client. Within Xbase++, each thread
maintains a unique workspace, which has it's own set of unique workareas.
The thread remains active for as along as the server is running and the
client is connected.

Client programs can be forced to use the server by linking in a DLL which
handles all the communication between server and client. A #include file is
supplied which translates all standard database operations to use the
functions in the DLL. For many programs, particularly those generated by the
Form Designer, this is sufficient. However, this is largely dependant upon
coding style, as there are some elements the pre-processor cannot handle,
and so some additional changes are likely to be required.

New code can be written to take advantage of the fact that operations are
performed through the server. Converted legacy code will suffer from a
certain amount of lag since the code was written in the standard 'local
connection' format. Understanding how to reduce lag by caching commands is
fundamental to getting the best performance from the server.

Using code optimised for best performance with regard to the server will
produce fast responsive programs and reduce network traffic immensely
thereby making the network itself faster and more responsive to the benefit
of all users and applications regardless of whether or not they are
utilising dbfServer.

There are several other distinct advantages to using this package: 
Want to lock everybody out of the system? Shut down the server program and
everyone will be unable to access the tables! 
Using the server program console, you can see exactly who is connected,
which machine they are using, which socket number they are connected through
and which files they have open (both tables and indexes). 
Do you have a time-consuming process which you know would run much faster on
the server? dbfServer has the ability to run such procedures for you. Not
only will the process run much faster, but again network traffic will be
vastly reduced to the benefit of all network users. 
It should be pointed out that any DBF table can be accessed by any client
program, provided it is visible to dbfServer. 
Converting Existing Programs
To link to the dbfServer in your own application, you need to do the
following: 
In each source file where database activity takes place, you need to add
this directive:

#include "dbfsocket.ch"


In your project make file, you need to add the following statement to your
executable's list of files:

DBFSOCKETC.LIB


In the same directory as your executable resides, and with the same name but
with an .ini extension you need to create an ini file. This file should
contain the following code:

[SOCKETS]
port=1042
server=localhost

You should change the port number to match the port dbfServer is listening
on, and the server name should be the IP name (or dotted octet address) of
the machine where dbfServer can be found.


There are three types of code that dbfServer cannot handle: 
Whilst it handles:
(cExpr)->fname
(cExpr)->(fname)

it cannot handle:
alias->fname
alias->(fname)

This is due to limitations in the preprocessor. You should change all such
code to enclose the ALIAS in parenthesis.


Expressions generated as macro's from strings will not be parsed properly,
and will still expect local workarea connectivity.


Fieldnames without aliased expressions cannot be pre- determined by the
pre-processor. You must alias them.

For example, if you have a table with the field NAME in it, you cannot do
this:

cName := NAME

Instead, you must do this:

cName := (Alias())->NAME  // or
cName := (cAlias)->NAME


Remember that the path you supply to the program for opening tables is FROM
THE SERVER not the client! 
That's all there is to it! 
Optimising
The Form Designer when used to connect to a database, adds database access
controls to the ::editControls array, which is then evaluated when the
dialog is created or the record pointer is moved (via dialog controls). In a
'local database connection' e.g. a USE  statement, this is ok. In a remote
connection scenario, this becomes innefficient because to refresh 20
controls, you need to send at least 60 instructions to the server. In large
data-intensive dialog's, you should consider another strategy.

One method is to perform dbfScatter()/dbfGather() to/from an array, and
point your dialog controls at the array rather than the database. dbfServer
handles these read/write operations in a single instruction.

All database functions are mapped to the function dbfSocket(). This function
accepts 3 parameters:

dbfSocket(<nFunction> [,<aParams> [, <lCache>] ] )

<nFunction>  This is a numeric constant identifying the database operation
to call. You should use the constants defined in dbfSocket2.ch

<aParams> This optional parameter is an array of parameters to be passed to
the function defined in <nFunction>

<lCache> When this parameter is TRUE, the command is automatically cached
and not sent to the server until a non-cached command is issued.

Certain commands are automatically cached, but you can use dbfSocket()  with
the auto-cache option to increase performance. The automatically cached
commands are a subset of all commands which return NIL. Some NIL-returning
commands are excluded from the subset because they are considered immediate,
e.g. dbCommit() and dbCommitAll() should not be cached, nor unlock
operations.

If you know that you are not interested in the return value from a command,
then you should consider caching it. As an Example, Select(cAlias) may be
used to select a workarea prior to performing operations on it, and if you
are not interested in saving the return value then you should cache the
command.

dbfScatter() and dbfGather() are special functions which request the server
to return and process an array respectively. This is more efficient than a
standard Scatter()/Gather() which sends at least 3 requests to the server
for each element of the array. As with the standard Alaska functions,
dbfScatter() and dbfGather()  handle arrays of simple values or objects.

Additionally, dbfScatter()  can accept an array of code blocks. This can be
used to explicitely identify the fields which are accessed in the event that
you don't need all the fields or want to get field data from multiple
workareas.   
Automatically Cached Commands
The following commands are automatically cached:

dbAppend()    dbGoTo()    dbSetFilter() 
dbClearFilter()  dbGoTop()  dbSetIndex() 
dbClearIndex()  dbRefresh()  dbSetOrder() 
dbClearRelation()  dbResumeSelect()  dbSetRelation() 
dbClearScope()  dbRollBack()  dbSetScope() 
dbCloseRelation()  dbRSuspendSelect()  dbSkip() 
dbGoBottom()  dbSelectArea()  OrdCondSet() 
dbGoPosition()  dbSetDescend()  
 

Remote Procedures
Remote Procedures are functions which are called by the client program, but
are run entirely on the server. The client tells the server to run the
procedure, then waits until the process has completed. The return value of
the process is the return value of the called procedure.

A Remote Procedure is programmed as a function in a dynamically loadable DLL
which must be visible to the server. If the function needs parameters, the
function must accept two parameters. The second parameter is an array
containing all the values the function requires, the first parameter is a
numeric indicating the length of the array.

Note that you cannot pass variables by reference to a remote procedure.

To call the remote procedure, use this command:

REMOTE CALL <func> IN <dll> [WITH <parms,...>] [<lUnload:UNLOAD>]

<func> is the name of the function to call
<dll> is the name (and path if required) of the dll where the function can
be found
<parms> is a comma-seperated list of all parameters that need to be passed
to the function
UNLOAD is an optional statement which causes the dll to be unloaded once the
procedure has finished.

An example of a (totally senseless!) function that can be used as a remote
procedure:

  Function Useless(argc, argv)
    local bFilt := {|| TRUE }
    local i := 0

    if argc > 0
       bFilt := argv[1]
    endif

    dbGoTop()
    While !Eof()
      if Eval(bFilt)
         i += RecNo() // this is silly...
      endif
      SKIP
    Enddo
    return (i)
  


To call this (assume it is linked into "RProc.DLL"):

REMOTE CALL Useless IN RProc WITH {|| GENDER == 'M' } UNLOAD


Error Handling
If the client attempts to perform an operation which fails at the server,
the server returns an error object to the client, which automaticaly passes
it to the current error handler. Any error message displayed by the client
will probably display the callstack of the client program, so you should be
able to track logic errors in your code. In the event the error really
belongs to a bug in the server (oh, horror!), you can find the callstack of
the server embedded in the error object's cargo slot.

By passing the error back to the client to handle, the error is displayed
where the problem most likely lays - if you attempt to write a record
without locking it first, the problem is in the client code not the server.
This also makes the server robust and resilient, allowing other connected
clients to continue working without interruption. 
Rebuilding the Server and dbfSocket.dll
To rebuild either the server or dbfSocketC.dll, you will require
ASINET1C.LIB, which is part of the Professional Subscription.

ASINET1C.DLL, which is part of the Professional Subscription redistributable
libraries, is supplied with the package, and should be placed in your
RUNTIME directory or somewhere in your PATH. 
GNU GENERAL


_______________________________________________
Harbour mailing list
Harbour@harbour-project.org
http://lists.harbour-project.org/mailman/listinfo/harbour

_______________________________________________
Harbour mailing list
Harbour@harbour-project.org
http://lists.harbour-project.org/mailman/listinfo/harbour

Reply via email to