Agent
view release on metacpan or search on metacpan
#!/usr/bin/perl
##
# Agent.pm v3.2
# Patchlevel: 00
# Steve Purkis <spurkis@engsoc.carleton.ca>
# December 15, 1998
##
package Agent;
use strict;
use UNIVERSAL;
use Class::Tom qw( cc repair );
use Data::Dumper; # for argument passing
use Agent::Message; # load message handling routines
use Agent::Transport; # load the autoloader
use vars qw($VERSION $MAJORVERSION $MINORVERSION $MICROVERSION $thread $Debug);
$MAJORVERSION = '3';
$MINORVERSION = '20';
$MICROVERSION = '00'; # aka patchlevel
# I realize with this scheme it's possible to have conflicting version
# numbers, but CPAN doesn't like tuples. Solution, MINOR < 100. If it
# hits 100 it's prolly time for an increase in MAJOR anyway.
$VERSION = "$MAJORVERSION.$MINORVERSION$MICROVERSION";
BEGIN {
# Check for Thread.pm...
eval "use Thread qw( async );";
if ($@) { $Agent::thread = 0; }
else { $Agent::thread = 1; }
}
sub new {
my ($class, %args) = @_;
my $self = {};
my ($stored, $fh, $name, $code, $cpt, $tom, $method) =
delete @args{'Stored', 'File', 'Name', 'Code', 'Compartment'};
# first get the code...
if ($stored) {
if (ref($stored) eq 'ARRAY') { $code = join('', @$stored); }
else { $code = $stored; }
$method = 'repair';
} else {
if ($fh) {
unless ($fh->isa('IO::Handle')) {
warn "File argument was not of IO::Handle!";
return;
}
local $/ = undef;
$code = <$fh>;
} elsif ($name) {
$code = _find_agent($name);
} elsif ($code) {
if (ref($stored) eq 'ARRAY') {
$code = join('', @$code);
}
} else {
my ($pkg, $fl, $ln) = caller();
warn "$fl:$ln passed no valid arguments!";
return;
}
unless (defined($code)) {
warn "agent's source code could not be resolved!";
return;
}
$method = 'cc';
}
# then make the Tom object.
if ($method eq 'repair') {
# use Tom's repair() to produce container:
unless ($tom = repair($code, $cpt)) { # Tom doesn't support this yet
warn "Discarding a corrupted agent!" if $Debug;
return ();
}
} elsif ($method eq 'cc') {
# use Tom's cc() to get container. Note that since we're
# only interested in the first container returned, parens
# are about $tom. Agent does not support multi-class agent
# definitions yet (sorry).
unless (($tom) = cc($code, $cpt)) { # Tom doesn't support this yet
warn "Tom didn't return a container!" if $Debug;
return;
}
}
# now register it:
if ($cpt) { $tom->register(Compartment => $cpt); }
else { $tom->register(); }
if ($@) {
warn "Unsafe agent trapped: $@\n";
return;
}
# and extract the object:
if ($cpt) {
# use $self as a wrapper object...
$self->{Compartment} = $cpt;
# get the object into the safe compartment...
$self->{AgentVar} = $tom->put_object($cpt);
if ($@) {
warn "Unsafe agent trapped: $@\n";
return;
}
unless ($self->{AgentVar}) {
$self->{AgentVar} = '$agent';
my $agentclass = $tom->class;
my $str =
return async { _run($self, %args); };
} else {
print "Threads not available on this system!\n" if $Debug;
}
}
_run($self, %args);
}
sub store {
my $self = shift;
# temporarily remove the Tom container:
my $tom = delete( $self->{Tom} );
# insert the agent & store it:
$tom->insert( $self );
my $stored = $tom->store();
# restore the Tom container:
$self->{Tom} = $tom;
return $stored;
}
sub identity {
my $self = shift;
# temporarily remove the Tom container:
my $tom = delete( $self->{Tom} );
# insert the agent & store it:
$tom->insert( $self );
my $id = $tom->checksum();
# restore the Tom container:
$self->{Tom} = $tom;
return $id;
}
##
# Private subroutines
##
# searches @INC and '.' for "$name" and "$name.pa".
sub _find_agent {
my ($name, @dirs) = @_;
if ($name !~ /.*\.pa$/) { $name .= '.pa'; } # add extension if needed
push (@dirs, '.', @INC); # search local dir & @INC too.
# adapted from Class::Tom::insert:
foreach $_ (@dirs) {
print "Agent: Looking in $_ for $name\n" if $Debug > 1;
if (-e "$_/$name") {
print "Agent: Found $name!\n" if $Debug;
unless ( open(PAFILE, "$_/$name") ) {
warn "Agent: could not open $_/$name!";
return;
}
local $/ = undef;
my $code = <PAFILE>;
close PAFILE;
return $code;
}
}
return;
}
sub _run {
my ($self, %args) = @_;
my $cpt = $self->{Compartment}; # is this a wrapper object?
if ($cpt) {
my $var = $self->{AgentVar}; # get the varname
my $str = $var . '->agent_main(';
if (%args) {
# get something to pass into $cpt
my $d = Dumper(\%args);
my @d = split(/\n/, $d);
shift @d; pop @d;
$str .= join('', @d);
}
$str .= ');';
print STDERR "running $str in Safe\n" if $Debug;
$cpt->reval($str);
if ($@) {
warn "Unsafe agent trapped! $@\n" if $Debug;
# should probably beef this up some
return;
}
} else {
eval { $self->agent_main(%args) };
}
}
##
# Destructor
##
sub DESTROY {
my $self = shift;
print ref($self), " agent being destroyed.\n" if $Debug;
}
1;
__END__
=head1 NAME
Agent - the Transportable Agent Perl module
=head1 SYNOPSIS
use Agent;
my $a = new Agent( Name => 'path_to_agent.pa', %args );
$a->run();
=head1 DESCRIPTION
Agent Perl is meant to be a multi-platform interface for writing and using
transportable perl agents.
=over 4
=item A Perl Agent
Is any chunk of Perl code that can accomplish some user-defined objective
by communicating with other agents, and manipulating any data it obtains.
A Perl Agent consists of a knowledge base (variables), a reasoning
procedure (code), and access to one or more languages coupled with
methods of communication. These languages remain largely undefined, or
rather, user-defined; support for KQML/KIF is under development.
=item Developing An Agent
Note that the developer must devise the reasoning procedure and knowledge
base described above. Agent Perl does not place any restrictions on what
you may do; it only tries to make the 'doing' part easier.
An agent is written as an inheriting sub-class of I<Agent>. Each agent's
class should be stored in a '.pa' file (I<p>erl I<a>gent), and must contain
an C<agent_main()> method. All agents are objects. See the examples for
more details, and learn how Agent.pm works so you won't step on its toes!
=back
=head1 CONVENTIONS
I<Arguments> to subroutines are passed in hashes unless otherwise noted.
Capital-a I<Agent> refers to C<Agent.pm> unless the context is obvious.
Lowercase I<agent> refers to I<an> agent.
=head1 CONSTRUCTOR
=over 4
=item new()
Creates a new agent object. You must tell new() where to get
the agent by passing in I<one> of the following arguments (in a hash):
I<Stored>: The agent stored in a Tom object.
I<File>: An IO::Handle (or any subclass) file handle from which the
agent can be read.
I<Name>: The agent's name. This prompts new to search @INC and './'
for the agent's '.pa' source file.
I<Code>: The agent's source code.
These are listed in order of precedence. To handle security issues,
new() also groks this argument:
I<Compartment>: A Safe Compartment within which the agent will be
registered, and later executed. See the C<Safe> pod for details.
Developers should note that these keywords are I<reserved>. Any additional
arguments are passed to the agent being created.
=back
=head1 METHODS
=over 4
=item store()
Returns the agent object in stringified form, suitable for network
transfer or storage.
( run in 1.392 second using v1.01-cache-2.11-cpan-d80b1682f3f )