PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/copilot/lib/Copilot/Container/TCP.pm

http://cernvm-copilot-monitor.googlecode.com/
Perl | 319 lines | 185 code | 76 blank | 58 comment | 10 complexity | 73f685bf050cc33a08d896c4c15e37e4 MD5 | raw file
  1. package Copilot::Container::TCP;
  2. =pod
  3. =head1 NAME Copilot::Container::TCP
  4. =head1 DESCRIPTION
  5. Container class for a TCP server. Copilot::Container::TCP is a child of Copilot::Container class. This class creates a listeing TCP socket and also allows to
  6. open client sockets to send data. The class also provides logging for the component.
  7. Please also see the documentation of Copilot::Container
  8. =cut
  9. =head1 METHODS
  10. =item new($options)
  11. Constructor for Copilot::Container::TCP class. Takes as an input hash reference with options. The following options can be specified:
  12. Component => Name of the component which must run in the container
  13. LoggerConfig => Path of the file with logger configuration (Log::Dispatch format)
  14. ComponentOptions => Hash reference with options which must be passed to the component.
  15. Example usage:
  16. my $jm = new Copilot::Container::TCP (
  17. {
  18. Component => 'JobManager',
  19. LoggerConfig => '/home/hartem/src/x/etc/JMLogger.conf',
  20. ComponentOptions => {
  21. workDir => '/tmp/chirp',
  22. AliEnUser => 'hartem',
  23. },
  24. }
  25. );
  26. =cut
  27. use strict;
  28. use warnings;
  29. use vars qw (@ISA);
  30. use Copilot::Container;
  31. use POE;
  32. use POE::Component::Server::TCP;
  33. use POE::Filter::Reference;
  34. use POE::Component::Logger;
  35. use Data::Dumper;
  36. @ISA = ("Copilot::Container");
  37. sub _init
  38. {
  39. my $self = shift;
  40. my $options = shift;
  41. #
  42. # Read config
  43. $self->_loadConfig($options);
  44. #
  45. # Create logger
  46. POE::Component::Logger->spawn(ConfigFile => $self->{'LOGGER_CONFIG_FILE'});
  47. #
  48. # Create TCP server here
  49. POE::Component::Server::TCP->new (
  50. Port => $self->{PORT},
  51. Alias => $self->{NAME},
  52. ClientFilter => "POE::Filter::Reference",
  53. ClientInput => \&_clientInput,
  54. ClientDisconnected => \&_clientDisconnected,
  55. ClientError => \&_clientError,
  56. InlineStates => {
  57. 'log' => \&_log,
  58. },
  59. Started => sub {
  60. $_[KERNEL]->state ('_wakeUp', $self);
  61. $_[KERNEL]->state ($self->{'SEND_HANDLER'}, $self);
  62. $_[KERNEL]->state ('_sendTCP', $self);
  63. $_[KERNEL]->state ('logger', $self);
  64. $_[KERNEL]->delay ('_wakeUp', 2 ); # wait untill the all objects are initialized and call wakeUp
  65. # $_[SESSION]->option (trace => 1);
  66. },
  67. ObjectStates => [
  68. $self => [
  69. # delivers messages from client to component
  70. deliverInput => 'deliverInput',
  71. # delivers messages from server back to client
  72. $self->{'OUTPUT_HANDLER'} => 'deliverOutput',
  73. # if needed component can use this to register itself somewhere
  74. # after startup
  75. $self->{'SEND_HANDLER'} => 'send',
  76. # writes messages to log
  77. $self->{'LOG_HANDLER'} => 'logger',
  78. # internal event for sending out tcp packets
  79. _sendTCP => '_sendTCP',
  80. # internal event for waking up the component
  81. _wakeUp => '_wakeUp',
  82. #
  83. ],
  84. ]
  85. );
  86. my $module = "Copilot::Component::".$self->{COMPONENT_NAME};
  87. eval " require $module";
  88. if ($@)
  89. {
  90. die "Failed to load $module : $@ \n";
  91. }
  92. $self->{COMPONENT} = $module->new( {
  93. 'COMPONENT_NAME' => $self->{'COMPONENT_NAME'},
  94. 'SERVER_ALIAS' => $self->{'NAME'},
  95. 'SERVER_OUTPUT_HANDLER' => $self->{'OUTPUT_HANDLER'},
  96. 'SERVER_SEND_HANDLER' => $self->{'SEND_HANDLER'},
  97. 'COMPONENT_OPTIONS' => $options->{'ComponentOptions'},
  98. 'SERVER_LOG_HANDLER' => $self->{'LOG_HANDLER'},
  99. }
  100. );
  101. return $self;
  102. }
  103. #
  104. # Loads config parameters into $self
  105. sub _loadConfig
  106. {
  107. my $self = shift;
  108. my $options = shift;
  109. # Component which will be running inside our server
  110. $self->{'COMPONENT_NAME'} = $options->{'Component'} || die "Component name not provided. Can not start the server.\n";
  111. # Will be used as an alias for POE::Session::Server::TCP
  112. $self->{'NAME'} = "Server_".$self->{'COMPONENT_NAME'};
  113. # Port on which to listen
  114. $self->{'PORT'} = $options->{'Port'} || 1984;
  115. # Event name which will be used to send messages from component to this server
  116. $self->{'OUTPUT_HANDLER'} = 'deliverOutput';
  117. # Event name which will be used to log messages
  118. $self->{'LOG_HANDLER'} = 'logger';
  119. # Event name which will be used in case when the component needs to send something
  120. # to the outer world.
  121. $self->{'SEND_HANDLER'} = 'send';
  122. # Logger configuration file
  123. $self->{'LOGGER_CONFIG_FILE'} = $options->{'LoggerConfig'} || die "Logger configuration file ton provided. Can not start the server.\n";
  124. }
  125. #
  126. # Called when server gets input from from clients
  127. sub _clientInput
  128. {
  129. my ( $kernel, $input, $heap ) = @_ [ KERNEL, ARG0, HEAP ];
  130. my $clientIp = $heap->{remote_ip};
  131. $kernel->yield ('deliverInput', $input);
  132. $kernel->yield ('logger', "Got '". $input->{'command'} ."' from $clientIp", 'debug');
  133. }
  134. #
  135. # Called when client disconnects
  136. sub _clientDisconnected
  137. {
  138. # print "\n\nClient disconnected !!!\n\n";
  139. }
  140. #
  141. # Called when there is an error in the connection
  142. sub _clientError
  143. {
  144. }
  145. #
  146. # Delvers input from client to the component
  147. sub deliverInput
  148. {
  149. my ($self, $heap, $kernel, $sender, $input) = @_[ OBJECT, HEAP, KERNEL, SESSION, ARG0];
  150. my ($componentAlias, $handler ) = ($self->{'COMPONENT_NAME'}, $self->{'COMPONENT'}->getInputHandler() );
  151. # Dispatch the input message to the component
  152. $kernel->post ($componentAlias, $handler, $input, $sender);
  153. }
  154. #
  155. # Delivers output from component to the client
  156. sub deliverOutput
  157. {
  158. my ($self, $output, $kernel, $heap) = @_ [OBJECT, ARG0, KERNEL, HEAP];
  159. #print "In deliver Output $self $output $kernel $heap\n";
  160. $heap->{client}->put ($output);
  161. }
  162. #
  163. # Component can use this event to communicate with outer world
  164. sub send
  165. {
  166. my ($self, $kernel, $input) = @_[ OBJECT, KERNEL, ARG0];
  167. if ($input->{'proto'} eq 'tcp')
  168. {
  169. $kernel->yield('_sendTCP', $input);
  170. }
  171. else
  172. {
  173. die "Protocol $input->{'proto'} is not known. Can not deliver message for component\n";
  174. }
  175. }
  176. #
  177. # internal used by 'send' event for sending the messages out
  178. sub _sendTCP
  179. {
  180. my ($self, $kernel, $input) = @_[ OBJECT, KERNEL, ARG0];
  181. use POE::Component::Client::TCP;
  182. my ($serverHost, $serverPort) = ($input->{'host'}, $input->{'port'});
  183. $kernel->yield ('logger', "Sending message to $serverHost:$serverPort for the component", 'info');
  184. POE::Component::Client::TCP->new(
  185. RemoteAddress => $serverHost,
  186. RemotePort => $serverPort,
  187. Filter => "POE::Filter::Reference",
  188. ServerInput => sub {}, # we do not expect to get anything from the server
  189. ObjectStates => [ $self => [_connected => '_connected']],
  190. Connected => sub { $_[KERNEL]->yield ('_connected', $input);},
  191. ConnectError => sub {
  192. my ($syscall, $errorNo, $error) = @_[ARG0, ARG1, ARG2];
  193. $kernel->yield ('logger', "$syscall failed in attempt to connect to $serverHost:$serverPort. $error ($errorNo)");
  194. die;
  195. },
  196. ServerError => sub {
  197. my ($syscall, $errorNo, $error) = @_[ARG0, ARG1, ARG2];
  198. $kernel->yield ('logger', "$syscall failed in thr connection to $serverHost:$serverPort. $error ($errorNo)");
  199. die;
  200. },
  201. );
  202. }
  203. sub _connected
  204. {
  205. my ($self, $output, $kernel, $heap) = @_[ OBJECT, ARG0, KERNEL, HEAP];
  206. $output->{info}->{agentProto} = 'tcp';
  207. $_[HEAP]->{server}->put ($output->{info});
  208. $kernel->yield ('logger', "Sending ". $output->{'info'}->{'command'} . " to server", 'info');
  209. $kernel->yield ('shutdown');
  210. #die "zzzz";
  211. }
  212. #
  213. # internal event for waking the component up
  214. sub _wakeUp
  215. {
  216. my ($self, $kernel) = @_[OBJECT, KERNEL];
  217. my ($componentAlias, $wakeUpHandler );
  218. eval { ($componentAlias, $wakeUpHandler ) = ($self->{'COMPONENT_NAME'}, $self->{'COMPONENT'}->getWakeUpHandler() ) };
  219. if ($@)
  220. {
  221. $kernel->yield('logger', 'The component does not need to be waken up.', 'info');
  222. return;
  223. }
  224. $kernel->yield('logger', 'Waking the component up.', 'info');
  225. $kernel->post ($componentAlias, $wakeUpHandler, $_[SESSION]);
  226. }
  227. sub logger
  228. {
  229. my ($self, $msg) = @_[OBJECT, ARG0];
  230. my $logLevel = $_[ARG1] || "info";
  231. # print "log: $msg\n"
  232. Logger->log ( {
  233. level => $logLevel,
  234. message => $msg."\n",
  235. }
  236. );
  237. }
  238. "M";