In this section, we shall construct a simple example of using
Kharon with KNC to write a very simple Kerberised client/server
application in just a few minutes.
The Object
In order to start with Kharon programming, the first thing that one wants
to do is construct an object which will perform the functionality that is
expected to live on the server. For our example, let’s consider a simple
service based on the canonical OO tutorial object, the incrementer.
We will add a couple of methods “complicated” and “exception” to
demonstrate slightly more complicated data structures and exception
handling. The use of the object should be relatively self-explanatory.
package Example::Implementation;
use strict;
use warnings;
sub new {
my $self;
$self->{num} = 1;
bless($self);
}
sub inc {
my ($self) = @_;
$self->{num}++;
undef;
}
sub query {
my ($self) = @_;
return $self->{num};
}
sub complicated {
return ("foo", ["bar", "baz", {a=>'b', c=>'d', wow=>undef} ]);
}
sub exception {
die "alkjsnckjsanclkjac";
}
1;
The Program
We can now write a simple program that uses our object:
#!/usr/pkg/bin/perl
use Example::Implementation;
use strict;
use warnings;
my $obj = Example::Implementation->new();
my $ret = $obj->query();
print "$ret\n";
$obj->inc();
$obj->inc();
$obj->inc();
$ret = $obj->query();
print "$ret\n";
$obj->complicated();
$obj->exception();
This program will output:
1
4
alkjsnckjsanclkjac at Example/Impementation.pm line 34.
Making our object into a daemon
So, far, though, we have not yet made a client/server application.
All we have is an object and a program that speaks to it. What we’ll
need to do now is make the object into a server. Here’s how we do it:
#!/usr/pkg/bin/perl
use Example::Implementation;
use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Server;
use strict;
use warnings;
#
# Instantiate our object:
my $obj = Example::Implementation->new();
#
# Setup Kharon:
my $ahr = Kharon::Protocol::ArrayHash->new(banner => { version => '2.0' } );
my $pes = Kharon::Engine::Server->new(protocols => [$ahr]);
$pes->Connect();
#
# And now ``Run the Object'', exporting the methods inc, query, exception
# and complicated:
$pes->RunObj(object => $obj, cmds => [ qw/inc query exception complicated/ ]);
This is a program that will interact on stdin/stdout in the following
way:
Now, you have a daemon that speaks the protocol that we discussed in
the previous section.
Connecting to the daemon remotely via knc (interactively)
You can further test your daemon by connecting to it remotely:
$ knc elric@stormbringer.imrryr.org 2666
This is a useful thing to remember, because it can be useful when
debugging to connect directly to your server and issue commands to
it and see how it responds.
Making a client module
Now, we need to write a client module, let’s call it Example/Client.pm:
package Example::Client;
use base qw(Kharon::Class::Client);
our @KHARON_RW_SC_EXPORT = qw/inc query exception/;
our @KHARON_RW_AC_EXPORT = qw/complicated/;
use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Client::Knc;
use strict;
use warnings;
sub new {
my ($isa, @servers) = @_;
my $self;
my $ahr = Kharon::Protocol::ArrayHash->new(banner => {version=>'2.0'});
my $pec = Kharon::Engine::Client::Knc->new(protocols => [$ahr]);
$pec->SetServerDefaults({KncService=>'service1', PeerPort=>2666});
$pec->Connect(@servers);
$self->{pec} = $pec;
bless($self, $isa);
}
1;
Changing our original program to be a client
We now have two objects defined in modules. The first
Example/Impementation.pm provides our functionality and is used by
the server side. The second Example/Client.pm has almost exactly
the same interface as Example/Impementation.pm but when its methods
are called it will marshall up the arguments and send them over
the wire to the server which will execute the method and return
the results.
So, we can rewrite our original program just adding:
use Example::Client;
And then modifying the instantiation of $obj from using
Example::Impementation to Example::Client.
my $obj = Example::Client->new('dowdeslx');
And now we have a Kerberised, mutually authenticated, encrypted,
integral client/server application.
Improving Our Simple Example
So, we note that in our simple example, we duplicate the specification
of the exported methods in two places: the daemon and the client
module. This is necessary to export Perl objects that have not
been designed with Kharon in mind but we can get around it if we’re
writing modules specifically for Kharon.
What we need to do to remove this redundancy is to define a superclass
for Example::Implementation and Example::Client which we will call
Example. So, we can write Example.pm as:
And change Example/Client.pm and Example/Implementation.pm to inherit from
it. The @KHARON_* variables can then be removed from Example/Client.pm
and we can eliminate the passing of cmds to RunObj in daemon.
The new files look like this…
Example/Implementation.pm
package Example::Implementation;
use base qw(Example);
use strict;
use warnings;
sub new {
my $self;
$self->{num} = 1;
bless($self);
}
sub inc {
my ($self) = @_;
$self->{num}++;
undef;
}
sub query {
my ($self) = @_;
return $self->{num};
}
sub complicated {
return ("foo", ["bar", "baz", {a=>'b', c=>'d', wow=>undef} ]);
}
sub exception {
die "alkjsnckjsanclkjac";
}
1;
Example/Client.pm
package Example::Client;
use base qw(Example Kharon::Class::Client);
use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Client::Knc;
use strict;
use warnings;
sub new {
my ($isa, @servers) = @_;
my $self;
my $ahr = Kharon::Protocol::ArrayHash->new(banner => {version=>'2.0'});
my $pec = Kharon::Engine::Client::Knc->new(protocols => [$ahr]);
$pec->SetServerDefaults({KncService=>'service1', PeerPort=>2666});
$pec->Connect(@servers);
$self->{pec} = $pec;
bless($self, $isa);
}
1;
daemon
#!/usr/pkg/bin/perl
use Example::Implementation;
use Kharon::Protocol::ArrayHash;
use Kharon::Engine::Server;
use strict;
use warnings;
#
# Instantiate our object:
my $obj = Example::Implementation->new();
#
# Setup Kharon:
my $ahr = Kharon::Protocol::ArrayHash->new(banner => { version => '2.0' } );
my $pes = Kharon::Engine::Server->new(protocols => [$ahr]);
$pes->Connect();
#
# And now ``Run the Object'', exporting the methods inc, query, exception
# and complicated:
$pes->RunObj(object => $obj);