Commit | Line | Data |
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 | |
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 |