]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/perlmods/OpenSRF/DomainObject/oilsMessage.pm
moving from XML to JSON in the core messaging
[OpenSRF.git] / src / perlmods / OpenSRF / DomainObject / oilsMessage.pm
1 package OpenSRF::DomainObject::oilsMessage;
2 use JSON;
3 use OpenSRF::AppSession;
4 use OpenSRF::DomainObject::oilsResponse qw/:status/;
5 use OpenSRF::Utils::Logger qw/:level/;
6 use warnings; use strict;
7 use OpenSRF::EX qw/:try/;
8
9 JSON->register_class_hint(hint => 'osrfMessage', class => 'OpenSRF::DomainObject::oilsMessage');
10
11 sub toString {
12         my $self = shift;
13         my $pretty = shift;
14         return JSON->perl2prettyJSON($self) if ($pretty);
15         return JSON->perl2JSON($self);
16 }
17
18
19 =head1 NAME
20
21 OpenSRF::DomainObject::oilsMessage
22
23 =head1
24
25 use OpenSRF::DomainObject::oilsMessage;
26
27 my $msg = OpenSRF::DomainObject::oilsMessage->new( type => 'CONNECT' );
28
29 $msg->payload( $domain_object );
30
31 =head1 ABSTRACT
32
33 OpenSRF::DomainObject::oilsMessage is used internally to wrap data sent
34 between client and server.  It provides the structure needed to authenticate
35 session data, and also provides the logic needed to unwrap session data and 
36 pass this information along to the Application Layer.
37
38 =cut
39
40 my $log = 'OpenSRF::Utils::Logger';
41
42 =head1 METHODS
43
44 =head2 OpenSRF::DomainObject::oilsMessage->type( [$new_type] )
45
46 =over 4
47
48 Used to specify the type of message.  One of
49 B<CONNECT, REQUEST, RESULT, STATUS, ERROR, or DISCONNECT>.
50
51 =back
52
53 =cut
54
55 sub type {
56         my $self = shift;
57         my $val = shift;
58         $self->{type} = $val if (defined $val);
59         return $self->{type};
60 }
61
62 =head2 OpenSRF::DomainObject::oilsMessage->api_level( [$new_api_level] )
63
64 =over 4
65
66 Used to specify the api_level of message.  Currently, only api_level C<1> is
67 supported.  This will be used to check that messages are well-formed, and as
68 a hint to the Application as to which version of a method should fulfill a
69 REQUEST message.
70
71 =back
72
73 =cut
74
75 sub api_level {
76         my $self = shift;
77         my $val = shift;
78         $self->{api_level} = $val if (defined $val);
79         return $self->{api_level};
80 }
81
82 =head2 OpenSRF::DomainObject::oilsMessage->threadTrace( [$new_threadTrace] );
83
84 =over 4
85
86 Sets or gets the current message sequence identifier, or thread trace number,
87 for a message.  Useful as a debugging aid, but that's about it.
88
89 =back
90
91 =cut
92
93 sub threadTrace {
94         my $self = shift;
95         my $val = shift;
96         $self->{threadTrace} = $val if (defined $val);
97         return $self->{threadTrace};
98 }
99
100 =head2 OpenSRF::DomainObject::oilsMessage->update_threadTrace
101
102 =over 4
103
104 Increments the threadTrace component of a message.  This is automatic when
105 using the normal session processing stack.
106
107 =back
108
109 =cut
110
111 sub update_threadTrace {
112         my $self = shift;
113         my $tT = $self->threadTrace;
114
115         $tT ||= 0;
116         $tT++;
117
118         $log->debug("Setting threadTrace to $tT",DEBUG);
119
120         $self->threadTrace($tT);
121
122         return $tT;
123 }
124
125 =head2 OpenSRF::DomainObject::oilsMessage->payload( [$new_payload] )
126
127 =over 4
128
129 Sets or gets the payload of a message.  This should be exactly one object
130 of (sub)type domainObject or domainObjectCollection.
131
132 =back
133
134 =cut
135
136 sub payload {
137         my $self = shift;
138         my $val = shift;
139         $self->{payload} = $val if (defined $val);
140         return $self->{payload};
141 }
142
143 =head2 OpenSRF::DomainObject::oilsMessage->handler( $session_id )
144
145 =over 4
146
147 Used by the message processing stack to set session state information from the current
148 message, and then sends control (via the payload) to the Application layer.
149
150 =back
151
152 =cut
153
154 sub handler {
155         my $self = shift;
156         my $session = shift;
157
158         my $mtype = $self->type;
159         my $api_level = $self->api_level || 1;;
160         my $tT = $self->threadTrace;
161
162         $session->last_message_type($mtype);
163         $session->last_message_api_level($api_level);
164         $session->last_threadTrace($tT);
165
166         $log->debug(" Received api_level => [$api_level], MType => [$mtype], ".
167                         "from [".$session->remote_id."], threadTrace[".$self->threadTrace."]", INFO);
168         $log->debug("endpoint => [".$session->endpoint."]", DEBUG);
169         $log->debug("OpenSRF::AppSession->SERVER => [".$session->SERVER()."]", DEBUG);
170
171
172         my $val;
173         if ( $session->endpoint == $session->SERVER() ) {
174                 $val = $self->do_server( $session, $mtype, $api_level, $tT );
175
176         } elsif ($session->endpoint == $session->CLIENT()) {
177                 $val = $self->do_client( $session, $mtype, $api_level, $tT );
178         }
179
180         if( $val ) {
181                 $log->debug("Passing request up to OpenSRF::Application", DEBUG);
182                 return OpenSRF::Application->handler($session, $self->payload);
183         } else {
184                 $log->debug("Request was handled internally", DEBUG);
185         }
186
187         return 1;
188
189 }
190
191
192
193 # handle server side message processing
194
195 # !!! Returning 0 means that we don't want to pass ourselves up to the message layer !!!
196 sub do_server {
197         my( $self, $session, $mtype, $api_level, $tT ) = @_;
198
199         # A Server should never receive STATUS messages.  If so, we drop them.
200         # This is to keep STATUS's from dead client sessions from creating new server
201         # sessions which send mangled session exceptions to backends for messages 
202         # that they are not aware of any more.
203         if( $mtype eq 'STATUS' ) { return 0; }
204
205         
206         if ($mtype eq 'DISCONNECT') {
207                 $session->disconnect;
208                 $session->kill_me;
209                 return 0;
210         }
211
212         if ($session->state == $session->CONNECTING()) {
213
214                 if($mtype ne "CONNECT" and $session->stateless) {
215                         $log->debug("Got message Stateless", DEBUG);
216                         return 1; #pass the message up the stack
217                 }
218
219                 # the transport layer thinks this is a new connection. is it?
220                 unless ($mtype eq 'CONNECT') {
221                         $log->error("Connection seems to be mangled: Got $mtype instead of CONNECT");
222
223                         my $res = OpenSRF::DomainObject::oilsBrokenSession->new(
224                                         status => "Connection seems to be mangled: Got $mtype instead of CONNECT",
225                         );
226
227                         $session->status($res);
228                         $session->kill_me;
229                         return 0;
230
231                 }
232                 
233                 my $res = OpenSRF::DomainObject::oilsConnectStatus->new;
234                 $session->status($res);
235                 $session->state( $session->CONNECTED );
236
237                 return 0;
238         }
239
240
241         $log->debug("Passing to Application::handler()", INFO);
242         $log->debug($self->toString(1), DEBUG);
243
244         return 1;
245
246 }
247
248
249 # Handle client side message processing. Return 1 when the the message should be pushed
250 # up to the application layer.  return 0 otherwise.
251 sub do_client {
252
253         my( $self, $session , $mtype, $api_level, $tT) = @_;
254
255
256         if ($mtype eq 'STATUS') {
257
258                 if ($self->payload->statusCode == STATUS_OK) {
259                         $session->state($session->CONNECTED);
260                         $log->debug("We connected successfully to ".$session->app, INFO);
261                         return 0;
262                 }
263
264                 if ($self->payload->statusCode == STATUS_TIMEOUT) {
265                         $session->state( $session->DISCONNECTED );
266                         $session->reset;
267                         $session->connect;
268                         $session->push_resend( $session->app_request($self->threadTrace) );
269                         $log->debug("Disconnected because of timeout", WARN);
270                         return 0;
271
272                 } elsif ($self->payload->statusCode == STATUS_REDIRECTED) {
273                         $session->state( $session->DISCONNECTED );
274                         $session->reset;
275                         $session->connect;
276                         $session->push_resend( $session->app_request($self->threadTrace) );
277                         $log->debug("Disconnected because of redirect", WARN);
278                         return 0;
279
280                 } elsif ($self->payload->statusCode == STATUS_EXPFAILED) {
281                         $session->state( $session->DISCONNECTED );
282                         $log->debug("Disconnected because of mangled session", WARN);
283                         $session->reset;
284                         $session->push_resend( $session->app_request($self->threadTrace) );
285                         return 0;
286
287                 } elsif ($self->payload->statusCode == STATUS_CONTINUE) {
288                         $session->reset_request_timeout($self->threadTrace);
289                         return 0;
290
291                 } elsif ($self->payload->statusCode == STATUS_COMPLETE) {
292                         my $req = $session->app_request($self->threadTrace);
293                         $req->complete(1) if ($req);
294                         return 0;
295                 }
296
297                 # add more STATUS handling code here (as 'elsif's), for Message layer status stuff
298
299         } elsif ($session->state == $session->CONNECTING()) {
300                 # This should be changed to check the type of response (is it a connectException?, etc.)
301         }
302
303         if( $self->payload and $self->payload->isa( "OpenSRF::EX" ) ) { 
304                 $self->payload->throw();
305         }
306
307         if( $self->payload ) {
308                 $log->debug("Passing to OpenSRF::Application::handler()\n" . $self->payload->toString(1), INTERNAL);
309         }
310         $log->debug("oilsMessage passing to Application: " . $self->type." : ".$session->remote_id, INFO );
311
312         return 1;
313
314 }
315
316 1;