ApplicationServer Boilerplate (.NET)
ApplicationServer Boilerplate code is an entire server app infrastructure built available via a single line of code.
The ApplicationServer
class eliminates the need for a whole bunch of boiler-plate code that used to make up an RO server application. With literally a single line of code, the server application can be up and running, and the ApplicationServer
class takes care of:
- Processing command line parameters to run the server in command-line, GUI and Windows Service/Dæmon mode
- Setting up the infrastructure to register, unregister or launch as Windows Service
- Providing a default window, in GUI mode
- Setting up the server channel and messages via code or via external configuration
- Setting up TLS traffic encryption
- Optionally making sure only a single instance of the server is active
- Providing events for exception handling, as well as startup and shutdown
This simple line of code provides all these features:
public static int Main(string[] args)
{
new ApplicationServer("Server App Name").Run(args);
return 0;
}
begin
new ApplicationServer('Server App Name').Run(args);
result := 0;
end.
ApplicationServer("Server App Name").Run(C_ARGV.nativeArray);
result := 0;
Public Shared Function Main(args As String()) As Int32
Dim server As New ApplicationServer("Server App Name")
server.run(args)
End Function
Command Line Parameters
By default the following command-line arguments are recognized (not case-sensitive):
Parameters | Description |
---|---|
-i, /i, --install | Install service |
-u, /u, --uninstall | Uninstall service |
-q, /q, --quiet | Suppress messages on service installation and uninstallation |
-c, /c, --commandline | Run in command-line/console mode |
-d, /d, --debug | Request extended debug info (e.g. full stacktraces) |
-h, /h, -?, /? | Show command-line arguments help message |
You don't have to write any code to handle these actions.
Additionally, the ApplicationServer
class provides APIs for fine-tuning the server channel used, TLS Certificates management, and so on. A sample of such calls is provided by the Remoting SDK server app template:
public static int Main(string[] args)
{
ApplicationServer server = new ApplicationServer("ROServer9");
//
// Some optional setup for your server:
//
// TLS
server.AutoCreateSelfSignedCertificate = true;
server.NetworkServer.UseTLS = true;
//server.NetworkServer.CertificateFileName = "</path/to/certificate>";
//server.NetworkServer.CertificateThumbprint = "XX XX XX ...";
//server.NetworkServer.Certificate = <certificate instance>
//server.NetworkServer.ServerChannel = new IpSuperHttpServerChannel();
//server.NetworkServer.Port = 8099;
server.Run(args);
return 0;
}
class method Program.Main(args: array of String): Integer;
begin
var server := new ApplicationServer('ROServer9');
//
// Some optional setup for your server:
//
// TLS
server.AutoCreateSelfSignedCertificate := true;
server.NetworkServer.UseTLS := true;
//server.NetworkServer.CertificateFileName := '</path/to/certificate>';
//server.NetworkServer.CertificateThumbprint := 'XX XX XX ...';
//server.NetworkServer.Certificate := <certificate instance>
//server.NetworkServer.ServerChannel := new IpSuperHttpServerChannel();
//server.NetworkServer.Port := 8099;
server.Run(args)
end;
let server = ApplicationServer("ROServer9")
//
// Some optional setup for your server:
//
// TLS
server.AutoCreateSelfSignedCertificate = true
server.NetworkServer.UseTLS = true
//server.NetworkServer.CertificateFileName = "</path/to/certificate>"
//server.NetworkServer.CertificateThumbprint = "XX XX XX ..."
//server.NetworkServer.Certificate = <certificate instance>
//server.NetworkServer.ServerChannel = new IpSuperHttpServerChannel()
//server.NetworkServer.Port = 8099
server.Run(C_ARGV.nativeArray)
Public Shared Function Main(args As String()) As Int32
Dim server As New ApplicationServer("ROServer9")
'
' Some optional setup for your server:
'
' TLS
server.AutoCreateSelfSignedCertificate = True
server.NetworkServer.UseTLS = True
' server.NetworkServer.CertificateFileName = "</path/to/certificate>"
' server.NetworkServer.CertificateThumbprint = "XX XX XX ..."
' server.NetworkServer.Certificate = <certificate instance>
' server.NetworkServer.ServerChannel = New IpSuperHttpServerChannel()
' server.NetworkServer.Port = 8099
server.Run(args)
Return 0
End Function
The server infrastructure can be easily expanded, with little code. For example, it is possible to provide a custom GUI implementation or a custom NetworkServer implementation by simply adding the corresponding component to the project and marking it with the ServerInfrastructure attribute.
Custom Network Server
In the following code a NetworkServer that exposes an additional ServerChannel is defined:
[ServerInfrastructure]
public class ExtendedNetworkServer : NetworkServer
{
private IpHttpServerChannel _serverChannel;
protected override void InternalStart()
{
base.InternalStart();
this._serverChannel = new IpHttpServerChannel();
this._serverChannel.Port = this.Port + 1;
this._serverChannel.Dispatchers.AddRange(this.ServerMessages.Select(m =>
new MessageDispatcher(m.DefaultDispatcherName, m)));
this._serverChannel.Open();
}
protected override void InternalStop()
{
this._serverChannel.Close();
base.InternalStop();
}
}
type
[ServerInfrastructure]
ExtendedNetworkServer = public class(NetworkServer)
private
var fServerChannel: IpHttpServerChannel;
protected
method InternalStart(); override;
method InternalStop(); override;
end;
// ...
method ExtendedNetworkServer.InternalStart();
begin
inherited InternalStart();
self.fServerChannel := new IpHttpServerChannel();
self.fServerChannel.Port := self.Port + 1;
self.fServerChannel.Dispatchers.AddRange(self.ServerMessages.Select((m) -> new MessageDispatcher(m.DefaultDispatcherName, m)));
self.fServerChannel.Open();
end;
method ExtendedNetworkServer.InternalStop;
begin
self.fServerChannel.Close();
inherited InternalStop();
end;
@ServerInfrastructure
public class ExtendedNetworkServer : NetworkServer {
private var _serverChannel: IpHttpServerChannel!
public override func InternalStart() -> ()! {
super.InternalStart()
self._serverChannel = IpHttpServerChannel()
self._serverChannel.Port = self.Port + 1
self._serverChannel.Dispatchers.AddRange(self.ServerMessages.Select({ (m) in
MessageDispatcher(m.DefaultDispatcherName, m)
}))
self._serverChannel.Open()
}
public override func InternalStop() -> ()! {
self._serverChannel.Close()
super.InternalStop()
}
}
<ServerInfrastructure> _
Public Class ExtendedNetworkServer
Inherits NetworkServer
Private Dim _serverChannel as IpHttpServerChannel
Protected Overrides Sub InternalStart()
MyBase.InternalStart()
Me._serverChannel=New IpHttpServerChannel()
Me._serverChannel.Port=Me.Port+1
Me._serverChannel.Dispatchers.AddRange(From m In Me.ServerMessages _
Select New MessageDispatcher(m.DefaultDispatcherName, m))
Me._serverChannel.Open()
End Sub
Protected Overrides Sub InternalStop()
Me._serverChannel.Close()
MyBase.InternalStop()
End Sub
End Class
This NetworkServer implementation will then be automatically used instead of the default one.
Starting
/Stopped
Events
Alternatively, ApplicationServer
also exposes Starting
and Stopped
events that can be used to perform additional setup and deconstruction:
public static int Main(string[] args)
{
ApplicationServer server = new ApplicationServer("RO9Server");
server.Starting += (sender, ea) =>
{
// additional setup
};
server.Stopped += (sender, ea) =>
{
// additional teardown
};
server.CertificateGenerating += (sender, ea) =>
{
// here HashAlgorithm, Subject and Issuer of the self-signed certificate can be changed
};
server.NetworkServer.Starting += (sender, ea) =>
{
// additional setup of the NetworkServer (in some cases this even allows
// to avoid defining a custom NetworkServer class)
// also Started, Stopping and Stopped events are available
};
server.Run(args);
return 0;
}
begin
var lServer := new ApplicationServer('Server App Name');
lServer.Starting += method begin
// additional setup
end;
lServer.Stopped += method begin
// additional teardown
end;
lServer.CertificateGenerating += method begin
// here HashAlgorithm, Subject and Issuer of the self-signed certificate can be changed
end;
lServer.NetworkServer.Starting += method begin
// additional setup of the NetworkServer (in some cases this even allows
// to avoid defining a custom NetworkServer class)
// also Started, Stopping and Stopped events are available
end;
lServer.Run(args);
result := 0;
end.
let server = ApplicationServer("Server App Name")
server.Starting += {
// additional setup
}
server.Stopped += {
// additional teardown
}
server.CertificateGenerating += {
// here HashAlgorithm, Subject and Issuer of the self-signed certificate can be changed
}
server.NetworkServer.Starting += {
// additional setup of the NetworkServer (in some cases this even allows
// to avoid defining a custom NetworkServer class)
// also Started, Stopping and Stopped events are available
}
server.Run(C_ARGV.nativeArray);
result := 0;
Public Shared Function Main(args As String()) As Int32
Dim server As New ApplicationServer("ServerSampleVB")
AddHandler server.Starting,
Sub(sender As Object, e As EventArgs)
' additional setup
End Sub
AddHandler server.Stopped,
Sub(sender As Object, e As EventArgs)
' additional teardown
End Sub
AddHandler server.CertificateGenerating,
Sub(sender As Object, e As CertificateGeneratingEventArgs)
' here HashAlgorithm, Subject and Issuer of the self-signed
' certificate can be changed
End Sub
AddHandler server.NetworkServer.Starting,
Sub(sender As Object, e As EventArgs)
' additional setup of the NetworkServer (in some cases this even
' allows to avoid defining a custom NetworkServer class)
' also Started, Stopping and Stopped events are available
End Sub
server.Run(args)
Return 0
End Function
Custom GUI
A custom WinForms form can be provided as custom GUI to be shown instead of the default one. It has to be marked with the ServerInfrastructure
attribute and has to provide a constructor that accepts a String
(the application name) and an INetworkServer
instance.
It is up to the form class to activate and deactivate the network server based on user's actions. A simples custom GUI form could look like this:
[ServerInfrastructure]
public partial class ServerGui : Form
{
private readonly INetworkServer _networkServer;
public ServerGui()
{
InitializeComponent();
}
public ServerGui(String serverName, INetworkServer networkServer) : this()
{
this.Text = serverName;
this._networkServer = networkServer;
this._networkServer.Start();
}
private void ServerGui_FormClosed(object sender, FormClosedEventArgs e)
{
this._networkServer.Stop();
}
}
type
ServerGUI = partial class(System.Windows.Forms.Form)
private
{$REGION designer-generated code}
method CustomGUI_FormClosed(sender: Object; e: FormClosedEventArgs);
{$ENDREGION}
var fNetworkServer: INetworkServer; readonly;
protected
method Dispose(aDisposing: Boolean); override;
public
constructor();
constructor(serverName: String; networkServer: INetworkServer);
end;
//...
constructor CustomGUI(serverName: String; networkServer: INetworkServer);
begin
constructor();
self.Text := serverName;
self.fNetworkServer := networkServer;
self.fNetworkServer.Start();
end;
method CustomGUI.Dispose(aDisposing: Boolean);
begin
if aDisposing then begin
if assigned(components) then
components.Dispose();
end;
inherited Dispose(aDisposing);
end;
method CustomGUI.CustomGUI_FormClosed(sender: Object; e: FormClosedEventArgs);
begin
self.fNetworkServer.Stop();
end;
@ServerInfrastructure
public __partial class CustomGUI: Form {
private var _networkServer: INetworkServer!
public init() {
InitializeComponent()
}
public init(_ serverName: String!, _ networkServer: INetworkServer!) {
InitializeComponent()
self.Text = serverName
self._networkServer = networkServer
self._networkServer.Start()
self.FormClosed += { (s, e) in
self._networkServer.Start()
}
}
}
<ServerInfrastructure> _
Public Class CustomGUI
Private _networkServer As INetworkServer
Public Sub New(serverName As String, networkServer As INetworkServer)
' This call is required by the designer.
InitializeComponent()
Me.Text = serverName
Me._networkServer = networkServer
Me._networkServer.Start()
End Sub
Private Sub CustomGUI_FormClosed(sender As Object, e As FormClosedEventArgs) _
Handles MyBase.FormClosed
Me._networkServer.Stop()
End Sub
End Class