eval 'exec perl -x $0 ${1+"$@"}' # -*-perl-*-
  if 0;
#!perl -w
#
# ======================================================================
# This file is Copyright 1998,1999 by the Purdue Research Foundation and
# may only be used under license.  For terms of the license, see the
# file named COPYRIGHT included with this software release.
# AAFID is a trademark of the Purdue Research Foundation.
# All rights reserved.
# ======================================================================
#
# AAFID::Entity package.
#
# AAFID project, COAST Laboratory, CERIAS, 1998-1999.
# 
# Diego Zamboni, Jan 26, 1998.
#
# $Id: Entity.pm,v 1.51 1999/09/06 22:52:17 zamboni Exp $
#
# NOTE: This file is in Perl's POD format. For more information, see the
#       perlpod(1) man page.
#

package AAFID::Entity;

# The following keeps up with the RCS version number. The self-assignment
# keeps the -w switch from complaining (because $VERSION may not be used
# here, but it is used in our base class).
$VERSION = do { my @r = (q$Revision: 1.51 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; $VERSION = $VERSION;

use vars qw(
	    @ISA
	    @EXPORT
	    %PARAMETERS
	    $VERSION
	    $sleeping
	    %_visitedClassPARAMETERS
	   );

use strict;
use Carp;
use AAFID::Common;
use AAFID::Comm;
use AAFID::Message;
use Sys::Hostname;
use Data::Dumper;
use Exporter();
use AAFID::Log;
use IO::File;
use IO::Socket;
use subs qw(init
	    sendConnectMessage
	    sendDisconnectMessage
	    ID
	    run
	    Cleanup
	    processInput
	    sendReport
	    setParameters
	    getParameter
	    getParametersList
	    sendMsgTo
	    );

@ISA=qw(
	Exporter
	AAFID::Log
	AAFID::Config
       );

@EXPORT=qw(_EndOfEntity);

=pod

This is the base class for all the AAFID entities: agents, transceivers
and monitors. All the classes that implement those entities should
be derived from this, because it provides all the common functionality.

=head1 Object creation

As is common practice in Perl, the object itself is represented by an
anonymous hash, which contains all the data pertaining to the object.

The B<AAFID::Entity> class automatically keeps a count of the number
of objects of each entity type (this is, B<Entity> or any derived
class), and assigns a number to each created entity. Also, a
(hopefully) unique identifier is generated for each entity with the
form C<hostname:ClassID:ClassVersion:InstanceNumber>. These are stored
in the object itself in a number of predefined parameters. See the
section "Standard Entity Parameters" for a full description of them.

The constructor is in charge of setting these parameters.

The constructor also sets up a hash called C<%Params> for access to
the parameters by calling the C<setTie> method. See the section
"Parameter manipulation" for details.

The constructor also calls the C<Init> method of the entity. This method
should be overriden by each specific class to provide entity-specific
initialization.

Every entity, when created, has to send a CONNECT message "up", this
is, to the entity that controls it, to let it know that it has
completed its initialization. This is not done at time of
construction, but instead in the C<run> method, just before starting
to run.

B<Important:> subclasses of B<AAFID::Entity> should not override the
constructor.

=cut

%PARAMETERS=(
	     Description	=>	"Generic Entity object",
	    );

sub new {
    my $class=shift;
    # Can also be called as an instance method.
    if (ref($class)) {
      $class=ref($class);
    }
    my $self={};
    my $countername="${class}::_EntityCounter";
    my $number;
    my $numberref;
    {
      no strict 'refs';
      $number=$ {$countername}++;
      $numberref=\$ {$countername};
    }
    $Data::Dumper::Indent=0;
    $Data::Dumper::Terse=1;
    bless $self, $class;
    # Incorporate the default %PARAMETERS hash into the entity
    # parameters.  We do this recursively, by following the
    # inheritance chain from top to bottom. In this way, the hash
    # defined by a subclass overrides the hash defined by a
    # superclass. Also, we add the ClassVersion parameter at the end,
    # because we want to take it from the $VERSION variable,
    # overriding anything else.
    {
      %_visitedClassPARAMETERS=();
      $self->_takePARAMETERS($class);
      no strict 'refs';
      my $vername="${class}::VERSION";
      $self->setParameters(ClassVersion => ($$vername || "0.00"));
    }
    # Now set the parameters specific to this entity.
    $self->setParameters(InstanceNumber => $number,
			 ClassCount     => $numberref,
			 ClassID	=> $class,
			 OpenHandles	=> IO::Select->new(),
			 OpenHandleIDs	=> {},
			 );
    # EntityID is set in a separate call because $self->ID needs
    # the other parameters to be in place.
    $self->setParameters(EntityID => $self->ID);
    # Load configuration information
    $self->configure();
    # Register and activate log categories.
    my $logfile=$self->getParameter('LogFile');
    $self->Log_register("signals", $logfile);
    $self->Log_register("I/O", $logfile);
    $self->Log_register("messages", $logfile);
    $self->Log_register("errors", $logfile);
    $self->Log_register("debug", $logfile);
    $self->Log_register("params", $logfile);
    $self->Log_register("processes", $logfile);
    # Activate some logging categories
    foreach (@{$self->getParameter('LogCategories')}) {
      $self->Log_activate($_);
    }
    unshift @INC, $self->getParametersList('ClassDir', 'AgentsDir');
    # Create class hash %Params tied to the parameters.
    $self->setTie();
    # Set up signal handling
    $self->setupDefaultSignalHandlers;
    # Do local initialization.
    if ($self->_init) {
       # do nothing
    } else {
       return undef;
    }
    # Store initial values for Status and Message.
    $self->_InitStatus();
    return $self;
}

=pod

To incorporate all the C<%PARAMETERS> hashes of the subclasses down to
the current class, we visit the graph defined by the C<@ISA> array
in a depth-first-left-to-right fashion (where left-to-right refers to
the order in which the elements of each C<@ISA> array are visited).
A cache is kept of classes that have been already visited in order
to avoid revisiting them in case they appear more than once in 
the inheritance graph.

=cut

sub _takePARAMETERS {
  my $self=shift;
  my $classname=shift;
  my $class;
  no strict 'refs';
  # First visit all the superclasses.
  foreach $class (@{"${classname}::ISA"}) {
    $self->_takePARAMETERS($class);
  }
  # And now take my own %PARAMETERS hash.
  if (!exists($_visitedClassPARAMETERS{$classname})) {
    $self->setParameters(%{"${classname}::PARAMETERS"});
    $_visitedClassPARAMETERS{$classname}=1;
  }
}

=pod

The B<AAFID::Entity> class provides an empty C<Init> method that functions
only as a place holder. Notice that C<Init> has to return a [[true]] value
for the entity to be instantiated correctly. A [[false]] value is considered
as an indication of failure to initialize.

=cut

sub Init {
    # Put your own initialization code here.
  return 1;
}

sub _init {
  my $self=shift;
  ref($self) or croak "AAFID::_init can only be called as an instance method";
  return $self->Init;
}

=pod

The C<callAtStart> method takes as argument a subroutine reference, and
schedules that subroutine to be called immediately upon startup of the
entity (the first time the event loop is entered), only once.

This is useful to schedule one-time events at the start of the execution,
such as generating an initial STATUS_UPDATE message for agents. This
method must be called from within the C<runtimeInit> method.

=cut

sub callAtStart {
  my $self=shift;
  my $sub=shift;
  Comm::Reactor::add_event(time(), $sub);
}

=head1 Sending a CONNECT message

The CONNECT message is a standard part of the AAFID communication protocol,
and as such the functionality of sending it is automatic, and the format
of the message is determined by the B<Entity> class. The format of the
DATA field for a CONNECT message is as follows:

    ClassID Description

The C<sendConnectMessage> subroutine sends one such message. If no
arguments are given, the message is sent up, and the message subtype
is "CHILD". If an argument is given, it is interpreted as an entity ID
if it is a string, and the message is sent to that entity, if its
handle is registered in our C<OpenHandleIDs> parameter, with a subtype
of "PARENT". If an argument is given but it is a reference, it is considered
to be a file handle reference, and the message is sent directly to that
handle.

=cut

sub sendConnectMessage {
    my $self=checkref(shift);
    my $to=shift;
    my $data=$self->getParameter("ClassID") . " " .
             $self->getParameter("Description");
    my $msg=$self->newMsg(TYPE		=> "CONNECT",
			  SUBTYPE	=> ($to?"PARENT":"CHILD"),
			  DATA		=> $data);
    if ($to) {
      if (!ref($to)) {
	# It is a scalar, use it as ID.
	$self->Log("I/O", 
		   "Sending CONNECT message to $to: ".$msg->toString."\n");
	$self->sendMsgTo($msg, $to)
      }
      else {
	# It is a reference, use it as file handle.
	$self->Log("I/O",
		   "Sending CONNECT message to handle " . Dumper($to) . ": ".
		   $msg->toString . "\n");
	if (AAFID::Comm::isSTDOUT($to) && $self->getParameter('_headLess')) {
	  $self->Log("I/O", "But I'm headless...\n");
	  my $what;
	  if ($self->getParameter('onSTDINclose') &&
	      ($what=$self->getParameter('onSTDINclose')->{onOutput})) {
	    $self->Log("I/O","Calling provided behavior\n");
	    &{$what}($to, $msg->toString);
	  }
	  else {
	    # No behavior provided
	    $self->Log("I/O", 
		       "This message (".$msg->toString.") is being lost\n");
	  }
	}
	else {
	  AAFID::Comm::sendMsgTo($msg, $to);
	}
      }
    }
    else {
      $self->Log("I/O", "Sending CONNECT message up: ".$msg->toString."\n");
      $self->sendReport($msg);
    }
    return $self;
}

=pod

The C<newMsg> subroutine returns a new message with the fields given as
arguments, but it automatically includes the current entity ID in the
FROM field, unless the FROM field is given in the arguments, in which
case it is not overriden.

=cut

sub newMsg {
  my $self=checkref(shift);
  my %fields=@_;
  if (!$fields{FROM}) {
    $fields{FROM}=$self->ID;
  }
  return AAFID::Message->new(%fields);
}

=head1 Responding to a CONNECT message

CONNECT messages are generated in two situations: whenever an entity
starts, a CONNECT CHILD message is sent to its "up" entity, to inform
it that it has started successfully. When an "up" entity starts a new
sub-entity, it sends a CONNECT PARENT message, so that the new entity
identifies who its "up" entity is.

In any case, the entity that receives a CONNECT message has to react
to it by creating a new record for the connecting entity in its 
C<OpenHandleIDs> parameter. 

=cut

sub message_CONNECT {
  my $self=checkref(shift);
  my $msg=checkref(shift, "AAFID::Message");
  my $entityid=$msg->{FROM};
  my $openhandles=$self->getParameter('OpenHandleIDs');
  my $entityrecord=$openhandles->{$entityid};
  my ($entityclass, $entitydesc)=split(/ /, $msg->{DATA}, 2);

  $self->Log("messages", "Got CONNECT message from $entityid\n");
  if ($entityrecord && $entityrecord->{_PendingConnect}) {
    $self->Log("messages", "I was expecting her. Checking consistency.\n");
    if (!defined($entityrecord->{Class}) || 
	($entityrecord->{Class} ne $entityclass)) {
      if ($entityrecord->{Class}) {
	$self->Log("errors", "WARNING: Class mismatch: I had '".
	    $entityrecord->{Class}.", she reports $entityclass. ".
	    "I'm keeping what I had.\n");
      }
      else {
	$self->Log("messages", "Updating Class name to '$entityclass'\n");
	$entityrecord->{Class} = $entityclass;
      }
    }
    if (!defined($entityrecord->{Description}) || 
	($entityrecord->{Description} ne $entitydesc)) {
      if ($entityrecord->{Description}) {
	$self->Log("messages", "She is giving me a new description ".
	    "'$entitydesc'. I had '".$entityrecord->{Description}."'. ".
	    "That's ok, it's her life.\n");
      }
      else {
	$self->Log("messages", "Setting Description to '$entitydesc'\n");
      }
      $entityrecord->{Description} = $entitydesc;
    }
    delete $entityrecord->{_PendingConnect};
    return undef;
  }
  elsif ($entityrecord) {
    $self->Log("errors", "I already have her, and was not expecting a ".
	       "CONNECT. Something is wrong. Doing nothing.\n");
    return undef;
  }
  else {
    $self->Log("messages", "She's new. Adding to the tables\n");
    $entityrecord=($openhandles->{$entityid}={});
    $entityrecord->{Class} = $entityclass;
    $entityrecord->{Description} = $entitydesc;
    # IMPORTANT: In this case, we assume that the same handle the message
    # came from is used to send messages. This may be wrong, but is
    # correct for TCP connections or Unix-domain sockets, which are the
    # planned cases where this block will be executed.
    $entityrecord->{Handle} = $msg->{_FROMHANDLE};
    return undef;
  }
}

=head1 Object identification

The C<ID> method simply returns the identifier of the entity. If it has
not been assigned, it creates and returns one, although it does not enter
it in the parameter table (that is to be done by the constructor).

=cut

sub ID {
  my $self=checkref(shift);
  my $id=$self->getParameter('EntityID');
  if ($id) {
    return $id;
  }
  else {
    my $plainhost;
    my $plainclass=ref($self);
    my $version=$self->getParameter('ClassVersion') || "0.00";
    my $number=$self->getParameter('InstanceNumber') || "00";
    $plainclass =~ s/^AAFID:://;
    ($plainhost=hostname) =~ s/\..*$//;
    return "$plainhost:$plainclass:$version:$number";
  }
}

=pod

The C<breakID> subroutine does the inverse process, breaking a provided
ID (or our own if no arguments are given) into its component parts, and
returning it as a hash reference. It does not necessarily have to be called
as an instance method, so it can be called by other programs.

=cut

sub breakID {
  my $self;
  my $id;
  # If first argument is a ref, assume we are being called as instance method.
  if (ref($_[0])) {
    $self=checkref(shift);
    $id=shift || $self->ID;
  }
  else {
    $id=shift || "";
  }
  if ($id =~ /^([^:]+):([^:]+(?:::[^:]+)*):([^:]+):(\d+)/) {
    return {Host 	=> $1,
	    Class 	=> $2,
	    Version	=> $3,
	    Number	=> $4
	   };
  }
  else {
    return undef;
  }
}

=head1 Running and stopping entities

All entities in the AAFID system are independent-running entities. Each
one of them is a separate process in the current implementation, but they
may be separate threads, or any other kind of running object.

Thus, after an entity object is created, we need a mechanism for making
it run, which should make the object start doing whatever it needs to do.
After this, we need a way of stopping (and possibly restarting) the 
entity at will. This will be necessary, for example, to allow a transceiver
to control several independent agents running under its control.

To this end, we have to establish some conventions. The ones we will use
for now are the following:

=over 4

=item *

Every entity must have an entry point routine called C<run()> which takes
no arguments (except the mandatory reference to itself that all instance
methods take in Perl) and starts the execution of the entity.

This method calls C<runtimeInit>, which can be overriden to provide
run-time initialization of the entity. Most of the time, it will not be
necessary to override C<run> itself, because C<runtimeInit> can do
all the necessary initialization for the subclasses. C<runtimeInit> can
abort the execution of the entity if an error occurs by returning a
false value (such as C<undef>).

If C<run> has to be overriden for some reason, it is important to remember
the calls to C<sendConnectMessage>, C<connectFilters> and 
C<setupSTDINcallback>. The C<run> method has to finish with a call to
C<$self-<gt>eventLoop>, which actually handles the execution of the
entity.

=cut

sub run {
    my $self=shift;
    my $class=ref($self);
    croak "AAFID::Entity: run() can only be called as an instance method"
	unless $class;
    # Do runtime initialization
    unless ($self->runtimeInit) {
      $self->Log("errors", "Error in run time initialization\n");
      return;
    }
    # Connect to the filters I requested, if any
    $self->connectFilters;
    # Set up the standard STDIN callback.
    $self->setupSTDINcallback;
    # After everything is ready, send a CONNECT message
    $self->sendConnectMessage;
    # Template run() method, which does nothing.
    $self->eventLoop();
}

=pod

The C<runtimeInit> subroutine allows entity writers to perform initialization
activites that have to be performed at runtime without having to override
C<run> itself.

=cut

sub runtimeInit {
  my $self=checkref(shift);
  # Insert your code here.
  return $self;
}

=pod

The C<run> method is also responsible for setting up the callbacks
that will be executed whenever a message is received. Initially, this
includes only responding to messages from standard input. This
subroutine has to be overriden by subclasses of Entity.

=cut

sub setupSTDINcallback {
  my $self=checkref(shift);
  if ($self->getParameter('_standalone')) {
    $self->Log("debug", "Running standalone, using an acceptor for STDIN.\n");
    # If we are running standalone, the message on STDIN come
    # from a human, therefore they are not in Reactor message
    # format, so we handle them through an acceptor.
    $self->Log("debug", "Adding acceptor for handle ".Comm::Reactor::stdin()." (STDIN)\n");
    Comm::Reactor::add_acceptor(Comm::Reactor::stdin(),
	      sub {
		my $fh=shift;
		my $msg=<$fh>;
		chomp $msg if $msg;
		$self->processInput($fh, AAFID::Comm::nextSTDINmsg($fh, $msg));
	      });
  }
  else {
    $self->Log("debug", "Not standalone, using a callback for STDIN.\n");
    # If not running standalone, the messages on STDIN come from
    # another entity, therefore we expect them to be in Reactor
    # format and handle them through a regular callback.
    $self->Log("debug", "Adding callback for handle ".Comm::Reactor::stdin()." (STDIN)\n");
    Comm::Reactor::add_handle(Comm::Reactor::stdin(),
	    sub {
	      my ($fh, $msg)=@_;
	      $self->processInput($fh, AAFID::Comm::nextSTDINmsg($fh, $msg));
	    });
  }
}

=pod

  After everything is ready, the C<eventLoop> method actually executes
  the entity.

=cut

sub eventLoop {
  my $self=checkref(shift);
  Comm::Reactor::loop();
}

=item *

Every entity must also be able to run as an independent program. For this,
we provide a hackish method called C<_EndOfEntity>, which should be the last
thing in the definition of an AAFID entity. This method checks whether the
file is being loaded from another program or being run as a standalone
program. If it is running standalone, it creates an instance of the current
class, and invokes the C<run> method. If not, it simply returns the value 1,
the customary value that has to be returned by Perl packages.

If the entity is running standalone, C<_EndOfEntity> also creates a
"wrapper process" that pipes its inputs from STDIN to the entity, and
prints the output of the entity to STDOUT. This is done so that the
user can interact with the entity by typing commands without having
to worry about the special format used for messages.

=cut

sub _EndOfEntity {
  my @c0=caller(0);
  my @c1=caller(1);
  
  if(@c1) {  # Being called from another program
    return 1;
  }
  else {     # Running standalone.
    my $obj=$c0[0]->new
      or die "Error creating entity, constructor returned undef\n";
    # Set the _standalone parameter so that messages from STDIN
    # are processed appropriately.
    $obj->setParameter(_standalone => 1);
    $obj->run;
    return 1;
  }
}

=item *

Entities should respond to several signals as follows.

=over 4

=item USR1

Means "pause temporarily; do nothing if you are already paused".

=item USR2

Means "restart running; do nothing if you are already running".

=item TERM

Means "terminate gracefully".

=item HUP

Means "restart yourself".

=back 4

B<AAFID::Entity> provides very simple signal handlers for these purposes.
For B<USR1>, it simply sleeps the process until some other signal happens.
For B<HUP> it calls the C<run> method again, and for B<TERM> it simply
exits. These sample signal handlers are installed by the default C<run> 
method, so any descendant classes should be careful to set them appropriately.
The method for setting the default signal handlers is called
C<setupDefaultSignalHandlers>

=cut

sub DefaultHandler_USR1 {
    my $self=checkref(shift);
    # We set the handlers globally so that it will react correctly
    # even if we are in some other signal handler that was called while
    # we were sleeping.
    $SIG{USR1}='IGNORE';
    $SIG{USR2} = sub { $self->DefaultHandler_USR2; };
    sleep(0);
    $self->setParameter(_sleeping => 1);
    my $sleeping=$self->getParameterRef('_sleeping');
    while($$sleeping) {
	$self->Log("signals", "Got signal USR1. Sleeping...\n");
	sleep;
    }
    $SIG{USR2} = 'IGNORE';
}

sub DefaultHandler_USR2 {
  local $SIG{USR2}='IGNORE';
  my $self=checkref(shift);
  $self->setParameter(_sleeping => 0);
  $self->Log("signals", "Got signal USR2. Restarting...\n");
  return;
}

sub DefaultHandler_TERM {
  my $self=checkref(shift);
  $self->Log("signals", "Got TERM signal. Good bye.\n");
  $self->_cleanup("Got TERM signal");
  exit(0);
}

sub DefaultHandler_HUP {
  my $self=checkref(shift);
  $self->Log("signals", "Got HUP signal. Restarting.\n");
  $self->setParameter(_sleeping => 0);
  $self->_restart;
}

sub setupDefaultSignalHandlers {
  my $self=checkref(shift);
  $self->Log("signals", "Installing signal handlers\n");
  $SIG{USR1} = sub { $self->DefaultHandler_USR1; };
  # USR2 is only useful after USR1, therefore it is normally ignored,
  # except when we are inside the handler for USR1.
  $SIG{USR2} = 'IGNORE';
  $SIG{TERM} = sub { $self->DefaultHandler_TERM; };
  $SIG{HUP}  = sub { $self->DefaultHandler_HUP; };
  # This is a hack--- need to correct --- TODO
  # These handlers are to prevent the program under Linux to generate
  # a lot of weird warnings and errors, but their real cause needs to be
  # found and corrected.
#  $SIG{__WARN__} = sub { open F, ">/tmp/error.warn"; print F $_[0]; close F;};
#  $SIG{__DIE__} = sub { open F, ">/tmp/error.die"; print F $_[0]; close F; exit(0); };
#  $SIG{__WARN__} = sub { return; };
#  $SIG{__DIE__} = sub { return; };
}

=pod

The C<_restart> method is used for restarting the object to its initial
condition. This might have to be improved.

=cut

sub _restart {
  my $self=checkref(shift);
  my $key;
  $self->Log("debug", "Restarting...\n");
  # Destroy everything in the current me.
  foreach $key (keys %$self) {
    # Log statements here disabled because Log uses some of the parameters
    # that are being undefined.
#    $self->Log("debug", "Destroying parameter '$key'\n");
    undef $self->{$key};
  }
#  $self->Log("debug", "Destroying myself and creating a new one\n");
  # As if we were just run.
  $self=$self->new;
  $self->Log("debug", "Here I am, the new me. Restarting.\n");
  $self->run;
}

=back 4

=head1 Terminating execution

At some point, the entity may need to finish its execution. If this is
done through the normal mechanisms, the C<Cleanup> routine will be
called on the object. This routine should do any necessary actions for
a graceful shutdown, and should be appropriately overriden by the
entity writer. It will be automatically passed a string (possibly empty)
that contains a description of the reason for the shutdown.

Internally, the C<_cleanup> subroutine is called, which calls the
user-provided C<Cleanup> subroutine, and then automatically generates
a DISCONNECT message and sends it both up and to any other entities
with which it is communicating.

The sending of the DISCONNECT message is actually done by a separate
subroutine C<sendDisconnectMessage>, so that it can be used by
subclass implementers. It takes an optional argument that gives a
description for the termination.

By default, the message is sent up with subtype of "CHILD". If a
second argument is given, it is interpreted as an entity ID, and the
DISCONNECT message is sent to that entity with a subtype of "PARENT".

=cut

sub sendDisconnectMessage {
  my $self=checkref(shift);
  my $desc=shift;
  my $to=shift;
  my $msg=$self->newMsg(TYPE		=> "DISCONNECT",
			SUBTYPE		=> ($to?"PARENT":"CHILD"),
			DATA		=> $desc);
  if ($to) {
    $self->Log("I/O", "Sending DISCONNECT message to $to: ".$msg->toString."\n");
    $self->sendMsgTo($msg, $to);
    # Make it write it immediately.
    Comm::Reactor::_write($to);
  }
  else {
    $self->Log("I/O", "Sending DISCONNECT message up: ".$msg->toString."\n");
    $self->sendReport($msg);
    unless ($self->getParameter('_standalone')) {
      # Make it write it immediately.
      Comm::Reactor::_write(Comm::Reactor::stdout());
    }
  }
}

sub Cleanup {
  my $self=checkref(shift);
  my $desc=shift;
  # Put your cleanup code here
}

sub _cleanup {
    my $self=checkref(shift);
    my $desc=shift;

    $self->Cleanup($desc);
    $self->sendDisconnectMessage($desc);
    $self->Log("messages", "Terminating: $desc\n");
    exit(0);
}

=head1 Interpretation of messages and execution of commands

AAFID entities have the ability to receive messages and perform actions
based on them. Messages may be received either from "above" (this is,
from the controller entity) or, for controller entities (for example,
Transceivers and Monitors), from the sub-entities that are running at
any specific moment.

The general format of a message is as follows (as defined by the
B<AAFID::Message> class):

  <message type> <message subtype> <from> <to> <time> <data>

where the message types and subtypes that are recognized determine how
the data field is interpreted, and what actions are to be taken.

To implement the actions to be taken in response to different messages,
we define a set of methods called C<message_YYY>, where YYY is the 
message type to which that method will respond. When a message of type YYY
is received, that method will be invoked with one argument: an 
B<AAFID::Message> object containing the message just received. The
C<message_YYY> method must be defined with the message type part (the YYY)
in uppercase.

If the message method returns something, it must be an B<AAFID::Message>
object, which will be sent back to whoever sent the original message.

If the requested message method does not exist, the default method 
C<message_NoSuchMessageType> will be called, whose default action is to
produce an ERROR message.

One special case is the COMMAND message type, which indicates specific
actions to be taken by the receiving entity. A COMMAND message has
the following format:

  COMMAND <command name> <from> <to> <time> <parameters>

where B<<parameters>> is of the form I<key => value[,...]>, in the
traditional Perl hash syntax. The specific parameters that have to be
provided depend on the specific command being invoked.

Any Entity subclass can define its own set of commands, depending on
the functionality it needs. To define a new command, the class writer
simply has to define a method called C<command_XXX>, where XXX is the
name of the command to define. When the command is invoked, the subroutine
will be invoked with two arguments: a message object containing the
parsed message, and a hash (possibly empty) containing the parameters 
that were provided. The C<command_XXX> method must be defined with the
command name (the XXX part) in uppercase.

A command subroutine should normally not return anything (or, more
precisely, return C<undef>). If it returns something, it must be a
hash B<reference> containing parameter/value pairs, which will be sent
back to whoever sent the original command in a message of the
following format:

  COMMAND RESULT <from> <to> <time> Command=><command name>[, <values>]

where B<<command name>> will contain the command that triggered the 
response, and B<<values>> will be the hash returned by the command
method.

If a requested command is not found in the class, the method 
C<command_NoSuchCommand> will be invoked. The default implementation
provided by B<AAFID::Entity> simply ignores the command.

The B<AAFID::Entity> class provides a basic set of commands:

=over 4

=item STOP

Stop the entity by calling its C<Cleanup> method and exiting. It can
also be invoked as a message type. Automatically causes the sending
of a DISCONNECT message, both up and to any other entities that may
be registered in the C<OpenHandleIDs> parameter.

=item EVAL C<Code> => I<code>

The C<Code> parameters should contain valid Perl code which will be
executed in the context of the receiving entity. This is mostly for
experimentation purposes.

=item SET_PARAMS Param => I<value> ...

The parameters specify a set of parameter/value pairs that will be set
in the entity using the C<setParameters> method.

=item GET_PARAMS Params => "name[, name ...]"

Returns a COMMAND RESULT message containing the requested parameters
and their values in C<Name =E<gt> Value> form.

=item DUMP_YOURSELF

Return a string representation of the object, as produced by the standard
B<Data::Dumper> package. Internally, an entity is represented as a hash
that contains the parameters of the entity.

=item RESTART

Destroys the current entity and starts a new one in its place. The new
entity behaves as it it has been just executed, so it sends a new
CONNECT message up.

=back 4

The C<processInput> subroutine is the general one that waits for input
on a set of input handles, and processes any messages that come on any
of them by reacting correspondingly.

If no parameters are passed to it, C<processInput> does no blocking,
and only reads and processes the next available message, if any. If a
numeric parameter is passed, it will be interpreted as a timeout, in
seconds, to wait for a message to become available before
returning. Finally, if C<-1> is passed as a timeout, the call will
block.

C<processInput> automatically adds C<STDIN> to the Entity
C<OpenHandles> and C<OpenHandleIDs> parameters (the adding to
C<OpenHandles> is actually done by C<AAFID::Comm::nextMsg>), so those
parameters must be initialized empty.

If the entity defines the subroutines C<PreMessageHook> and 
C<PostMessageHook>, they will be called for all messages destined
for us before the message is processed, and afterwards. C<PreMessageHook>
will be called with the message as an argument, and C<PostMessageHook>
will be called with the message and the result from the subroutine
that processed it as arguments.

=cut

sub processInput {
  my $self=checkref(shift);
  my ($fh, $msg)=@_;

  # Nothing, return
  return undef if !$msg;
  if($msg==-1) {
    # If got a close on STDIN, act accordingly
    my $isUp;
#    print STDERR "$$: Entering file-close section fh=$fh, msg='$msg'\n";
    my $ohids=$self->getParameter('OpenHandleIDs');
    $self->Log("debug", "fh=".Dumper($fh).", ohids=".Dumper($ohids).", keys ohids=".join(" ",keys(%$ohids))."\n");
    $isUp=grep { exists($ohids->{$_}) && 
		 ($ohids->{$_}->{Handle}||0)==($fh||0) && 
		 $ohids->{$_}->{_isUp} } keys(%$ohids);
    $self->Log("debug", "isUp=$isUp\n");
    if ($fh==Comm::Reactor::stdin() || fileno($fh)==fileno(STDIN) || $isUp) {
      if (!defined($self->getParameter('runHeadless'))) {
	$self->Log("I/O", "Got close on STDIN, exiting\n");
	$self->_cleanup("Close on STDIN");
      }
      else {
	$self->Log("I/O", "Got close on STDIN but runHeadless is defined.\n");
	my $onstdinclose=$self->getParameter('onSTDINclose');
	my $what;
	# Set the flag to indicate that we are running headless.
	$self->setParameter(_headLess => 1);
	# Whatever happens, we can no longer consider ourselves running
	# standalone. Store the value of the parameter for later.
	my $was_standalone=$self->getParameter('_standalone');
	$self->setParameter(_standalone => undef);
	if ($onstdinclose) {
	  $what=$onstdinclose->{onClose};
	}
	if ($what) {
	  # onSTDINclose->onClose is defined, call that subroutine.
	  $self->Log("I/O", 
		     "Calling routine provided in onSTDINclose->onClose\n");
	  &{$what}($fh);
	}
	else {
	  # Standard behavior: do nothing, just log it.
	  $self->Log("I/O", "I'm doing nothing special for now.\n");
	}
	# Remove from OpenHandles param
	$self->getParameter('OpenHandles')->remove($fh);
	# In any case, remove from the OpenHandleIDs table any entries
	# whose Handle is STDOUT.
	$self->Log("I/O", "Updating OpenHandleIDs table.\n");
	my $k;
	for $k (keys %{$ohids}) {
	  if (defined($ohids->{$k}->{Handle}) && 
	      AAFID::Comm::isSTDOUT($ohids->{$k}->{Handle}) ||
	      $ohids->{$k}->{Handle}==$fh) {
	    $self->Log("debug", 
		       "Entity '$k' is associated to STDOUT, removing.\n");
	    # Remove from reactor only if it is not $fh, because $fh
	    # was removed automatically by Comm::nextSTDINMsg
	    if ($ohids->{$k}->{Handle}!=$fh) {
	      Comm::Reactor::remove_handle($ohids->{$k}->{Handle});
	    }
	    delete $ohids->{$k}->{Handle};
	    delete $ohids->{$k};
	  }
	}
	# If I'm standalone (which means that I'm running from a terminal)
	# detach from it.
	my $pid;
	if ($was_standalone) {
	  if (defined($pid=fork)) {
	    exit(0) if $pid;
	  }
	  else {
	    die "Error in fork: $!\n";
	  }
	}
      }
      return undef;
    }
    else {
      #### TODO: Implement correctly what is described in this comment
      # For now this only works with bidirectional handles, because what
      # is stored in the Handle field is the handle used to write _to_
      # the entity, and we are comparing against the handle from which
      # we got the message.
      #
      # If we get a close on any other handle, do some cleanup and check
      # if the entity had already disconnected. If not, fake a DISCONNECT
      # message to myself so that things finish correctly.
      $self->Log("I/O", "Got close on handle: ".Dumper($fh)."\n");
      my $openhandles=$self->getParameter('OpenHandleIDs');
      my $selectobject=$self->getParameter('OpenHandles');
      $selectobject->remove($fh);
      my $who;
      foreach $who (keys %{$openhandles}) {
	$self->Log("debug", "Checking $who on OpenHandleIDs table...\n");
	$fh=$fh||0;
	if (defined($openhandles) && defined($openhandles->{$who}) && 
	    defined($openhandles->{$who}->{Handle}) &&
	    (($openhandles->{$who}->{Handle}==$fh) ||
	     (fileno($openhandles->{$who}->{Handle})==fileno($fh)))) {
	  $self->Log("I/O", "The handle corresponds to entity $who, who had not disconnected properly. Faking a DISCONNECT message to myself.\n");
	  my $fmsg=AAFID::Message->new(TYPE => 'DISCONNECT',
				       SUBTYPE => 'CHILD',
				       FROM => $who,
				       TO => $self->ID,
				       DATA => 'Unexpected channel close');
	  $self->processInput($fh, $fmsg);
	  # Don't remove from reactor because Comm::nextMsg removed
	  # it automatically.
	  #Comm::Reactor::remove_handle($ohids->{$who}->{Handle});
	  delete $openhandles->{$who}->{Handle};
	  delete $openhandles->{$who};
	}
      }
      return undef;
    }
  }
  # If we get here, the message was not a channel close
  if($msg && $msg->{_FROMSTDIN} && $msg->{FROM}) {
    my $oh=$self->getParameter('OpenHandleIDs');
    if(!defined($oh->{$msg->{FROM}})) {
      $self->Log("I/O", "Creating record for ".$msg->{FROM} .
		 " in OpenHandleIDs\n");
      $oh->{$msg->{FROM}} = {};
      $oh->{$msg->{FROM}}->{_PendingConnect} = 1;
      $oh->{$msg->{FROM}}->{_isUp} = 1;
      # We got a connection from STDIN, so if we were running headless,
      # we are no longer. Also, if I was running standalone, I am no
      # longer.
      if ($self->getParameter('_headLess')) {
	$self->setParameter(_headLess => undef);
	$self->setParameter(_standalone => undef);
      }
    }
    if (!defined($oh->{$msg->{FROM}}->{Handle})) {
      $self->Log("I/O", "Associating ".$msg->{FROM}." to STDOUT.\n");
      $oh->{$msg->{FROM}}->{Handle}=Comm::Reactor::stdout;
      if (!defined($oh->{$msg->{FROM}}->{Handle})) {
	$self->Log("errors", "Could not associate handle to STDOUT: $!\n");
	delete $oh->{$msg->{FROM}};
	return undef;
      }
      else {
	$oh->{$msg->{FROM}}->{Handle}->autoflush(1);
	$oh->{$msg->{FROM}}->{_isUp} = 1;
	# If I was running headless, no more. Also, if I was running
	# standalone, after being headless, I am no more.
	if ($self->getParameter('_headLess')) {
	  $self->setParameter(_headLess => undef);
	  $self->setParameter(_standalone => undef);
	}
      }
    }
  }
  if($msg) {
    my $msgtype=uc($msg->{TYPE});
    my $result;
    if ($self->isForMe($msg)) {
      $self->Log("debug", 
		 "Got a $msgtype message, trying to call message_$msgtype\n");
      if ($self->can("PreMessageHook")) {
	$self->PreMessageHook($msg);
      }
      my $msgsub=$self->can("message_$msgtype");
      if(!$msgsub) {
	$result=$self->message_NoSuchMessageType($msg);
      }
      else {
	$result=&{$msgsub}($self, $msg);
      }
      if ($self->can("PostMessageHook")) {
	$self->PostMessageHook($msg, $result);
      }
      if ($result) {
	if(ref($result) ne "AAFID::Message") {
	  croak "Message_${msgtype} generated a non-Message result!";
	}
	$self->sendMsgTo($result, $msg->{FROM});
      }
    }
    else {
      $self->relayMessage($msg);
    }
  }
  return $msg;
}

sub relayMessage {
  my $self=checkref(shift);
  my $msg=checkref(shift, "AAFID::Message");
  my $msgto=$msg->{TO};
  my $msgtype=uc($msg->{TYPE});
  $self->Log("debug",
	     "Got a $msgtype message, but not for me, it's for $msgto\n");
  $self->sendMsgTo($msg, $msgto);
}

# Determine if the message is for me. A TO field of "-" acts as a wildcard.

sub isForMe {
  my $self=checkref(shift);
  my $msg=checkref(shift, "AAFID::Message");
  my $msgto=$msg->{TO};
  my $oh=$self->getParameter('OpenHandleIDs');
  if ( ($msgto ne "-") && 
       (lc($msgto) ne lc($self->ID)) && 
       !exists $oh->{$msgto}) {
    $self->Log("debug", "The message is for $msgto, but I don't know her, " .
                 "so I'll take it.\n");
  }
  return ( ( lc($msgto) eq lc($self->ID) ) or
	   ( $msgto eq "-" ) or
	   ( !exists $self->getParameter('OpenHandleIDs')->{$msgto}) );
}

# Definition of subroutines for message types.

# Default method if unknown message.
sub message_NoSuchMessageType {
    my $self=checkref(shift);
    my $msg=checkref(shift, "AAFID::Message");
    $self->Log("messages", "Undefined message type: ".$msg->toString."\n");
    # Do nothing.
    return undef;
}

sub message_COMMAND {
    my $self=shift;
    ref($self) or croak "message_COMMAND can only be called as an instance method";

    my $msg=shift;
    # Get the command name
    my $cmdname=uc($msg->{SUBTYPE});
    $self->Log("messages", "Got a $cmdname command\n");
    # Get the parameters
    my %paramhash=();
    my $paramstring=$msg->{DATA};

    if($paramstring) {
	$self->Log("debug", "Evaluating %paramhash=($ {paramstring})\n");
	eval "%paramhash=($ {paramstring})";
	if($@) {
	    chomp $@;
	    $self->Log("messages", "Error evaluating command parameters: $@\n");
	    my $nmsg=$self->newMsg(TYPE	=> "COMMAND",
				   SUBTYPE=> "ERROR",
				   DATA	=> "ParamError => $@");
	    $self->sendMsgTo($nmsg, $msg->{FROM});
	    return;
	}
    }
    # Execute the command
    my $result;
    $self->Log("messages", "Trying to call command_$cmdname\n");
    my $cmdsub=$self->can("command_$cmdname");
    if(!$cmdsub) {
	chomp $@;
	$self->Log("debug", 
	       "command_$cmdname not found, calling command_NoSuchCommand\n");
	$result=$self->command_NoSuchCommand($msg, %paramhash);
    }
    else {
	$result=&{$cmdsub}($self, $msg, %paramhash);
    }
    if ($result) {
	$self->Log("debug", "command_${cmdname} produced a result: ".Dumper($result)."\n");
	# TODO: How to detect if %result is indeed a hash?
	$$result{Command}=$cmdname;
	my $datadump=Dumper($result);
	$datadump =~ s/^\s*\{(.*)\}\s*/$1/;
	my $nmsg=$self->newMsg(TYPE	=> "COMMAND",
			       SUBTYPE	=> "RESULT",
			       DATA	=> $datadump);
	# TODO: make this send adequately to whoever sent the command
	$self->Log("debug", "Sending RESULT message to $msg->{FROM}: ".$msg->toString."\n");
	$self->sendMsgTo($nmsg, $msg->{FROM});
    }
    return undef;
}

sub command_NoSuchCommand {
    my $self=shift;
    my $msg=shift;
    $self->Log("messages", "Command " . $msg->{SUBTYPE} . " not defined: "
	. $msg->toString."\n");
    # Do nothing here
    return undef;
}

sub message_STOP {
  my $self=checkref(shift);
  $self->command_STOP(@_);
}

# Definition of soubroutines for different commands.

sub command_STOP {
    my $self=shift;
    ref($self) or croak "command_STOP can only be called as an instance method";
    # Don't even look at the arguments.
    $self->_cleanup("STOP command or message");
    exit(0);
}

sub command_EVAL {
    my $self=checkref(shift);
    # For now, blindly eval this.
    my ($msg, %params)=@_;
    my $result;
    $self->Log("debug", "Eval'ing ".$params{Code}."\n");
    $result=eval $params{Code};
    if($@) {
	chomp $@;
	$self->Log("debug", "Error in eval: $@\n");
	return {ErrorMessage=>$@};
    }
    if (!defined($result)) {
      return undef;
    }
    else {
      # Return result produced by the code.
      $Data::Dumper::Indent=0;
      $Data::Dumper::Terse=1;
      my $datadump=Dumper(\$result);
      return { EvalResult => $datadump };
    }
}

sub command_SET_PARAMS {
    my $self=checkref(shift);
    my ($msg, %params)=@_;
    $self->setParameters(%params);
    return undef;
}

sub command_GET_PARAMS {
  my $self=checkref(shift);
  my ($msg, %params)=@_;
  if ($params{Params}) {
    my @params=split(/[,\s]+/, $params{Params});
    my %paramvalues=$self->getParameters(@params);
    return \%paramvalues;
  }
}

# This may be useful for debugging

sub command_DUMP_YOURSELF {
  my $self=checkref(shift);
  my $result=Dumper($self);
  return {Me => $result};
}

=pod

The RESTART is implemented as a command only (not a message) so that
subentities will be able to reimplement it if they want.

=cut

sub command_RESTART {
  my $self=checkref(shift);
  $self->_restart;
}

=head1 Sending message "up"

The Entity class includes this function simply to make it easier for
Entity writers to send messages "up" (this is, to the parent entity).
This is simply a gateway to the corresponding method in
B<AAFID::Comm>.

=cut

sub sendReport {
  my $self=shift;
  ref($self) or croak "AAFID::Entity::sendReport can only be called as an instance method";
  my $msg;
  foreach $msg (@_) {
    if(ref($msg) ne "AAFID::Message") {
      $self->Log("errors", "sendReport: got a non-Message argument: $msg");
      next;
    }
    else {
      $self->Log("debug", "Sending message up: ".$msg->toString."\n");
      if ($self->getParameter('_headLess')) {
	$self->Log("I/O", "Trying to send message up while headless...\n");
	my $what;
	if ($self->getParameter('onSTDINclose') &&
	    ($what=$self->getParameter('onSTDINclose')->{onOutput})) {
	  $self->Log("I/O", "Behavior provided, calling subroutine.\n");
	  &{$what}(Comm::Reactor::stdout(), $msg->toString);
	}
	else {
	  # No behavior provided.
	  $self->Log("I/O",
		     "This message (".$msg->toString.") is being lost\n");
	}
	return;
      }
      else {
	if ($self->getParameter('_standalone')) {
	  # Hidden hack to make it print the message as is (no Reactor
	  # message format) when we are running standalone.
	  AAFID::Comm::sendMsgUp($msg, 1);
	}
	else {
	  AAFID::Comm::sendMsgUp($msg);
	}
      }
    }
  }
}

=head1 Sending a message to an arbitrary entity.

This subroutine takes as arguments a message and an entity identifier. It
looks up the handle associated with that identifier, and send the message
to it.

=cut 

sub sendMsgTo {
  my $self=shift;
  ref($self) or croak "sendMsgTo can only be called as an instance method";
  my $msg=shift;
  my $whoID=shift;
  my $oh=$self->getParameter('OpenHandleIDs');
  my $h;
  if ($oh->{$whoID} && ($h=$oh->{$whoID}->{Handle})) {
    $self->Log("debug", "Sending message to $whoID (handle ".Dumper($h)."): ".$msg->toString."\n");
    if (AAFID::Comm::isSTDOUT($h) && $self->getParameter('_headLess')) {
      $self->Log("I/O", "Trying to send a message while headless.\n");
      my $what;
      if ($self->getParameter('onSTDINclose') &&
	  ($what=$self->getParameter('onSTDINclose')->{onOutput})) {
	$self->Log("I/O", "Calling provided routine\n");
	&{$what}($h, $msg->toString);
      }
      else {
	# No behavior provided.
	$self->Log("I/O", "This message (".$msg->toString.") is being lost\n");
      }
      return;
    }
    else {
      if ($self->getParameter('_standalone')) {
	AAFID::Comm::sendMsgTo($msg, $h, 1);
      }
      else {
	AAFID::Comm::sendMsgTo($msg, $h);
      }
    }
  }
  else {
    # Recognize "-" as a special case for "up"
    if ($whoID eq "-") {
      $self->sendReport($msg);
    }
    else {
      $self->Log("errors", "Invalid ID: $whoID\n");
    }
  }
}

=head1 Filter support

If an entity requests filters through its C<FiltersNeeded> parameter,
the controller entity that starts it will take care of starting the
filters before running the entity, and will put the information necessary
to contact each filter in the C<FilterPaths> parameter, with each key
representing the filter name and each element containing the path of
the Unix domain socket through which the filter can be contacted.

Once the entity starts, it has to contact the filters, and needs a way
of reading data from the filters and of telling the filter the pattern
it wants to match for the data it receives.

The C<connectFilters> subroutine opens sockets to all the filters
given in C<FilterPaths>, and sends to each one of them the initial
pattern as given in C<FiltersNeeded>.

=cut

sub connectFilters {
  my $self=checkref(shift);
  my $fp=$self->getParameter('FilterPaths', {});
  my $f=$self->getParameter('FilterSockets', {});
  my $fn=$self->getParameter('FiltersNeeded', {});
  my $fname;
  my $cbname;
  $self->Log("I/O", "Contacting filters\n");
  foreach $fname (keys %$fp) {
    my $fpath=$fp->{$fname};
    my $sock=$self->newFilterClientSocket($fpath);
    if ($sock) {
      $self->Log("I/O", "Opened socket $fpath to filter $fname.\n");
      $f->{$fname}=$sock;
      $self->sendConnectMessage($sock);
      $self->setFilterPattern($fname, 
			      $ {$fn->{$fname}}[0]);
      # See if a specific callback was requested. If not, set the
      # generic callback.
      if (defined($cbname=$ {$fn->{$fname}}[1])) {
	# Having this as an instance method allows each subclass to
	# override it if they want to set the callback in a different
	# way (for example in an agent, the callback has to be 
	# wrapped around code that checks for status changes).
	$self->_setFilterCallback($sock, $fname, $cbname);
      }
      else {
	$self->Log("debug", "Setting generic callback for filter $fname.\n");
	Comm::Reactor::add_handle($sock, 
				  sub { $self->storeFilterMsg($fname, @_); });
      }
    }
    else {
      $self->Log("errors", 
		 "Error opening socket $fpath to filter $fname: $!\n");
    }
  }
}

sub _setFilterCallback {
  my ($self, $sock, $fname, $cbname)=@_;
  my $cb=eval "sub { \$self->$cbname('$fname', \@_); }";

  if ($@) {
    $self->Log("errors", 
	       "Error defining callback for filter $fname: $@\n");
    return undef;
  }
  $self->Log("debug", "Setting callback $cbname for filter $fname.\n");
  Comm::Reactor::add_handle($sock, $cb);
  return 1;
}

sub newFilterClientSocket {
  my $self=checkref(shift);
  my $addr_sock=shift;
  if (!$addr_sock) {
    $!="No socket path given";
    return undef;
  }
  my $sock=IO::Socket::UNIX->new(Type           => SOCK_STREAM,
                                 Peer           => $addr_sock,
				);
  $sock->autoflush(1) if $sock;
  return $sock;
}

=pod

The C<setFilterPattern> subroutine takes as argument a filter name and a
hash containing the pattern that needs to be set for that filter, and 
sends the appropriate message.

=cut

sub setFilterPattern {
  my $self=checkref(shift);
  my $fname=shift;
  my $patref=shift || {};
  my $pat=hashToStr($patref);
  if (!$fname) {
    $self->Log("errors", "setFilterPattern: no filter name given.\n");
    return;
  }
  else {
    my $socks=$self->getParameter('FilterSockets', {});
    if ($socks->{$fname}) {
      $self->Log("debug", "Sending pattern ($pat) to filter $fname.\n");
      my $msg=$self->newMsg(TYPE    => 'COMMAND',
			    SUBTYPE => 'SETPATTERN',
			    DATA    => $pat
			   );
      AAFID::Comm::sendMsgTo($msg, $socks->{$fname});
    }
  }
}

=pod

To read from a filter, the subroutine C<readFilter> is used. It gets
as argument the name of the filter, and returns the next line of input
from the filter, if one is available. If there are no lines available
or an error occurs, it returns C<undef>. If an error occurs, it also
sets the C<$!> variable.

The C<readFilter> subroutine makes use of the C<FilterMessages> parameter,
which is maintained by the C<storeFilterMsg> subroutine.

=cut

sub storeFilterMsg {
  my $self=checkref(shift);
  my ($fname, $fh, $msg)=@_;
  $self->Log("debug", "Got message '$msg' from filter $fname.\n");
  my $fm=$self->getParameter('FilterMessages', {});
  $fm->{$fname}=[] unless defined($fm->{$fname});
  my $fmsgs=$fm->{$fname};
  push @{$fmsgs}, $msg;
  $self->Log("debug", "Message queue for filter $fname: (`".
	     join("',`", @{$fmsgs})."')\n");
}

sub readFilter {
  my $self=checkref(shift);
  my $fname=shift;
  my $fm;
  my $fmsgs;
  if ($fname) {
    $self->Log("debug", "Trying to read from filter $fname\n");
    if ( ($fm=$self->getParameter('FilterMessages')) &&
	 ($fmsgs=$fm->{$fname}) &&
	 @{$fmsgs} ) {
      return shift @{$fmsgs};
    }
    else {
      $self->Log("debug", "No lines available\n");
      return undef;
    }
  }
  else {
    $self->Log("errors", "readFilter: No filter name given.\n");
    $!="No filter name given for read";
    return undef;
  }
}

=head1 Parameter manipulation

Identification and some behavior control for entities is provided
through named parameters. The names of the parameters are 
case-insensitive (internally, they are all converted to lowercase).

=head2 Accessing parameters through the standard methods

In the current implementation, these parameters are stored in the
object itself, which is represented as a hash. However, it is not
recommended that the parameters are accessed directly through the
hash. Instead, the C<setParameters> and C<getParameters> methods must
be used. C<setParameters> receives a hash with S<key => value>
elements, and returns the object reference itself. C<getParameters>
receives a list of parameter names, and returns a hash with the
requested parameters as keys, and their values (or C<undef> if the
requested parameter does not exist).

Another method used to access the parameters is C<getParametersList>,
which receives the same type of arguments as C<getParameters>, but 
instead of returning a hash, returns a list with only the values of
the requested parameters, in order, and with C<undef> in the place of
non-existant parameters.

For reducing typing errors, C<setParameter> behaves exactly like
C<setParameters>, and C<getParameter> (in singular) does the right
thing: returns not a hash, not an array, but the value of the
parameter whose name is passed as its first argument. Additionally,
if a second parameter is passed to C<getParameter>, it is used
to initialize the parameter if it is not defined.

Finally, the C<getParametersRef>, C<getParameterRef> and 
C<getParametersListRef> subroutines behave exactly like their non-Ref
counterparts, but instead of returning the values of the requested
parameters, return references ot them, so that the caller can store them
and later modify the parameters directly (not recommended), or read the
values dynamically in case they change through some other mechanism
(useful).

The C<deleteParameter> removes the given parameter altogether.

=cut

sub setParameters {
    my $self=shift;
    my %params=@_;
    my $k;
    foreach $k (keys %params) {
      $self->{lc($k)}=$params{$k};
    }
    return $self;
}

sub setParameter {
    return setParameters(@_);
}

sub getParameters {
    my $self=shift;
    my $k;
    ref($self) 
	or croak "getParameters can only be called as an instance method";
    my %returnedparams=map { $_ => $self->{lc($_)} } @_;
    return %returnedparams;
}

sub getParameter {
  my $self=shift;
  my $param=shift;
  my $value=shift;
  # Get the value of the parameter
  my $paramvalue=$self->{lc($param)};
  # If it is undefined and a default value is used, initialize it.
  if ( (!defined($paramvalue)) && defined($value) ) {
    $self->setParameter($param => $value);
    $paramvalue=$self->{lc($param)};
  }
  return $paramvalue;
}

sub getParametersList {
    my $self=shift;
    ref($self)
	or croak "getParametersList can only be called as an instance method";
    return map { $self->{lc($_)} } @_;
}

sub getParametersRef {
    my $self=shift;
    ref($self) 
	or croak "getParameters can only be called as an instance method";
    return map { $_ => \$self->{lc($_)} } @_;
}

sub getParameterRef {
    return (getParametersListRef(@_))[0];
}

sub getParametersListRef {
    my $self=shift;
    ref($self)
	or croak "getParametersList can only be called as an instance method";
    return map { \$self->{lc($_)} } @_;
}

sub deleteParameter {
  my $self=shift;
  my $key=lc(shift);
  if ($key) {
    delete $self->{$key};
  }
}

=head2 Accessing parameters through a hash

An easier but less powerful method for accessing parameters is also
available. When an B<AAFID::Entity> or a subclass of it is instantiated,
a hash called C<Params> is automatically created, which can be used
from anywhere in the object to access the parameters. Readings and
assignments to elements of the C<Params> hash are equivalent to calls
to C<getParameter> and C<setParameter>, respectively.

It is important to note some points:

=over 4

=item *

The C<%Params> array is a B<class> variable, not an instance one. This means
that it is shared by all the objects of the same class that inhabit a single
Perl process. This is not a problem in the current implementation because
each entity is a separate process.

=item *

If references to the parameters are needed, the C<getParametersRef>,
C<getParameterRef> or C<getParametersListRef> methods need to be used,
because if references are accessed through the C<%Params> hash, references
to copies of the actual parameters will be obtained.

=back 4

To implement the possibility of accessing parameters through a hash,
the methods C<TIEHASH>, C<FETCH>, C<STORE>, C<DELETE>, C<EXISTS>,
C<FIRSTKEY> and C<NEXTKEY> are defined, and the tying operation is
performed in the C<setTie> method, which is called during
initialization.

=cut

sub TIEHASH {
  my $class=shift;
  my $object=checkref(shift);
  bless $object, $class;
  return $object;
}

sub FETCH {
  my $self=checkref(shift);
  my $key=shift;
  my $value=$self->getParameter($key);
  $self->Log('params', "FETCH called with key '$key', returning '" .
	     ($value?"$value":"undef") . "'.\n");
  return $value;
}

sub STORE {
  my $self=checkref(shift);
  my $key=shift;
  my $value=shift;
  $self->Log('params', "STORE called with key '$key', value '$value'.\n");
  $self->setParameter($key => $value);
  return $value;
}

sub DELETE {
  my $self=checkref(shift);
  my $key=shift;
  my $oldvalue=$self->getParameter($key);
  $self->Log('params', "DELETE called with key '$key'.\n");
  $self->deleteParameter($key);
  return $oldvalue;
}

sub EXISTS {
  my $self=checkref(shift);
  my $key=lc(shift);
  $self->Log('params', "EXISTS called with key '$key'.\n");
  return exists $self->{$key};
}

sub FIRSTKEY {
  my $self=checkref(shift);
  $self->Log('params', "FIRSTKEY called.\n");
  my $a=keys %{$self};
  return scalar each %{$self};
}

sub NEXTKEY {
  my $self=checkref(shift);
  $self->Log('params', "NEXTKEY called.\n");
  return scalar each %{$self};
}

sub CLEAR {
  my $self=checkref(shift);
  # WE dont allow it
  $self->Log('params', "CLEAR called, but not allowed.\n");
}

sub DESTROY {
  # do nothing
}

sub setTie {
  my $self=checkref(shift);
  my $class=ref($self);
  my $varname="${class}::Params";
  no strict 'refs';
  tie %$varname, $class, $self;
  return $self;
}

=head1 Managing callbacks

Callbacks are used to cause an entity to react to input on file handles,
to set timed events, or to monitor files. These methods are essentially
wrappers around the corresponding methods in B<Comm::Reactor>.

First, methods for adding and removing callbacks for input on file handles.

=cut

sub addHandleEvent {
  my $self=checkref(shift);
  my ($fh, $callback)=@_;
  Comm::Reactor::add_acceptor($fh, $callback);
}

sub removeHandleEvent {
  my $self=checkref(shift);
  my $fh=shift;
  Comm::Reactor::remove_handle($fh);
}

=pod

Next, methods for monitoring regular files. These methods are different
in the sense that they receive a file name instead of a file handle. This
allows them to reopen the file in case it moves or is truncated. Also,
the monitoring mechanism is internally implemented differently.

C<addTextFileMonitor> has to receive at least one callback (to call
when data is read) but can also receive callbacks for when the file is
truncated, for when it is removed, and for when something (like an
open) fails. If these are not provided, default behavior is given, and
it is documented in B<Comm::Reactor>.

=cut

sub addTextFileMonitor {
  my $self=checkref(shift);
  my ($fname, $cbread, $cbtrunc, $cbrm, $cbfail)=@_;
  Comm::Reactor::add_file($fname, $cbread, $cbtrunc, $cbrm, $cbfail);
}

sub removeTextFileMonitor {
  my $self=checkref(shift);
  my $fname=shift;
  Comm::Reactor::remove_file($fname);
}

=pod

Next, methods for monitoring file handles on which AAFID messages may
arrive (these are the same as handle events, but the AAFID message
special format is expected). The callback is called with the file
handle and the text line read from the handle.

=cut

sub addAAFIDmsgEvent {
  my $self=checkref(shift);
  my ($fh, $callback)=@_;
  Comm::Reactor::add_handle($fh, $callback);
}

sub removeAAFIDmsgEvent {
  my $self=checkref(shift);
  my $fh=shift;
  Comm::Reactor::remove_handle($fh);
}

=pod

Next, methods for adding and removing one-time timed events.

=cut

sub addTimeEvent {
  my $self=checkref(shift);
  my ($time, $cb)=@_;
  Comm::Reactor::add_event($time, $cb);
}

sub removeTimeEvent {
  my $self=checkref(shift);
  my ($time, $cb)=@_;
  Comm::Reactor::remove_event($time, $cb);
}

=pod

Finally, methods for managing repeating events.

=cut

sub addRepeatingEvent {
  my $self=checkref(shift);
  my ($period, $cb)=@_;
  Comm::Reactor::add_repeating_event($period, $cb);
}

sub removeRepeatingEvent {
  my $self=checkref(shift);
  my ($period, $cb)=@_;
  Comm::Reactor::remove_repeating_event($period, $cb);
}

=head1 Keeping status

All entities (not only agents) keep a Status and a Message values, which
indicate the current state of the entity. Originally only agents kept
these values, but it seems to be useful to have all entities keep them.

These values are kept in the "Status" and "Message" standard parameters.
An entity can specify its own initial values for these parameters by
putting them in its C<%PARAMETERS> hash, or by assigning them during
its C<Init> method. If initial values are not specified by the entity,
zero and an empty string are used for the Status and Message, respectively.
In any case, copies of the initial values are stored when the entity
starts running in the B<_InitialStatus> and B<_InitialMessage> parameters,
so that they can be reset to these values if necessary.

The C<_InitStatus> method is called when the entity is initialized.

=cut

sub _InitStatus {
  my $self=checkref(shift);
  my $status=$self->getParameter('Status', 0);
  my $msg=$self->getParameter('Message', "");
  $self->setParameters(_InitialStatus => $status, _InitialMessage => $msg);
}

=pod

The REPORT_STATUS command causes the agent to respond with a "COMMAND
RESULT" message that contains two fields in the DATA portion of the
message: C<Status> and C<Message>, which contain the current status
level and descriptive message of the agent.

=cut

sub command_REPORT_STATUS {
  my $self=checkref(shift);
  my %status=$self->getParameters('Status', 'Message');
  return \%status;
}

=pod

The RESET_STATUS command causes the entity to restore its C<Status>
and C<Message> values to its initial values.

=cut

sub command_RESET_STATUS {
  my $self=checkref(shift);
  my ($status, $msg)=$self->getParametersList('_InitialStatus',
					      '_InitialMessage');
  $self->setParameters(Status => $status, Message => $msg);
  $self->Log('debug', $self->ID . ": resetting status to ($status, '$msg')\n");
  return undef;
}

=head1 Standard entity parameters

The parameters that are understood by all entities (the B<AAFID::Entity>
class and all of its subclasses) are:

=over 4

=item ClassCount

(integer reference) A reference to the number of entities of this
class that currently exist.  To access it, you would have to do
something like C<${$self-<gt>getParameter("ClassCount")}>.

=item InstanceNumber

(integer) The number assigned to the current instance.

=item ClassID

(string) The class name of the entity.

=item Description

(string) Optional parameter which gives a textual description of the Entity,
for identification purposes.

=item ClassVersion

(string) A version number of the form B<major.minor>, assigned by the
class writer and declared in the global variable C<$VERSION>. If using
RCS, a good way of making the version number coincide with the RCS
revision number is inserting this:

  # The following keeps up with the RCS version number. The
  # self-assignment keeps the -w switch from complaining
  # (because $VERSION may not be used here, but it is used in
  # our base class).
  $VERSION = do { my @r = (q$Revision: 1.51 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; $VERSION = $VERSION;

=item EntityID

(string) A unique identifier for the entity, of the form 
C<hostname:ClassID:Version:InstanceNumber>.

=item OpenHandles

(IO::Select) An B<IO::Select> object that contains the handles of all
the currently open input handles. It is initialized to an empty
object, but eventually at least C<STDIN> will be added to it. This is
done automatically by the C<AAFID::Comm::nextMsg> subroutine, to which
this object must be passed in every call.

It will also contain the reading end of the pipes of any sub-entity
that this entity is controlling.

This parameter is handled automatically by the Entity class, the entity
writer does not have to worry about it.

=item OpenHandleIDs

(hash reference) A reference to a hash that relates entity IDs to
information about them. This information is stored under different
keys:

=over 4 

=item C<Handle> 

(IO::Handle) Contains a reference to the file handle object that is
used to send information to the entity.

=item C<Description> 

(string) Contains the entity's descriptive text, if any. %'

=item C<Class>

(string) Contains the entity's class name. %'

=item C<_PendingConnect>

(boolean) If true, indicates that the entity corresponding to this
record was started by us, but has not yet sent a CONNECT message to
announce that it is alive. It also happens when a message comes from
standard input from an entity that had not yet been registered. It is
automatically registered, and this flag is set in the hopes that the
message itself will be a CONNECT PARENT message.

=item C<_isUp>

(boolean) If true, indicates that the entity corresponding to this record
is our "up" entity.

=back 4

This parameter, as well as C<OpenHandles>, are handled completely by
the Entity base class. The entity writer does not have to worry about
initializing them.

=item FiltersNeeded

(array reference) If the entity needs any filters to be active for its
operation, this parameter contains that information. Each key in the
hash is the name of a filter, and the element contains an array
reference whose first element is a hash reference that contains the
initial pattern that will be sent to that filter as filtering criteria
for data that the entity needs. The second element of the array, if it
exists, must be the name of a subroutine that will be invoked as a
callback whenever the filter generates data. The method will be
invoked as an instance method of the current entity with the filter
name, filter file handle and the message received as arguments, this
is, the equivalent of:

   $self->callback($fname, $fh, $msg)

where C<callback> is the name of the subroutine, C<$fname> is the name
of the filter (as a string), C<$fh> is the file handle for the filter,
and C<$msg> is the message that was received.

For example, an agent that needs to get data from the
B<NetworkAccesses> filter, that only needs to see the data from
C<telnet> requests, and whose C<processTelnet> method must be called
whenever data comes from that filter, could specify the following in
its C<%PARAMETERS> hash:

  FiltersNeeded => { NetworkAccesses => [ {
					   Service => 'telnetd'
					  },
					  'processTelnet'
					]
		   }

If the second argument (the name of the callback subroutine) is not
provided, no callback is associated to the filter, and data from it
will have to be requested manually using the C<readFilter> method.
Notice that at the low level, a callback is established that stores
the messages that come from each filter in the C<FilterMessages>
parameter, so that all that C<readFilter> does is get the next message
from the corresponding element of that parameter.

=item FilterPaths

(hash reference) Contains information that allows the Entity to
contact each of the filters it needs. Each key is a filter name, and
the corresponding element contains the path in which the Unix-domain
socket for the filter is located, and through which the entity can
contact it. This parameter is set by the controller entity that starts
the entity.

=item FilterMessages

(hash reference) The messages generated by filters for which no
callback was specified are stored here, and retrieved using the
C<readFilter> method. The hash is indexed by filter name, and each
element is an array reference that contains the messages, in the
order they were received.

=item runHeadless

(scalar) If defined, the entity will be able to run "headless", this
is, it will continue running if its channel from "up" is closed (normally
STDIN or some redirection thereof). See also B<onSTDINclose>. If this
parameter is not defined, the normal behavior (exit when a close on STDIN
is detected) occurs.

=item onSTDINclose

(hash reference) If defined, allows control over the behavior of the
entity when B<runHeadless> is defined and STDIN (or any other handle
associated with "up") is closed. The hash may contain the keys 
`onClose' and `onOutput', and each element must contain a reference
to a subroutine that will be called when the handle is closed and
whenever a message is sent "up". The subroutine defined by `onClose'
will be passed as argument the file handle itself, and the subroutine
defined by 'onOutput' will be passed the handle and the message (as a
string) that was sent "up".

If B<runHeadless> is defined by B<onSTDINclose> is not, the standard
behavior is to do nothing (apart from logging it) when STDIN is closed,
and to simply ignore any messages sent up (the messages effectively
disappear in thin air).

=item _headLess

(boolean) Internal parameter that is defined whenever we are running
headless.

=item ClassDir

(string) Base directory for all the classes used by AAFID, including
the ones in Comm::, AAFID:: and Filter::

=item AgentsDir

(string) Directory where the agent modules are. Usually it is
B<ClassDir/Agents>.

=item TmpDir

(string) Temporary directory.

=item Status, Message

(integer, string) Store the current Status of the entity and its
associated Message. Status has to be a value between 0 and 10, and
Message is any string that describes the current status.

=item _InitialStatus, _InitialMessage

(integer, string) Store copies of the initial values of Status and
Message, and are used by the RESET_STATUS command to restore Status
and Message to their initial values.

=back 4

=cut

=head1 Standard entity messages and commands

TODO

=cut

_EndOfEntity;

#
# $Log: Entity.pm,v $
# Revision 1.51  1999/09/06 22:52:17  zamboni
# - Made command_EVAL return the value produced by the evaluated expression,
#   if it is not undef.
# - Made the AAFID directories be put at the front of @INC instead of at
#   the end.
#
# Revision 1.50  1999/09/06 15:22:38  zamboni
# Added a log message to _cleanup.
#
# Revision 1.49  1999/09/03 17:08:53  zamboni
# Changed the start line to something that is path-independent, and
# updated the copyright notice.
#
# Revision 1.48  1999/08/31 07:33:40  zamboni
# - Fixed a bug in removeRepeatingEvent.
# - Renamed addFileMonitor and removeFileMonitor to addTextFileMonitor and
#   removeTextFileMonitor.
# - Added addAAFIDmsgEvent and removeAAFIDmsgEvent.
#
# Revision 1.47  1999/08/08 00:24:17  zamboni
# - Added support for initial values of Status and Message, provided by
#   the entity as parameters.
# - Moved status support to Entity.pm (from Agent.pm) by moving the
#   REPORT_STATUS command to Entity, creating the _InitStatus method,
#   adding the RESET_STATUS command, and adding some documentation about
#   it.
# - Added documentation for some standard parameters, and updated some
#   others.
#
# Revision 1.46  1999/06/28 21:22:04  zamboni
# Merged with a07-port-to-linux
#
# Revision 1.45.2.1  1999/06/28 18:31:34  zamboni
# - Added error checking when creating the new object in _EndOfEntity().
# - Made some changes in setupDefaultSignalHandlers. It is now in its
#   original state, but with some commented changes that were necessary
#   to silence some weird warnings and errors under Linux.
#
# Revision 1.45  1999/06/11 21:50:32  zamboni
# - Added the following methods, which are wrappers around the corresponding
#   Comm::Reactor methods:
# 	- addHandleEvent, removeHandleEvent
# 	- addFileMonitor, removeFileMonitor
# 	- addTimeEvent, removeTimeEvent
# 	- addRepeatingEvent, removeRepeatingEvent
# - Added runtimeInit() (copied it from Agent.pm)
# - Added eventLoop()
# - Modified run() to call runtimeInit() and to call eventLoop() instead
#   of Comm::Reactor::loop, and moved the call to sendConnectMessage
#   to after all the initialization.
# - Added descriptions for ClassDir, AgentsDir and TmpDir
#
# Revision 1.44  1999/06/10 14:46:15  zamboni
# Commented out trap for __WARN__
#
# Revision 1.43  1999/06/09 06:40:00  zamboni
# - Added signal handlers for __WARN__ and for __DIE__ to be able to
#   debug some strange warnings that occur in Linux.
#
# Revision 1.42  1999/06/08 05:01:54  zamboni
# Merged branch a06-raw-data-collection into main trunk
#
# Revision 1.41.2.1  1999/06/07 16:05:45  zamboni
# - Added the 'runHeadless' parameter which allows an Entity to continue
#   running after receiving a close on STDIN.
#   This still needs some work.
# - Added some error checking.
# - Made it send itself a fake DISCONNECT message when a channel close is
#   received on a channel for which we have an associated entity.
# - Added deleteParameter()
# - Added definitions of methods DELETE, EXISTS, FIRSTKEY, NEXTKEY, CLEAR
#   and DESTROY to have full compliance with hash operations (for tying to
#   a hash).
#
# Revision 1.41  1999/04/01 02:37:48  zamboni
# - Added error check to connectFilters after creating the callback.
# - Created a new subroutine _setFilterCallback that gets called from
#   connectFilters as an instance method. This allows subclasses to
#   override this method if they want to define a callback in a different
#   way (for example, see Agent.pm).
# - Added callAtStart which allows to specify a subroutine that will be
#   called immediately upon running, but still needs work.
# - Added code to allow the entity to run headless, this is, without a
#   controlling entity. These modifications include the addition of the
#   runHeadless parameter. If this parameter is set, a close on STDIN
#   does not terminate the entity as it normally does, but simply sets
#   a flag and the entity keeps running normally. The new parameter
#   onSTDINclose allows the specification of what actions to take when
#   the entity tries to send messages to STDOUT while it is headless.
#   The default action is to simply drop the messages. When a message
#   comes from STDIN, the entity recovers its "head" and is able to
#   work normally again.
#
#   This feature still needs work, it doesn't really work yet.
#
# Revision 1.40  1999/03/29 22:33:25  zamboni
# Merged branch a05-new-comm-module, which updates it to make use of the new event-based communication mechanism.
#
# Revision 1.39.2.5  1999/03/29 16:20:21  zamboni
# - Major change to _EndOfEntity. It it no longer needs to spawn a separate
#   interface process when running standalone. Instead, the _standalone
#   flag of the entity is set, which causes it to process input from STDIN
#   and output to STDOUT differently, without using the Reactor message
#   format. Very nice for easier debugging, and it removed 65 lines
#   of code from _EndOfEntity.
# - Modified setupSTDINcallback to check for the _standalone flag and set
#   an acceptor instead of a handler if the flag is set. Using an acceptor
#   allows us to not use the Reactor message format for reading from STDIN
#   when the entity is running standalone.
# - Modified sendDisconnectMessage to not call _write on the handle if
#   running standalone.
# - Improved processInput's way of recognizing if the handle on which a close
#   occured is STDIN.
# - Made processInput check OpenHandleIDs when a close is received on a handle.
#   If the handle is found there, fake a disconnect message to clean up after
#   the entity that died. This only works for two-directional handles (such
#   as sockets) because it looks for the handle on which the message came in,
#   and for pipes that handle is not stored in OpenHandleIDs.
# - Modified sendReport to call AAFID::Comm::sendMsgUp with a special flag
#   to indicate if we are running standalone. This allows sendMsgUp to use
#   the proper message format depending on whether it is running standalone.
# - Similar modification to sendMsgTo.
#
# Revision 1.39.2.4  1999/03/28 01:14:21  zamboni
# - Made setUpCommunicationCallbacks and _EndOfEntity use
#   Comm::Reactor::stdin() instead of creating their own handles for
#   STDIN.
# - Renamed setUpCommunicationCallbacks to setupSTDINcallback, which
#   reflects its function better.
# - Added calls to Comm::Reactor::flush before exiting.
# - HACK: made sendDisconnectMessage write the message immediately by
#   calling Comm::Reactor::_write directly. This is wrong and ugly. There
#   must be a better way of doing it.
# - Fixed connectFilters to interpret the new format of the FiltersNeeded
#   parameter and to add the corresponding callbacks.
# - Added storeFilterMsg as the generic callback for filters that do not
#   have a specific callback. It stores the messages in the FilterMessages
#   parameter for later retrieval.
# - Changed readFilter to get the next line from the FilterMessages parameter
#   instead of trying to read it directly.
# - Updated documentation of FiltersNeeded.
# - Added description of FilterMessages.
# - Made setupSTDINcallback check the _standalone parameter so that the
#   messages from STDIN are processed adequately. I am about to make
#   a major change to _EndOfEntity to support this, that is why I am checking
#   in this version, just to make sure I can go back if needed.
#
# Revision 1.39.2.3  1999/03/19 17:09:10  zamboni
# - Modified processInput to use Comm::Reactor::stdout instead of creating
#   its own STDOUT handle.
#
# Revision 1.39.2.2  1999/03/18 20:53:30  zamboni
# - Removed the while(1) { ... } from around calls to Comm::Reactor::loop
#   because now loop has its own infinite loop built in.
# - Added a call to Comm::Reactor::flush in _cleanup (this was previuosly
#   a call to Comm::Reactor::loop, but the semantics of loop have changed,
#   as noted in the previous item).
#
# Revision 1.39.2.1  1999/03/18 00:46:20  zamboni
# - Modified _EndOfEntity to do the following: when the entity is being
#   run standalone, it creates a "wrapper process" that reads from STDIN
#   using Perl's line-oriented methods (<STDIN>) and sends the messages
#   to the entity through a pipe using Reactor::send_message. Similarly,
#   it reads from the entity's standard output through a pipe using
#   Reactor callbacks, and prints the messages to STDOUT. This allows
#   the entity to internally use the Reactor message format, which
#   involves special coding of the messages, without the user ever
#   seeing it.
# - Added setUpCommunicationCallbacks(); the default version in Entity
#   adds a callback for STDIN. This routine is called from run(), so any
#   subclass must call it manually.
# - Added a final call to Comm::Reactor::loop to _cleanup to flush
#   pending writes and reads before exiting.
# - Modified processInput to be called as a Comm::Reactor callback.
# - The standalone Entity runs ok now with the new communication objects.
# - Still missing updates in the POD sections.
#
# Revision 1.39  1999/03/05 16:48:38  zamboni
# - Fixed signal handling. It is now done correctly (I think)
# - Added a restart capability, accessible both by sending the process
#   a HUP signal and by issuing the RESTART command. The restart is done
#   in the _restart subroutine.
#
# Revision 1.38  1999/03/05 15:52:57  zamboni
# Cosmetic fix (broke a long line in two).
#
# Revision 1.37  1998/10/20 22:33:46  zamboni
# Fixed a problem that prevented plain AAFID::Entity's from being
# instantiated. The problem was that the default version of the Init
# subroutine did not return a value, and thus _init was taking it as
# failed and aborting the instantiation.
#
# Revision 1.36  1998/09/28 15:39:49  zamboni
# Made some changes in the documentation that were suggested by
# Stephanie Miller on her evaluation of the prototype. Also, applied a
# patch provided by Frederic Dumont that adds some error checking to the
# agents and the initialization of an Entity (it checks for Init
# returning 'undef') and corrects a color in the GUI ("darkyellow"
# apparently does not exist on 8-bit displays). Finally, made the
# default value of LogFile in config/AAFID be /tmp/aafid.log.
# Added Linda Jessen to the CREDITS file.
#
# Revision 1.35  1998/09/09 09:04:53  zamboni
# * Entity.pm: made readLine return undef on both EOF and
#   error, but to also set $! on error.
#
# Revision 1.34  1998/09/07 17:35:20  zamboni
# Added support for filters!
#
# * Monitor.pm: Replaced all uses of AAFID::Message->new by uses
#   of $self->newMsg.
#
# * Filter.pm: Cleaned up the code and the comments, and added some
#   extra features, such as default versions of makefield and
#   makeline that make it easier to define new filters if they read
#   data where the fields are space-separated. Also, added some
#   error checks here and there.
#
# * Entity.pm: Added filter support
#   - Added descriptions for FiltersNeeded and FilterPaths parameters.
#   - Modified getParameter so that if a second argument is given, it is
#     used to initialize the parameter in case it is undefined.
#   - Added subroutines connectFilters, newFilterClientSocket,
#     setFilterPattern
#   - Added subroutine newMsg.
#
# * ControllerEntity.pm: Added filter support.
#   - Some general cleanup (removing stray commented code, etc.)
#   - Rewrote some sections for clarity (in invoke, _invoke,
#     _instantiate_and_run)
#   - Modularized loadmodule. Created new subroutines:
#       _loadmodule_error
#       _loadmodule_success
#   - Modularized _instantiate_and_run, creating new subroutine
#       _fork_and_run.
#   - Added the setupFilters subroutine, which takes care of loading
#     all the filters required by an entity. It is complemented by
#     an augmented message_CONNECT, which detects when a filter is
#     connecting and does the necessary updating.
#
# * Comm.pm: Removed an annoying debug message from bufferInput.
#
# * Agent.pm: Added code to contact the filters.
#
# Revision 1.33  1998/08/26 22:36:56  zamboni
# Reimplemented the reading mechanisms to use non-buffered I/O. This fixes
# problems that ocurred when mixing buffered I/O with the use of the Select
# object. Now the AAFID::Comm library keeps an internal buffer of lines
# read, and allows to retrieve them through nextMsg (interpreting them
# as AAFID messages, as usual) and through a new subroutine called nextLine,
# which returns the line read.
#
# Also, all the reading routines (nextMsg and now nextLine) return only
# the next available element. I had changed nextMsg so that it returned an
# array of messages, but that was a bad decision, I think, so we went back
# to the old behavior.
#
# Moved the RCS log to the end of each file.
#
# Now nextMsg and nextLine take named parameters for Timeout, Handle and
# Select object.
#
# Entity.pm and Monitor.pm were modified to go back to the behavior of Comm::nextMsg
# returning a single message. They had been modified to process a list of messages,
# but this was seen to be a bad decision.
#
# Also, calls to nextMsg were modified to make use of the new syntax, which requires
# named parameters.
#
# Revision 1.32  1998/08/11 16:24:00  zamboni
# - Made processInput use the new feature of AAFID::Comm::nextMsg that returns
#  a list of messages instead of a single message. Now processInput processes
#  at once all the pending messages, instead of only the first one.
#
# Revision 1.31  1998/06/29 20:11:23  zamboni
# Added copyright message
#
# Revision 1.30  1998/06/26 21:28:09  zamboni
# - Modified the constructor (sub new) to recursively incorporate all the
#   %PARAMETERS hashes of all the subclasses and the current class into
#   the object's parameters. This makes it unnecessary now to do any
#   manual additional incorporation, as was done previously in the _init
#   subroutine of Agent.pm. This also makes the interaction with the
#   configuration mechanism (AAFID::Config) cleaner, because now the
#   AAFID::Config::configure subroutine can be safely called before
#   _init, knowing that no default parameters will be set in _init
#   any more.
#
# - Changed the order to first set all the standard parameters (from the
#   %PARAMETERS hashes) and then to set the entity-specific parameters
#   such as entity counter, entity ID, etc. (previously it was the
#   other way around).
#
# - Added the use of AAFID::Config to get the log file name, as well as
#   the log categories that have to be activated. Now the constructor
#   of Entity registers and activates all the log categories.
#
# Revision 1.29  1998/06/25 20:48:52  zamboni
# - Made the entity parameters be case-insensitive. Also, modified
#   getParameters to not return the result of the map directly (which
#   could return a hash with repeated elements if the same parameter
#   is requested twice), but to assign that to a temporary internal hash
#   (which eliminates any repeated elements) and return the temporary
#   hash.
#
# - Made Entity a subclass of AAFID::Config and added a call to
#   $self->configure in the constructor.
#
# Revision 1.28  1998/06/17 19:47:38  zamboni
# - Added TIEHASH, STORE, FETCH and setTie methods to create a %Params hash
#   during object creation that allows access to the parameters through
#   that hash. A call to setTie was inserted in the constructor.
#
# Revision 1.27  1998/05/07 06:04:06  zamboni
# - Added relayMessage, which is called when isForMe returns false to
#   send the message to someone else. The purpose of making it into a
#   separate method is that it can then be overriden by subclasses (for
#   example, Monitor) to do the appropriate thing.
#
# Revision 1.26  1998/05/07 05:18:35  zamboni
# - Added the use of the two methods PreMessageHook and PostMessageHook.
#   If an entity that is a subclass of AAFID::Entity defines any (or
#   both) of these methods, they will be called before and after a
#   received message is processed, respectively, but only if the message
#   is destined for the entity (as reported by isForMe).
#
# - Made it possible to call breakID as a class method
#   (AAFID::Entity::breakID), and not only as an instance method.
#
# Revision 1.25  1998/05/03 03:36:25  zamboni
# - Corrected a condition check in isForMe that made it print an "I don't
#   know her" message when the message is for us.
#
# Revision 1.24  1998/05/02 19:51:38  zamboni
# POD correction.
#
# Revision 1.23  1998/04/27 15:09:29  zamboni
# - Added a command GET_PARAMS that receives an argument called "Params" that
#   itself contains a comma-and-space-separated list of parameter names
#   that are to be returned.
# - Added documentation for the GET_PARAMS and DUMP_YOURSELF commands.
# - Made only "messages" and "errors" log categories active by default.
#
# Revision 1.22  1998/03/17 06:39:37  zamboni
# - Changed cleanup to Cleanup to be consistent with Init.
#
# Revision 1.21  1998/03/13 16:59:06  zamboni
# - Added $VERSION and %PARAMETERS.
# - Made $VERSION and %PARAMETERS set correctly when the constructor is
#   called from a subclass. Previously, only the values defined in AAFID::Entity
#   were used. Now, those are used as default, but are then overriden by any
#   subclass-provided parameters.
# - Added description of how to set $VERSION to the documentation part.
#
# Revision 1.20  1998/03/13 01:47:53  zamboni
# Made $VERSION keep up with the RCS revision number.
#
# Revision 1.19  1998/03/06 07:10:55  zamboni
# - Made processInput return the message that was received.
# - Made $self->ID check if the ID has not been assigned, and if so, build
#   and return it. Then made the constructor use $self->ID instead of
#   building the ID there.
# - Added breakID subroutine.
# - Added "use strict"
# - Corrected problems pointed out by strict module. One not-so-nice "problem"
#   detected was the use of barewords for getParameter, as in
#   $self->getParameter(Foo), which had to be changed to
#   $self->getParameter('Foo') on strict's complain. Less convenient, but
#   safer, I guess.
# - Made processInput recognize when the message is for the current entity, and
#   if not, relay it appropriately. "For the current entity" means that the
#   TO field of the message contains either the ID of the current entity or
#   the wildcard "-".
# - Made it even more intelligent. If the message is not for the current
#   entity, but the entity to which it is addressed does not exist, we
#   take it.
#
# Revision 1.18  1998/03/05 03:32:41  zamboni
# - Added the command DUMP_YOURSELF
#
# Revision 1.17  1998/03/04 22:07:42  zamboni
# - Moved the exit(0) to inside _cleanup. This way, subclasses (such as
#   ControllerEntity) can override it to avoid exiting immediately when
#   a cleanup is called.
# - Removed the exit(0) from processInput after the call to _cleanup, and
#   replaced it with a return undef. See above.
# - Modified message_STOP to call $self->command_STOP and not simply
#   command_STOP, because otherwise it always calls Entity::command_STOP,
#   even if it has been overriden in the class it is being called from.
#
# Revision 1.16  1998/03/04 19:43:44  zamboni
# Removed spurious log message from sendMsgTo (it printed the Dump of the
# handle to which it is sending the message, which was unnecessary and ugly).
#
# Revision 1.15  1998/03/04 06:31:48  zamboni
# Did general debugging and cleaning up. Seems to mostly work now.
# Did the following changes in the entity IDs:
#   - 'AAFID::' is removed from the class name, if it contains it.
#   - The host name is reduced to only the host part, domain name is removed.
# TODO: These are just for the sake of space and readability of the log
# files. These changes (particularly the removing of the domain name)
# should be undone later.
#
# Revision 1.14  1998/02/26 21:22:33  zamboni
# Made sendDisconnectMessage() take two optional parameters: first, the reason
# for the disconnect, which will be sent literally in the DATA field of
# the message, and second, an entity ID to which the message must be sent. If
# this ID is not provided, the message is sent up with a subtype of CHILD. If
# it is there, it is sent to that entity with a subtype of PARENT.
#
# Revision 1.13  1998/02/26 21:00:48  zamboni
# - Added message_CONNECT. This was going to originally be in ControllerEntity,
#   but then I realized that ControllerEntities should also send a CONNECT
#   message down to their children, and thus any entity needs to be able
#   to react to it.
# - Changed all uses of OpenHandleIDs to its new structure, in which the
#   parameter is a reference to a hash keyed by entity ID, and each element
#   in turn is a hash with keys for different pieces of information about
#   the entity, such as Class, Description or Handle.
#
# Revision 1.12  1998/02/26 05:31:25  zamboni
# Changed the references to $msg->{FROMSTDIN} to $msg->{_FROMSTDIN}.
#
# Revision 1.11  1998/02/26 04:57:10  zamboni
# Disabled debugging output by default.
#
# Revision 1.10  1998/02/25 20:50:29  zamboni
# Changed a bit of wording in documentation.
#
# Revision 1.9  1998/02/25 19:28:38  zamboni
# Added a comment about sendConnectMessage in run().
#
# Revision 1.8  1998/02/25 19:26:57  zamboni
# Moved the sending of the CONNECT message from the constructor to the
# run() method. This is because when a new entity is instantiated from
# a controller entity, it will not yet be running on a separate process,
# and thus the message would go to the standard output of the controller
# entity. When the run() method is invoked, everything will be set up
# correctly.
#
# Revision 1.7  1998/02/25 07:20:20  zamboni
# - Added functions to get references to the parameters (getParametersRef,
#   getParameterRef, getParametersListRef)
# - Added section on standard entity parameters at the end.
# - Added internal method _init which is called from the constructor instead
#   of calling Init directly. _init in this class simply calls Init, but
#   in other classes it may be used to do some standard initialization
#   before (or after) calling the user-provided Init.
# - Made processInput take an optional argument to specify a timeout.
# - Corrected a number of minor errors.
#
# Revision 1.6  1998/02/23 05:41:06  zamboni
# First apparently fully working version. Correctly processes messages
# and commands.
#
# Revision 1.5  1998/02/18 20:09:26  zamboni
# First version that should kind of work by being able to receive messages
# through stdin, and generate output through stdout. Of course, it needs
# supporting functions from AAFID::Comm to do that, but I'll work on it now.
#
# Revision 1.4  1998/02/18 05:20:06  zamboni
# First checkin of the new version of Entity, using stdin/stdout for
# communication, and many other changes.
#
# Revision 1.3  1998/01/28 21:58:12  zamboni
# Changed the message printed by run().
#
# Revision 1.2  1998/01/27 03:33:02  zamboni
# Seems to work, mostly. The USR1 signal handler is looping once before
# stabilizing in the sleep (like if it was receiving two signals, or something),
# but it pauses just fine. My biggest problem now is the HUP signal handler.
# The problem is that I need to call the run method again, but it can only
# be called as an instance method. Thus, it cannot be called from the signal
# handler, because at that point we do not have information about the object
# we are in. I have to give that a little more thought.
#
# Revision 1.1  1998/01/26 23:30:52  zamboni
# Initial revision
#
