N-tier architecture: Difference between revisions

From OpenPetra Wiki
Jump to navigation Jump to search
 
(6 intermediate revisions by 3 users not shown)
Line 23: Line 23:
** advantage: server knows about the client (stateful); minimum data transfer, diffs are enough.
** advantage: server knows about the client (stateful); minimum data transfer, diffs are enough.
* LogicConnectors: keeps an object on the server side; needed for progress bar; eg. Report Calculation
* LogicConnectors: keeps an object on the server side; needed for progress bar; eg. Report Calculation
* ServerLookup: used for static methods; no object is held on the server (stateless); eg. Verify Partner in ExperimentingClient; UserDefaults uses ServerLookups internally
* ServerLookups: used for static methods; no object is held on the server (stateless); eg. Verify Partner in ExperimentingClient; UserDefaults uses ServerLookups internally
** WebConnector: very similar to ServerLookup (stateless); easy generation of interface and instantiator, and winforms
** WebConnectors: very similar to ServerLookup (stateless); easy generation of interface and instantiator, and winforms
* Cacheable: also static methods; they are not called by the developer, but by the Cachemanager on the server and the client side, when you are accessing a cached table
* Cacheable: also static methods; they are not called by the developer, but by the Cachemanager on the server and the client side, when you are accessing a cached table


Line 57: Line 57:
** nant generateWinforms will add functions to edit windows, eg. for retrieving data and saving data, using webconnectors
** nant generateWinforms will add functions to edit windows, eg. for retrieving data and saving data, using webconnectors
* WebConnector files are identified by their namespace ending with WebConnectors, and the class name ends in WebConnector. There is no interface, because the methods are static, and that won't work for interface methods.
* WebConnector files are identified by their namespace ending with WebConnectors, and the class name ends in WebConnector. There is no interface, because the methods are static, and that won't work for interface methods.
* Namespaces in auto-generated <code>*.Interfaces-generated.cs</code> files: Any Namespace that is needed for the signature (e.g. for the Return Value or the Arguments) of a public Method of a WebConnector or UIConnector and that isn't listed in the file <code>\inc\template\src\ClientServerGlue\Interface.cs</code> in the code section above <code>{#USINGNAMESPACES}</code> needs to be manually added to the file <code>\csharp\ICT\Petra\Shared\lib\Interfaces\InterfacesUsingNamespaces.yml</code>! (You'll see the pattern on how to do this once you open the file.)


