package OpenSRF::Transport; use strict; use warnings; use base 'OpenSRF'; use Time::HiRes qw/time/; use OpenSRF::AppSession; use OpenSRF::Utils::JSON; use OpenSRF::Utils::Logger qw(:level); use OpenSRF::DomainObject::oilsResponse qw/:status/; use OpenSRF::EX qw/:try/; use OpenSRF::Transport::SlimJabber::MessageWrapper; #------------------ # --- These must be implemented by all Transport subclasses # ------------------------------------------- =head2 get_listener Returns the package name of the package the system will use to gather incoming requests =cut sub get_listener { shift()->alert_abstract(); } =head2 get_peer_client Returns the name of the package responsible for client communication =cut sub get_peer_client { shift()->alert_abstract(); } =head2 get_msg_envelope Returns the name of the package responsible for parsing incoming messages =cut sub get_msg_envelope { shift()->alert_abstract(); } # ------------------------------------------- our $message_envelope; my $logger = "OpenSRF::Utils::Logger"; =head2 message_envelope( [$envelope] ); Sets the message envelope class that will allow us to extract information from the messages we receive from the low level transport =cut sub message_envelope { my( $class, $envelope ) = @_; if( $envelope ) { $message_envelope = $envelope; $envelope->use; if( $@ ) { $logger->error( "Error loading message_envelope: $envelope -> $@", ERROR); } } return $message_envelope; } =head2 handler( $data ) Creates a new MessageWrapper, extracts the remote_id, session_id, and message body from the message. Then, creates or retrieves the AppSession object with the session_id and remote_id. Finally, creates the message document from the body of the message and calls the handler method on the message document. =cut sub handler { my $start_time = time(); my( $class, $service, $data ) = @_; $logger->transport( "Transport handler() received $data", INTERNAL ); # pass data to the message envelope my $helper = OpenSRF::Transport::SlimJabber::MessageWrapper->new( $data ); # Extract message information my $remote_id = $helper->get_remote_id(); my $sess_id = $helper->get_sess_id(); my $body = $helper->get_body(); my $type = $helper->get_msg_type(); $logger->set_osrf_xid($helper->get_osrf_xid); if (defined($type) and $type eq 'error') { throw OpenSRF::EX::Session ("$remote_id IS NOT CONNECTED TO THE NETWORK!!!"); } # See if the app_session already exists. If so, make # sure the sender hasn't changed if we're a server my $app_session = OpenSRF::AppSession->find( $sess_id ); if( $app_session and $app_session->endpoint == $app_session->SERVER() and $app_session->remote_id ne $remote_id ) { $logger->transport( "Backend Gone or invalid sender", INTERNAL ); my $res = OpenSRF::DomainObject::oilsBrokenSession->new(); $res->status( "Backend Gone or invalid sender, Reconnect" ); $app_session->status( $res ); return 1; } # Retrieve or build the app_session as appropriate (server_build decides which to do) $logger->transport( "AppSession is valid or does not exist yet", INTERNAL ); $app_session = OpenSRF::AppSession->server_build( $sess_id, $remote_id, $service ); if( ! $app_session ) { throw OpenSRF::EX::Session ("Transport::handler(): No AppSession object returned from server_build()"); } # Create a document from the JSON contained within the message my $doc; eval { $doc = OpenSRF::Utils::JSON->JSON2perl($body); }; if( $@ ) { $logger->transport( "Received bogus JSON: $@", INFO ); $logger->transport( "Bogus JSON data: \n $body \n", INTERNAL ); my $res = OpenSRF::DomainObject::oilsXMLParseError->new( status => "JSON Parse Error --- $body\n\n$@" ); $app_session->status($res); #$app_session->kill_me; return 1; } $logger->transport( "Transport::handler() creating \n$body", INTERNAL ); # We need to disconnect the session if we got a jabber error on the client side. For # server side, we'll just tear down the session and go away. if (defined($type) and $type eq 'error') { # If we're a server if( $app_session->endpoint == $app_session->SERVER() ) { $app_session->kill_me; return 1; } else { $app_session->reset; $app_session->state( $app_session->DISCONNECTED ); # below will lead to infinite looping, should return an exception #$app_session->push_resend( $app_session->app_request( # $doc->documentElement->firstChild->threadTrace ) ); $logger->debug( "Got Jabber error on client connection $remote_id, nothing we can do..", ERROR ); return 1; } } # cycle through and pass each oilsMessage contained in the message # up to the message layer for processing. for my $msg (@$doc) { next unless ( $msg && UNIVERSAL::isa($msg => 'OpenSRF::DomainObject::oilsMessage')); if( $app_session->endpoint == $app_session->SERVER() ) { try { if( ! $msg->handler( $app_session ) ) { return 0; } $logger->debug("Successfully handled message", DEBUG); } catch Error with { my $e = shift; my $res = OpenSRF::DomainObject::oilsServerError->new(); $res->status( $res->status . "\n$e"); $app_session->status($res) if $res; $app_session->kill_me; return 0; }; } else { if( ! $msg->handler( $app_session ) ) { return 0; } $logger->info("Successfully handled message", DEBUG); } } $logger->debug(sprintf("Message processing duration: %.3fs",(time() - $start_time)), DEBUG); return $app_session; } 1;