Launcher.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:8k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. ##
  2. # MODULE DESCRIPTION: Startup notification: launcher
  3. #
  4. # This class provides an easy way to launch an application while
  5. # monitoring its startup process.
  6. #
  7. # <h3>Terminology</h3>
  8. # <dl>
  9. # <dt><b>Launcher</b></dt>
  10. # <dd>The application which wants to start another application.</dd>
  11. # <dt><b>Launchee</b></dt>
  12. # <dd>The application you want to start.</dd>
  13. # </dl>
  14. #
  15. # <h3>Usage</h3>
  16. # Create a new StartupNotification::Launcher class, and call the
  17. # $StartupNotificationLauncher->getArg() method.
  18. # Pass the result as the first parameter to the launchee. You can
  19. # then use the get* methods to check on the launchee's startup
  20. # progress and status. If the progress is at 100%, then that means
  21. # that the launchee has fully initialized.
  22. #
  23. # The launchee <b>must</b> support startup notification, either by
  24. # using the @CLASS(StartupNotification::Launchee) class, or by
  25. # implementing the protocol as described below.
  26. #
  27. # You are supposed to use this class in combination with
  28. # @CLASS(AppLauncher), @CLASS(PerlLauncher), or any other
  29. # mechanism for starting external application.
  30. #
  31. # Note that this class cannot always detect whether the launchee
  32. # exited unexpectedly. So you must also use your launching
  33. # mechanism's functions to check whether the launchee exited.
  34. # (for example, using $AppLauncher->check())
  35. #
  36. # <h3>Launcher example</h3>
  37. # This example launches a Perl script (using @CLASS(PerlLauncher))
  38. # and monitors its startup progress. See @CLASS(StartupNotification::Launchee)
  39. # for a launchee example.
  40. # <pre class="example">
  41. # use strict;
  42. # use Utils::PerlLauncher;
  43. # use Utils::StartupNotification::Launcher;
  44. #
  45. # my $sn = new StartupNotification::Launcher();
  46. # my $launcher = new PerlLauncher(undef, 'testStartupNotifyLaunchee.pl', $sn->getArg());
  47. # if (!$launcher->launch(0)) {
  48. #     print STDERR "Cannot launch application.n" .
  49. #                  "Error message: " . $launcher->getError() . "n" .
  50. #                  "Error code: " . $launcher->getErrorCode() . "n";
  51. #     exit 1;
  52. # }
  53. #
  54. # while (1) {
  55. #     if ($launcher->check()) {
  56. #         if ($sn->getProgress() == -1) {
  57. #             print "Application exited unexpectedly.n";
  58. #             print "Currently its still running.n";
  59. #             print "-------------------n";
  60. #
  61. #         } else {
  62. #             print "Application startup progress: " .
  63. #                 $sn->getProgress() . "%n";
  64. #             print "Application startup status: " .
  65. #                 $sn->getStatus() . "n";
  66. #             print "-------------------n";
  67. #         }
  68. #
  69. #     } else {
  70. #         print "Application exited with exit code " .
  71. #         $launcher->getExitCode() . ".n";
  72. #         print "Progress: " . $sn->getProgress() . "n";
  73. #         if ($sn->getProgress()) {
  74. #             print "Application was exited unexpectedly.n";
  75. #         }
  76. #         last;
  77. #     }
  78. #     sleep 1;
  79. # }
  80. # </pre>
  81. #
  82. # <h3>Protocol</h3>
  83. # TODO...
  84. package StartupNotification::Launcher;
  85. use strict;
  86. use IO::Socket::INET;
  87. use Utils qw(dataWaiting);
  88. use IPC::Messages;
  89. ### CATEGORY: Class StartupNotification::Launcher
  90. ##
  91. # StartupNotification::Launcher->new()
  92. # Ensures: $self->getProgress() == 0 && $self->getStatus() eq ''
  93. # Throws: @CLASS(StartupNotification::CreateSocketException)
  94. #
  95. # Creates a new StartupNotification::Launcher object.
  96. sub new {
  97. my ($class) = @_;
  98. my %self;
  99. $self{socket} = new IO::Socket::INET(
  100. LocalHost => '127.0.0.1',
  101. Proto => 'tcp',
  102. Listen => 1,
  103. ReuseAddr => 1
  104. );
  105. if (!$self{socket}) {
  106. die new StartupNotification::CreateSocketException(
  107. $!, int($!));
  108. }
  109. # int progress
  110. $self{progress} = 0;
  111. # String status
  112. $self{status} = '';
  113. # Bytes buf
  114. $self{buf} = '';
  115. # IO::Socket::INET client
  116. # String error
  117. # int errno
  118. return bless %self, $class;
  119. }
  120. sub DESTROY {
  121. $_[0]->{socket}->close() if ($_[0]->{socket});
  122. $_[0]->{client}->close() if ($_[0]->{client});
  123. }
  124. ##
  125. # String $StartupNotificationLauncher->getArg()
  126. # Ensures: result ne ''
  127. #
  128. # Returns a the argument which you must pass to the
  129. # launchee application. (as the <i>first</i> parameter)
  130. sub getArg {
  131. my $port = $_[0]->{socket}->sockport();
  132. return "--startup-notify-port=$port";
  133. }
  134. ##
  135. # int $StartupNotificationLauncher->getProgress()
  136. # Ensures: 0 <= result <= 100 || result == -1
  137. #
  138. # Returns the launchee's startup progress (in percentage),
  139. # as reported by the launchee.
  140. #
  141. # If the launchee failed to start (that is, it exited before telling
  142. # the launcher that its progress is 100%), then -1 is returned.
  143. # You can use $StartupNotificationLauncher->getError() and
  144. # $StartupNotificationLauncher->getErrno() to
  145. # find out why the launchee failed.
  146. sub getProgress {
  147. my ($self) = @_;
  148. $self->_iterate();
  149. return $self->{progress};
  150. }
  151. ##
  152. # String $StartupNotificationLauncher->getStatus()
  153. #
  154. # Returns the launchee's startup status, as reported
  155. # by the launchee.
  156. #
  157. # If the launchee failed to start (that is, it exited before telling
  158. # the launcher that its progress is 100%), then undef is returned.
  159. # You can use $StartupNotificationLauncher->getError() and
  160. # $StartupNotificationLauncher->getErrno() to
  161. # find out why the launchee failed.
  162. sub getStatus {
  163. my ($self) = @_;
  164. $self->_iterate();
  165. return $self->{status};
  166. }
  167. ##
  168. # String $StartupNotificationLauncher->getError()
  169. # Requires: $self->getProgress() == -1
  170. #
  171. # If the launchee failed to start, then you can use
  172. # this method to retrieve the error message, as reported
  173. # by the launchee.
  174. #
  175. # If the result is undef, then that means the launchee didn't
  176. # send the launcher an error message, or that the launchee
  177. # exited unexpectedly (crash).
  178. sub getError {
  179. return $_[0]->{error};
  180. }
  181. ##
  182. # int $StartupNotificationLauncher->getErrno()
  183. # Requires: $self->getProgress() == -1
  184. #
  185. # If the launchee failed to start, then you can use
  186. # this method to retrieve the error code, as reported
  187. # by the launchee.
  188. #
  189. # If the result is undef, then that means the launchee didn't
  190. # send the launcher an error message, or that the launchee
  191. # exited unexpectedly (crash).
  192. sub getErrno {
  193. return $_[0]->{errno};
  194. }
  195. # Ensures:
  196. #     if $self->{socket} && $self->{client} &&
  197. #     (client progress == 100 || client disconnected):
  198. #         !defined($self->{socket})
  199. #         !defined($self->{client})
  200. #     if client disconnected && client progress != 100:
  201. #         $self->{progress} == -1
  202. #         !defined($self->{status})
  203. sub _iterate {
  204. my ($self) = @_;
  205. return if (!$self->{socket});
  206. if (!$self->{client} && dataWaiting($self->{socket})) {
  207. $self->{client} = $self->{socket}->accept();
  208. }
  209. if (!$self->{client}) {
  210. return;
  211. }
  212. # Invariant: defined($self->{client})
  213. $self->_receiveClientData();
  214. }
  215. # Requires:
  216. #     defined($self->{client}
  217. # Ensures:
  218. #     if client progress == 100 || client disconnected:
  219. #         !defined($self->{socket})
  220. #         !defined($self->{client})
  221. #     if client disconnected && client progress != 100:
  222. #         $self->{progress} == -1
  223. #         !defined($self->{status})
  224. sub _receiveClientData {
  225. my ($self) = @_;
  226. my ($eof, $ID);
  227. while (dataWaiting($self->{client})) {
  228. my $buf;
  229. $self->{client}->recv($buf, 1024 * 32);
  230. if ($buf eq '') {
  231. $eof = 1;
  232. last;
  233. } else {
  234. $self->{buf} .= $buf;
  235. }
  236. }
  237. do {
  238. my %args;
  239. $ID = IPC::Messages::decode($self->{buf}, %args, $self->{buf});
  240. if (defined $ID) {
  241. $self->_processClientMessage($ID, %args);
  242. }
  243. } while (defined $ID);
  244. $self->_clientDisconnected() if ($eof);
  245. }
  246. # Ensures:
  247. #     if client progress == 100:
  248. #         !defined($self->{socket})
  249. #         !defined($self->{client})
  250. sub _processClientMessage {
  251. my ($self, $ID, $args) = @_;
  252. if ($ID eq "progress") {
  253. $self->{progress} = $args->{i};
  254. if ($self->{progress} == 100) {
  255. delete $self->{socket};
  256. delete $self->{client};
  257. }
  258. } elsif ($ID eq "status") {
  259. $self->{status} = $args->{msg};
  260. } elsif ($ID eq "error") {
  261. $self->{error} = $args->{msg};
  262. $self->{errno} = $args->{code};
  263. }
  264. }
  265. # Ensures:
  266. #     !defined($self->{socket})
  267. #     !defined($self->{client})
  268. #     if client disconnected && client progress != 100:
  269. #         $self->{progress} == -1
  270. #         !defined($self->{status})
  271. sub _clientDisconnected {
  272. my ($self) = @_;
  273. if ($self->{progress} != 100) {
  274. $self->{progress} = -1;
  275. $self->{status} = undef;
  276. }
  277. delete $self->{socket};
  278. delete $self->{client};
  279. }
  280. 1;