== todo ==
== todo ==
Line 76: Line 77:
* options: for complex screens with several tabs, store the whole Typed Data Set, or each tab on its own?
* options: for complex screens with several tabs, store the whole Typed Data Set, or each tab on its own?
* names that the generateWinforms is looking for in the server: Create<TableName>, Find<TableName>, Save<TableName>, Save<TypedDataSet>
* names that the generateWinforms is looking for in the server: Create<TableName>, Find<TableName>, Save<TableName>, Save<TypedDataSet>
= Encrypted Connection between Server and Client =
There is no encryption required for the development setup and for the standalone setup.
If you run the server on your local network, or even on the Internet, you should setup a secure connection between client and server.
You will need to create a private/public key pair and install it on the server. You can use the GenerateEncryptionKey.exe from the csharp\ICT\PetraTools\_bin\Debug directory.
You should publish the public key of the server so that the clients can download the public key. It is recommended to publish that key on a certified https website, so that the identity of the website is reliable. When downloading from https with a self signed certificate, the connection will fail. Such a certificate can be bought for even less than 20 Euros a year.
Alternatively, you can store the public key directly on the client.
You need to modify the following configuration files:
* setup\petra0300\remoteclientWin\PetraClientRemote.config
** activate the commented line <provider type="Ict.Petra.Shared.RemotingSinks.Encryption.EncryptionClientSinkProvider, Ict.Petra.Shared.RemotingSinks" HttpsPublicKeyXml="https://PETRAHOST/publickey.xml"/> by removing the &lt;!-- and --&gt;
** change the value for HttpsPublicKeyXml to point at a certified source
** Alternatively, you can use '''FilePublicKeyXml''' instead of '''HttpsPublicKeyXml''', and point at a local file on the client.
* setup\petra0300\linuxserver\PetraServerConsole-&lt;YourDatabase&gt;.config
** activate the commented line <provider type="Ict.Petra.Shared.RemotingSinks.Encryption.EncryptionServerSinkProvider, Ict.Petra.Shared.RemotingSinks"/> by removing the &lt;!-- and --&gt;
** You also have to modify Server.ChannelEncryption.PrivateKeyfile to point at your private key file
This happens when the client connects to the server:
# The client downloads the public key from the certified website. If the website is bogus, or someone pretends to be that website, then the certificate will not be valid, and the download will fail.
# The client creates a new symmetric key for each session. This symmetric key is sent once per session to the server, but it is encrypted using the public key of the server, so only the server can decrypt it using its private key.
# During a session, all data sent by the client and the server is encrypted with the symmetric key. This is less demanding for the CPU than using Public/Private key algorithms. Since only the client and the server know the symmetric key, and it is only used for one session, all the data is transported securely between server and client.
= Issues with .Net Remoting and possible solutions =
== iptables forwarding ==
[https://sourceforge.net/apps/mantisbt/openpetraorg/view.php?id=276 Bug 276] describes the problem that the OpenPetra server is running on a virtual server, using linux vserver, and we are using port forwarding with iptables. Usually, the server publishes its local IP address to the client, and the communication cannot work. The solution is the new configuration parameter '''ListenOnIPAddress''' which contains the public IP address.
In addition to that, at the moment all ports need to be forwarded for each client, and the forwarding has to keep the port number. This needs to be resolved at some point as well. See next topic below about ports for each client.
== using an extra port for each client ==
At the moment, there is one port for the main server, which the clients connect to for authentication. On successful authentication, a new appdomain is created on the server, and this appdomain gets an own channel on a separate port. Ports are reused, when a client disconnects.
This is not good for environments, where only the port 80 and port 443 are open, and the others are blocked by the firewall, since we have not got a chance to connect several clients on the same port.
See also [https://sourceforge.net/apps/mantisbt/openpetraorg/view.php?id=277 Bug 277], and http://www.codeproject.com/KB/IP/CrossDomainRemoting.aspx seems to be the answer to the problem.
== connecting through a web proxy ==
Here is a topic at StackOverflow: http://stackoverflow.com/questions/2027189/how-do-i-use-net-remoting-through-a-web-proxy that describes the issue.

Latest revision as of 21:05, 11 Mayıs 2022

Client/Server Architecture

There are several options how to design a client/server architecture.

  • It depends on what environment you are using (bandwidth, datalink speed, etc) and on your requirements (supported operating systems etc).
  • On the other hand you want to try to keep the logic in one place (the server), so that several types of clients can connect the one server.
  • Also access to the database should go all through the server, so that you don't need to create a database user for each of your users, and access permissions are handled by the server.

Several types of clients:

  • Standalone: this is for single user systems; they don't need to know that a server is started in the background.
  • Fat client, Rich client: this is in our case a Winforms application, that connects to a server. The client can handle the logic, but perhaps we want to keep it still simple on the client. Updates/Patches to the client need to be run semi automatically.
  • Slim Client: this could be a web client; it requires no installation effort.
    • Rich Internet applications (RIAs) look like desktop applications, but run in the web browser

Another topic is the server itself:

  • for our .net remoting setup, we have an Appdomain for each client, ie. all the dlls are loaded for every connected client
    • this helps with server crashes: if one client crashes, the other clients can continue with work
    • on the other hand, this uses a lot of memory
  • for the SOAP/Web service setup of the server, there should be much better scalability
  • how much should the server keep track of the state of the client? stateful vs stateless

Glue between Server and Client

different ways of accessing data on the server

  • UIConnectors: only used for screens; keep an object on the server for each screen
    • advantage: server knows about the client (stateful); minimum data transfer, diffs are enough.
  • LogicConnectors: keeps an object on the server side; needed for progress bar; eg. Report Calculation
  • ServerLookups: used for static methods; no object is held on the server (stateless); eg. Verify Partner in ExperimentingClient; UserDefaults uses ServerLookups internally
    • WebConnectors: very similar to ServerLookup (stateless); easy generation of interface and instantiator, and winforms
  • Cacheable: also static methods; they are not called by the developer, but by the Cachemanager on the server and the client side, when you are accessing a cached table


How to create a new Serverlookup

Depending on its namespace and for server lookups you can use:

  1. Ict.Petra.Server.MPartner.Partner.ServerLookups

nant will collect all "public static <functionName />(<parameterList />)" functions and include their headers into the automatic generated file "csharp\ICT\Petra\Shared\lib\Interfaces\Partner.Interfaces.cs" and "csharp\ICT\Petra\Server\lib\MPartner\Instantiator.AutoHierarchy.cs".

So on the server side you have to

  • use a valid namespace
  • "public static" to define the function

If you want to call this function by the client you have to use:

  1. TRemote.MPartner.Partner.ServerLoookups.<functionName />(<parameterList />)

(The namespaces on the server and the client are mapped one-to-one)

code generation

  • the code for interfaces and instantiators is generated
  • run nant generateGlue for the first time
  • to add new modules (on the level of MFinance, MCommon, etc), search for REMOTINGURL_IDENTIFIER_MCOMMON in ICT/Petra/Shared/Constants.cs and ICT/Petra/Server/app/Core/ClientAppDomainConnector.cs and create similar lines of code for the new module
  • then you can create a UIConnector .cs file; the constructor will be added to the Instantiator and to the interface the next time you run nant generateGlue
  • UIConnector classes are inside a namespace ending with UIConnectors, and implementing an interface, which will be autogenerated by nant generateGlue. The class name should end with UIConnector
  • a method that should not be added to the interface, should either be private, or have the attribute [NoRemoting]
  • Web connector methods are the easiest - they will be added to the interface and the instantiator, and even the winforms: just make sure to add your method to the Webconnector class; run nant generateGlue, it will create the interface and instantiator for you
    • nant generateWinforms will add functions to edit windows, eg. for retrieving data and saving data, using webconnectors
  • WebConnector files are identified by their namespace ending with WebConnectors, and the class name ends in WebConnector. There is no interface, because the methods are static, and that won't work for interface methods.
  • Namespaces in auto-generated *.Interfaces-generated.cs files: Any Namespace that is needed for the signature (e.g. for the Return Value or the Arguments) of a public Method of a WebConnector or UIConnector and that isn't listed in the file \inc\template\src\ClientServerGlue\Interface.cs in the code section above {#USINGNAMESPACES} needs to be manually added to the file \csharp\ICT\Petra\Shared\lib\Interfaces\InterfacesUsingNamespaces.yml! (You'll see the pattern on how to do this once you open the file.)

todo

  • Todo: something special about UIConnectors; extra things are generated for UIConnectors
  • the others are implemented manually at the moment


  • TODO: progress bar; need different threads; keep the connection; prevent the object from being garbage collected
    • need extra object on server for progress and cancel option (eg FReportingGenerator.AsyncExecProgress)
    • keep the main object (eg. FReportingGenerator) alive by registering it
  • Table Maintance: use UIConnector

Data Processing in an n-tier architecture

  • example 1: create a new partner: new partner key is generated, will never be reused, even if the new partner is never saved to the database
  • example 2: create a new GL batch; it is important to have consecutive batch numbers; cancel batch if it is not needed anymore; batch is stored with status "cancelled"
  • example 3: AP Document is created with negative AP number, proper AP number is only assigned on submitting changes
  • options: create new row on server, or on client
  • options: for complex screens with several tabs, store the whole Typed Data Set, or each tab on its own?
  • names that the generateWinforms is looking for in the server: Create<TableName>, Find<TableName>, Save<TableName>, Save<TypedDataSet>