D.4 Descrição dos tipos de stanzas <iq/>
5.9 Exemplo de envio de um elemento <features/> para o cliente
5.2.3
Leitura de elementos XMPP
Às classes derivadas de XmppReader basta-lhes implementar o método BuildInstance. Apenas foram criadas classes para realizar o parse dos elementos que um cliente XMPP pode enviar. Estas classes estão divididas em duas categorias, as fábricas que realizam parse a elementos de negociação do stream e as fábricas que realizam parse aos stanzas e seus elementos associados. Todas estas classes são apresentadas na figura 5.2. Na primeira cat- egoria estão as classes: AuthFactory, BindFactory, ResponseFactory, SessionFactory e StartTlsFactory. Na segunda categoria estão as classes: BodyFactory, IqFactory, Mes- sageFactory, PresenceFactory, PriorityFactory, QueryFactory, ShowFactory, Status- Factory, SubjectFactory e ThreadFactory. Pelos próprios nomes é possível verificar quais os elementos a que realizam o parse.
5.2.4
Data Transfer Objects
As classes criadas para transferir a informação obtida da base de dados para quem a requisitou foram User e Contact. Estas classes contêm, respectivamente, a informação de um utilizador (para autenticação) e a informação respectiva a um item de um roster.
É possível verificar que não existem DTOs para obter a informação de mensagens offline ou stanzas para subscrição de presença. A informação obtida é directamente retornada em objectos das classes que representam os respectivos stanzas. Tendo em conta que essa infor- mação é obtida para ser entregue aos clientes, é imediatamente obtida no formato ideal para o realizar.
5.2.5
Objectos de suporte ao stream
Para auxiliar o funcionamento do servidor, tal como já foi indicado anteriormente, foram criados duas classes, Connection e Session, que são descritas em seguida.
• Connection: tem como objectivo auxiliar na escrita e leitura do stream. Funciona como um wrapper sobre o stream .NET;
• Session: representa uma sessão, com toda a informação que lhe é inerente, como por exemplo username e recurso associado. É construída ao longo da negociação do stream.
5.3
Aplicação Web para registo
Antes que um cliente se possa ligar ao bus de instant messaging, o utilizador deve registar uma conta.
Existe uma extensão para o protocolo XMPP que define uma forma de registo de um utilizador XMPP, antes que este se autentique, a XEP-0077, denominada de ”In-Band Regis- tration”.
A extensão XEP-0077 não será implementada por duas razões. Em primeiro lugar a implementação completa do protocolo XMPP não é o objectivo principal do trabalho, em segundo lugar, este tipo de registo não permite que utilizadores não XMPP efectuem o seu registo.
Com o objectivo de criar uma forma única de registo, foi criada uma pequena aplicação Web, que permite que todos os utilizadores dos serviços suportados se registem no bus de instant messaging. A figura 5.3 apresenta uma visualização da página Web desenvolvida.
Capítulo 6
Servidor XMPP
Este capítulo apresenta pormenores de implementação das principais funcionalidades do servidor XMPP, juntamente com a descrição dos componentes por elas responsáveis, ap- resentados na figura 4.1. Estas funcionalidades são apresentadas de seguida, indicando qual o componente que a implementa.
• Gestão de ligações TCP: Connection Manager;
• Gestão de sessões de instant messaging: Session Manager; • Processamento de stanzas: Request Manager.
As classes que implementam os componentes Connection Manager e Session Manager, implementam o padrão Singleton, de forma a apenas existir uma única instância da classe. O que acontece pois estas classes possuem o estado da informação vital para o funcionamento do servidor. A classe que implementa o componente Request Manager não implementa este padrão, pois tem como único objectivo processar os stanzas que lhe são entregues, não ar- mazenando qualquer tipo de informação. Desta forma é definida com uma classe estática, tendo apenas métodos estáticos.
A implementação do servidor será realizada na plataforma .NET, como já foi referido anteriormente.
6.1
Gestão de ligações TCP
A gestão das ligações TCP consiste em aceitar novas ligações e gerir as já existentes. A gestão das ligações em si consiste no armazenamento das mesmas para realizar todos os acessos de leitura e escrita. Esta funcionalidade é implementada pelo componente Connection Manager, que é um dos principais componentes do servidor.
A negociação da ligação é um processo sequencial, bem definido e independente da gestão da mesma, além de ser controlado por outro componente (Session Manager). Devido à sua natureza, este processo ocorre de forma síncrona entre o cliente e o servidor. Por outro
Figura 6.1: Ilustração de uma entrada em connections
lado, a gestão das ligações ocorre sempre de forma assíncrona, pois trata-se de um processo completamente aleatório, não existindo qualquer sequência de acções definida. O cliente pode enviar um stanza ao servidor a qualquer altura, o que torna inviável que o servidor fique bloqueado à espera de informação. As escritas podem ser muitas vezes executadas durante o processamento de um stanza, podendo implicar escritas para vários streams. De forma a acelerar o conjunto das escritas, cada uma delas será efectuada de forma assíncrona. Assim todas as leituras e escritas são iniciadas de forma assíncrona e completadas por IO completion threadsdo ThreadPool.
A gestão das ligações passa também pelo armazenamento das mesmas, tal como foi dito anteriormente, por forma a poder usá-las para receber e enviar informação a qualquer altura. Mas manter as ligações não basta, é necessário associar uma ligação a uma sessão de instant messaging, por forma a saber qual o recurso de que utilizador realizou o pedido. Assim este componente armazena um dicionário (chamado connections), em que cada entrada tem como chave o identificador da sessão e como valor o objecto ligação (instância de Connection, referida em 5.2.5). A figura 6.1 ilustra uma entrada nesse dicionário.
O acesso a este dicionário é apenas realizado na posse do lock associado ao próprio di- cionário.
A classe responsável por implementar este componente é a classe ConnectionManager.
6.1.1
Recepção de ligações
As novas ligações são recebidas pelo porto TCP 5222 (tal como referido em 2.1.2, é a porta definida pela especificação para comunicação entre servidor e cliente). O componente inicia uma espera assíncrona sobre o porto, cuja função de callback será executada por uma IO completion thread. Este callback deve ter um tempo de execução curto, de forma a libertar a IO completion threado mais rápido possível. Logo a sua execução consiste em apenas iniciar um nova espera assíncrona e iniciar a negociação do stream XML numa worker thread do ThreadPool.
Se a negociação for bem sucedida, será devolvido um par <identificador de sessão, instân- cia de Connection>, que será adicionado a connections. De seguida será iniciada a primeira leitura assíncrona sobre a ligação. A listagem 6.1 apresenta um excerto de código usado na recepção de uma ligação.
T c p C l i e n t c l i e n t = ( T c p C l i e n t ) obj ; S e s s i o n M a n a g e r m a n a g e r = S e s s i o n M a n a g e r . G e t I n s t a n c e (); K e y V a l u e P a i r < string , C o n n e c t i o n >? sp = m a n a g e r . N e g o t i a t i o n ( c l i e n t ); if ( sp . H a s V a l u e ) { // Key : s e s s i o n I d e n t i f i e r // V a l u e : c o n n e c t i o n l o c k ( c o n n e c t i o n s ) { c o n n e c t i o n s . Add ( sp . V a l u e . Key , sp . V a l u e . V a l u e ); } sp . V a l u e . V a l u e . B e g i n R e a d ( t h i s . R e a d C a l l b a c k , sp . V a l u e ); } e l s e { try { c l i e n t . C l o s e (); } c a t c h ( I O E x c e p t i o n ) { } }