Connectionイベントハンドラ

ユーザーコネクションに関するイベントが呼ばれます。コネクションへのJIDのバインドや、コネクションの切断、コネクションの状態であるプレゼンスに関するイベントはこのクラスに実装していきます。

プロトコルガイドのログインからログアウトまでのケーススタディにおけるリソースバインディングイニシャルプレゼンスログアウト のそれぞれのセクションを参照するとよいでしょう。


on_bind_request

リソースバインディング要求に対する処理を、このイベントハンドラで実装します。
リソースバインディングについて詳しくは、ログインからログアウトまでのケーススタディ [ リソースバインディング ]を参照するとよいでしょう。

SYNOPSIS
sub on_bind_request {
    my ($self, $ctx, $args) = @_;

    my $stream_id = $args->stream_id;
    my $user_id   = $args->user_id;

    $self->log_debug("on_bind_request");

    my $user = $ctx->get('db')->find_user_by_id($user_id);
    unless ($user) {
        $self->log_debug("user not found");
        return;
    }

    my $resource = $args->resource;
    unless ($resource) {
        Ocean::Error::ProtocolError->throw(
            message => q{resource not found}, 
        );
    }

    my $jid = Ocean::JID->build(
        $user->username, 
        $self->domain, 
        $resource
    );

    $ctx->get('db')->insert_connection(
        user_id  => $user->user_id,
        username => $user->username,
        resource => $resource,
        #presence => undef,
    );

    my $builder = 
        Ocean::Stanza::DeliveryRequestBuilder::BoundJID->new;
    $builder->stream_id($stream_id);
    $builder->jid($jid);
    if ($args->want_extval) {
        $builder->nickname($user->nickname);
        $builder->photo_url($user->profile_img_url || '');
    }
    $ctx->deliver($builder->build());
}

引数としてOcean::HandlerArgs::BindRequestを受け取ります。このオブジェクトは次のアクセサを持ちます。

アクセサ名 概要
stream_id要求を送信してきたストリームのIDです
user_id認証成功時に返したユーザーIDです
resource認証成功時に発行したsession_idです
want_extval拡張仕様を要求するかどうか。要求された場合、リソースバインディング成功時に、JIDだけでなく、ニックネームと画像URLも含めて返します。

Ocean::Stanza::DeliveryRequestBuilder::BoundJID

セッター名 概要
stream_id認証を要求してきたストリームのID
jid割り当てられたJID
nickname
photo_url

on_presence

ログイン後のプレゼンス配信はこのハンドラで行われます。

SYNOPSIS
sub on_presence {
    my ($self, $ctx, $args) = @_;

    my $sender_jid = $args->from;

    my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );

    my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );

    $sender_conn->update({ 
        presence_show   => $args->show,
        presence_status => $args->status,
    });

    my @followers = $ctx->get('db')->search_followers_of($sender);

    for my $follower_id ( @followers ) {

        my @follower_conns = 
            $ctx->get('db')->search_available_connection_by_user_id($follower_id);

        for my $follower_conn ( @follower_conns ) {

            my $receiver_jid = Ocean::JID->build(
                $follower_conn->username,
                $self->domain,
                $follower_conn->resource,
            );

            my $builder =
                Ocean::Stanza::DeliveryRequestBuilder::Presence->new;
            $builder->from($sender_jid);
            $builder->to($receiver_jid);
            $builder->show($args->show);
            $builder->status($args->status);
            $builder->image_hash($sender->profile_img_hash)
                if $sender->profile_img_hash;

            $ctx->deliver($builder->build());

        }

    }
}

引数としてOcean::HandlerArgs::Presenceを受け取ります。このオブジェクトは次のアクセサを持ちます。

アクセサ名 概要
from送信者のFull JIDです
show要求されたshowタイプです
status要求されたステータステキストです

Ocean::Stanza::DeliveryRequestBuilder::Presence

セッター名 概要
from
to
show
status
image_hash

on_initial_presence

ログイン処理の最後、イニシャルプレゼンスに対する処理がこの中で実装されます。

イニシャルプレゼンスについて詳しくは、ログインからログアウトまでのケーススタディ [ イニシャルプレゼンス ]を参照するとよいでしょう。

