edited to reflect the moving around of the demo files
[urisagit/Stem.git] / lib / Stem / Inject.pm
1 #  -*- mode: cperl; cperl-indent-level:8; tab-width:8; indent-tabs-mode:t; -*-
2
3 #  File: Stem/Inject.pm
4
5 #  This file is part of Stem.
6 #  Copyright (C) 1999, 2000, 2001 Stem Systems, Inc.
7
8 #  Stem is free software; you can redistribute it and/or modify
9 #  it under the terms of the GNU General Public License as published by
10 #  the Free Software Foundation; either version 2 of the License, or
11 #  (at your option) any later version.
12
13 #  Stem is distributed in the hope that it will be useful,
14 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #  GNU General Public License for more details.
17
18 #  You should have received a copy of the GNU General Public License
19 #  along with Stem; if not, write to the Free Software
20 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
22 #  For a license to use the Stem under conditions other than those
23 #  described here, to purchase support for this software, or to purchase a
24 #  commercial warranty contract, please contact Stem Systems at:
25
26 #       Stem Systems, Inc.              781-643-7504
27 #       79 Everett St.                  info@stemsystems.com
28 #       Arlington, MA 02474
29 #       USA
30
31 #######################################################
32
33 package Stem::Inject ;
34
35 use strict ;
36
37 use IO::Socket ;
38
39 use Stem::Msg ;
40 use Stem::Packet ;
41
42 my $attr_spec = [
43
44         {
45                 'name'          => 'host',
46                 'required'      => 1,
47                 'help'          => <<HELP,
48 The hostname to use when connecting to the portal.
49 HELP
50         },
51
52         {
53                 'name'          => 'port',
54                 'required'      => 1,
55                 'help'          => <<HELP,
56 The port to use when connecting to the portal.
57 HELP
58         },
59
60         {
61                 'name'          => 'to',
62                 'required'      => 1,
63                 'help'          => <<HELP,
64 The cell to which the message is addressed.
65 HELP
66         },
67
68         {
69                 'name'          => 'type',
70                 'required'      => 1,
71                 'help'          => <<HELP,
72 This is the type of the message. It is used to select the delivery method in
73 the addressed Cell.
74 HELP
75         },
76
77         {
78                 'name'          => 'cmd',
79                 'help'          => <<HELP,
80 This is used for the delivery method if the message type is 'cmd'.
81 HELP
82         },
83
84         {
85                 'name'          => 'codec',
86                 'help'          => <<HELP,
87 The Stem::Codec module to use when creating packets.
88 HELP
89         },
90
91         {
92                 'name'          => 'data',
93                 'help'          => <<HELP,
94 This is the data the message is carrying. It should (almost) always be
95 a reference.
96 HELP
97         },
98
99         {
100                 'name'          => 'timeout',
101                 'default'       => 60,
102                 'help'          => <<HELP,
103 The timeout before giving up on getting a reply from the portal, in
104 seconds.  Defaults to 60.
105 HELP
106         },
107
108         {
109                 'name'          => 'wait_for_reply',
110                 'default'       => 1,
111                 'help'          => <<HELP,
112 Indicates whether or not a reply is expected.  Defaults to true.
113 HELP
114         },
115
116 ] ;
117
118 sub inject {
119
120         my $class = shift ;
121
122         my $self = Stem::Class::parse_args( $attr_spec, @_ ) ;
123         return $self unless ref $self ;
124
125         $self->{'from'} = "Stem::Inject:inject$$";
126
127         $self->{'packet'} =
128             Stem::Packet->new( codec => $self->{'codec'} ) ;
129
130         local $SIG{'ALRM'} = sub { die 'Read or write to socket timed out' };
131
132         my $result;
133
134         eval {
135
136                 my $address = "$self->{'host'}:$self->{'port'}";
137                 $self->{'sock'} = IO::Socket::INET->new($address) ;
138                 $self->{'sock'} or die "can't connect to $address\n" ;
139
140                 alarm $self->{'timeout'} if $self->{'timeout'} ;
141
142                 $self->_register() ;
143
144                 $result = $self->_inject_msg() ;
145         } ;
146
147         alarm 0 ;
148
149         return $@ if $@ ;
150
151         return unless $self->{'wait_for_reply'};
152
153         return $result ;
154 }
155
156 sub _register {
157
158         my( $self, $data ) = @_ ;
159
160         my $reg_msg =
161             Stem::Msg->new( from => $self->{'from'},
162                             type => 'register',
163                           ) ;
164
165         die $reg_msg unless ref $reg_msg ;
166
167         my $reg = $self->{'packet'}->to_packet($reg_msg) ;
168
169         my $written = syswrite( $self->{'sock'}, $$reg ) ;
170         defined $written or die "can't write to socket\n" ;
171
172         my $read_buf ;
173         while (1) {
174
175                 my $bytes_read = sysread( $self->{'sock'}, $read_buf, 8192 ) ;
176
177                 defined $bytes_read or die "can't read from socket" ;
178                 last if $bytes_read == 0 ;
179
180                 my $data = $self->{'packet'}->to_data( $read_buf ) ;
181
182                 last;
183         }
184 }
185
186 sub _inject_msg {
187
188         my( $self ) = @_;
189
190         my %msg_p =
191             ( 'to'   => $self->{'to'},
192               'from' => $self->{'from'},
193               'type' => $self->{'type'},
194             ) ;
195
196         $msg_p{'cmd'}  = $self->{'cmd'} if $self->{'type'} eq 'cmd';
197         $msg_p{'data'} = $self->{'data'},
198
199         my $data_msg = Stem::Msg->new(%msg_p) ;
200
201         die $data_msg unless ref $data_msg ;
202
203         my $data = $self->{'packet'}->to_packet($data_msg) ;
204
205         my $written = syswrite( $self->{'sock'}, $$data ) ;
206         defined $written or die "can't write to socket\n" ;
207
208         return unless $self->{'wait_for_reply'};
209
210         my $read_buf ;
211         while (1) {
212
213                 my $bytes_read = sysread( $self->{'sock'}, $read_buf, 8192 ) ;
214
215                 defined $bytes_read or die "can't read from socket" ;
216                 last if $bytes_read == 0 ;
217
218                 my $reply = $self->{'packet'}->to_data( $read_buf ) ;
219
220                 return $reply->data ;
221         }
222 }
223
224 1 ;
225
226 __END__
227
228 =pod
229
230 =head1 NAME
231
232 Stem::Inject - Inject a message into a portal via a socket connection
233
234 =head1 SYNOPSIS
235
236   my $return =
237       Stem::Inject->inject( to   => 'some_cell',
238                             type => 'do_something',
239                             port => 10200,
240                             host => 'localhost',
241                             data => { foo => 1 },
242                           );
243
244   # do something with data returned
245
246 =head1 USAGE
247
248 This class contains just one method, C<inject>, which can be used to
249 inject a single message into a Stem hub, via a known server portal.
250
251 This is very useful if you have a synchronous system which needs to
252 communicate with a Stem system via messages.
253
254 =head1 METHODS
255
256 =over 4
257
258 =item * inject
259
260 This method is the sole interface provided by this class.  It accepts
261 the following parameters:
262
263 =over 8
264
265 =item * host (required)
266
267 This parameter specifies the host with which to connect.
268
269 =item * port (required)
270
271 The port with which to connect on the specified host.
272
273 =item * to (required)
274
275 The address of the cell to which the message should be delivered.
276
277 =item * type (required)
278
279 The type of the message to be delivered.
280
281 =item * cmd
282
283 The cmd being given.  This is only needed if the message's type is
284 "cmd".
285
286 =item * data
287
288 The data that the message will carry, if any.
289
290 =item * codec
291
292 The codec to be used when communicating with the port.  This defaults
293 to "Data::Dumper", but be careful to set this to whatever value the
294 receiving port is using.
295
296 =item * timeout (defaults to 60)
297
298 The amount of time, in seconds, before giving up on message delivery
299 or reply.  This is the I<total> amount of time allowed for message
300 delivery and receiving a reply.
301
302 =item * wait_for_reply (defaults to true)
303
304 If this is true then the C<inject> method will expect a reply to the
305 message it delivers.  If it doesn't receive one this will be
306 considered an error.
307
308 =back
309
310 If there is an error in trying to inject a message, either with the
311 parameters given, or with the socket connection, then this method will
312 return an error string.
313
314 If no reply was expected, this method simply returns false.
315 Otherwise, it returns the reply message's data, which will always be a
316 reference.
317
318 =back
319
320 =head1 AUTHOR
321
322 Dave Rolsky <david@stemsystems.com>
323
324 =cut