edited to reflect the moving around of the demo files
[urisagit/Stem.git] / lib / Stem / Inject.pm
CommitLineData
4536f655 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
33package Stem::Inject ;
34
35use strict ;
36
37use IO::Socket ;
38
39use Stem::Msg ;
40use Stem::Packet ;
41
42my $attr_spec = [
43
44 {
45 'name' => 'host',
46 'required' => 1,
47 'help' => <<HELP,
48The hostname to use when connecting to the portal.
49HELP
50 },
51
52 {
53 'name' => 'port',
54 'required' => 1,
55 'help' => <<HELP,
56The port to use when connecting to the portal.
57HELP
58 },
59
60 {
61 'name' => 'to',
62 'required' => 1,
63 'help' => <<HELP,
64The cell to which the message is addressed.
65HELP
66 },
67
68 {
69 'name' => 'type',
70 'required' => 1,
71 'help' => <<HELP,
72This is the type of the message. It is used to select the delivery method in
73the addressed Cell.
74HELP
75 },
76
77 {
78 'name' => 'cmd',
79 'help' => <<HELP,
80This is used for the delivery method if the message type is 'cmd'.
81HELP
82 },
83
84 {
85 'name' => 'codec',
86 'help' => <<HELP,
87The Stem::Codec module to use when creating packets.
88HELP
89 },
90
91 {
92 'name' => 'data',
93 'help' => <<HELP,
94This is the data the message is carrying. It should (almost) always be
95a reference.
96HELP
97 },
98
99 {
100 'name' => 'timeout',
101 'default' => 60,
102 'help' => <<HELP,
103The timeout before giving up on getting a reply from the portal, in
104seconds. Defaults to 60.
105HELP
106 },
107
108 {
109 'name' => 'wait_for_reply',
110 'default' => 1,
111 'help' => <<HELP,
112Indicates whether or not a reply is expected. Defaults to true.
113HELP
114 },
115
116] ;
117
118sub 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
156sub _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
186sub _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
2241 ;
225
226__END__
227
228=pod
229
230=head1 NAME
231
232Stem::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
248This class contains just one method, C<inject>, which can be used to
249inject a single message into a Stem hub, via a known server portal.
250
251This is very useful if you have a synchronous system which needs to
252communicate with a Stem system via messages.
253
254=head1 METHODS
255
256=over 4
257
258=item * inject
259
260This method is the sole interface provided by this class. It accepts
261the following parameters:
262
263=over 8
264
265=item * host (required)
266
267This parameter specifies the host with which to connect.
268
269=item * port (required)
270
271The port with which to connect on the specified host.
272
273=item * to (required)
274
275The address of the cell to which the message should be delivered.
276
277=item * type (required)
278
279The type of the message to be delivered.
280
281=item * cmd
282
283The cmd being given. This is only needed if the message's type is
284"cmd".
285
286=item * data
287
288The data that the message will carry, if any.
289
290=item * codec
291
292The codec to be used when communicating with the port. This defaults
293to "Data::Dumper", but be careful to set this to whatever value the
294receiving port is using.
295
296=item * timeout (defaults to 60)
297
298The amount of time, in seconds, before giving up on message delivery
299or reply. This is the I<total> amount of time allowed for message
300delivery and receiving a reply.
301
302=item * wait_for_reply (defaults to true)
303
304If this is true then the C<inject> method will expect a reply to the
305message it delivers. If it doesn't receive one this will be
306considered an error.
307
308=back
309
310If there is an error in trying to inject a message, either with the
311parameters given, or with the socket connection, then this method will
312return an error string.
313
314If no reply was expected, this method simply returns false.
315Otherwise, it returns the reply message's data, which will always be a
316reference.
317
318=back
319
320=head1 AUTHOR
321
322Dave Rolsky <david@stemsystems.com>
323
324=cut