SYNOPSIS
sub on_initial_presence {
    my ($self, $ctx, $args) = @_;

    my $sender_jid = $args->from;
    my $no_probe   = $args->no_probe;

    $self->log_debug("on_initial_presence");

    my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );

    # UPDATE CONNECTION STATE
    my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );
    unless ($sender_conn) {
        $self->log_warn("connection for sender_jid %s not found", $sender_jid->as_string);
        return;
    }
    $sender_conn->update({ 
        presence_show   => $args->show,
        presence_status => $args->status,
    });

    $self->log_debug("broadcast presence");

    # send presence to follower
    my @followers = $ctx->get('db')->search_followers_of($sender);

    for my $follower_id ( @followers ) {

        my @follower_conns = 
            $ctx->get('db')->search_available_connection_by_user_id($follower_id);

        for my $follower_conn ( @follower_conns ) {

            my $receiver_jid = Ocean::JID->build(
                $follower_conn->username,
                $self->domain, 
                $follower_conn->resource,
            );

            $self->log_debug("deliver presence to %s", 
                $receiver_jid->as_string);

            my $builder =
                Ocean::Stanza::DeliveryRequestBuilder::Presence->new;
            $builder->from($sender_jid);
            $builder->to($receiver_jid);
            $builder->show($args->show);
            $builder->status($args->status);
            $builder->image_hash($sender->profile_img_hash)
                if $sender->profile_img_hash;

            $ctx->deliver($builder->build());

        }

    }

    return if $no_probe;

    $self->log_debug("probe presence");

    my @followees = $ctx->get('db')->search_followees_of($sender);

    for my $followee_id ( @followees ) {

        my @followee_conns = 
            $ctx->get('db')->search_available_connection_by_user_id($followee_id);

        for my $followee_conn ( @followee_conns ) {

            my $followee_presence = $followee_conn->presence;

            my $followee_jid = Ocean::JID->build(
                  $followee_conn->username,
                  $self->domain, 
                  $followee_conn->resource,
            );

            my $followee = $ctx->get('db')->find_user_by_username( $followee_jid->node );

            my $builder = 
                Ocean::Stanza::DeliveryRequestBuilder::Presence->new;
            $builder->from($followee_jid);
            $builder->to($sender_jid);
            $builder->show($followee_presence->show);
            $builder->status($followee_presence->status);
            $builder->image_hash($followee->profile_img_hash)
                if $followee->profile_img_hash;

            $ctx->deliver($builder->build());

        }

    }
}

引数としてOcean::HandlerArgs::Presenceを受け取ります。このオブジェクトは次のアクセサを持ちます。

アクセサ名 概要
from送信者のFull JIDです
show要求されたshowタイプです
status要求されたステータステキストです
no_probeprobeを実行するかどうかのフラグです。

on_unavailable_presence

ストリーム切断時に呼ばれます。すでに有効状態になっているストリームだった場合、このイベントハンドラが呼ばれます。そうでなかった場合は、下のon_silent_disconnectionが呼ばれます。このイベントハンドラでは、ストリームの切断に伴う後処理を適切に行うと同時に、このユーザーのストリームがオフラインになったことを、ユーザーのフォロワーのうちオンラインのストリーム全てに対して通知しなければなりません。

ログアウトについて詳しくは、ログインからログアウトまでのケーススタディ [ ログアウト ]を参照するとよいでしょう。

次の実装例を確認すると、まずコネクション情報の削除、それからフォロワーへの通知を行っていることがわかると思います。

SYNOPSIS
sub on_unavailable_presence {
    my ($self, $ctx, $args) = @_;

    my $sender_jid = $args->from;

    my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );

    my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );
    $sender_conn->delete() if $sender_conn;

    # send presence to follower
    my @followers = $ctx->get('db')->search_followers_of($sender);

    for my $follower_id ( @followers ) {

        my @follower_conns = 
            $ctx->get('db')->search_available_connection_by_user_id($follower_id);

        for my $follower_conn ( @follower_conns ) {

            my $receiver_jid = Ocean::JID->build(
                $follower_conn->username,
                $self->domain,
                $follower_conn->resource,
            );

            my $builder =
                Ocean::Stanza::DeliveryRequestBuilder::UnavailablePresence->new;
            $builder->from($sender_jid);
            $builder->to($receiver_jid);

            $ctx->deliver($builder->build());
        }
    }
}

ここでは一般的な処理の例のみにとどまっていますが、たとえばグループチャットをサポートしていた場合は、部屋からの退出処理などをここに追加しなければなりません。

引数としてOcean::HandlerArgs::UnavailablePresenceを受け取ります。このオブジェクトは次のアクセサを持ちます。

アクセサ名 概要
from送信者のFull JID

Ocean::Stanza::DeliveryRequestBuilder::UnavailablePresence

セッター名 概要
from送信者のFull JID
to宛先のFull JID

on_silent_disconnection

ストリーム切断時に呼ばれます。まだ有効状態になっていないストリームが切断された場合に、このイベントハンドラが呼ばれます。ストリームが有効状態になっていたら、このイベントハンドラでなく、上のon_unavailable_presenceが呼ばれます。このイベントハンドラでは、ストリームの切断に伴う後処理のみを行い、フォロワーに対する通知などは行いません。

次の実装例を見ると、on_unavailable_presenceのコードから、フォロワーへの通知をしているパートを抜いただけになっているのが分かると思います。

SYNOPSIS
sub on_silent_disconnection {
    my ($self, $ctx, $args) = @_;

    my $sender_jid = $args->from;

    my $sender = $ctx->get('db')->find_user_by_username( $sender_jid->node );

    my $sender_conn = $ctx->get('db')->find_connection_by_jid( $sender_jid );
    $sender_conn->delete() if $sender_conn;
}

引数としてOcean::HandlerArgs::SilentDisconnectionを受け取ります。このオブジェクトは次のアクセサを持ちます。

アクセサ名 概要
from送信者のFull JIDです

配送処理は必要ないので、対応する配送リクエストビルダーもありません。