Construindo um Aplicativo Ativado por AIM no Eclipse

Ativando seus aplicativos para fazer a interface com AIM

Os aplicativos de hoje tiram proveito de uma interface que muitas pessoas já estão usando: mensagem instantânea (IM). Os aplicativos oferecem integração com IM, pois oferece fácil acesso através de uma interface com a qual as pessoas estão familiarizadas e muitas pessoas já possuem ativada e em execução. Aplicativos de IM também estão disponíveis em muitas plataformas remotas, fornecendo a seus usuários a capacidade de fazer a interface com seu aplicativo a partir de dispositivos remotos.

Nathan A. Good, Senior Information Engineer, Freelance Developer

Nathan GoodNathan A. Good vive na área de Twin Cities em Minnesota. Profissionalmente, ele realiza desenvolvimento de software, arquitetura de software e administração de sistemas. Quando não está gravando software, ele gosta de construir PCs e servidores, ler sobre e trabalhar com novas tecnologias e tentar convencer seus amigos de migrar para software livre. Ele escreveu e foi co-autor de muitos livros e artigos, incluindo Professional Red Hat Enterprise Linux 3, Regular Expression Recipes: A Problem-Solution Approach e Foundations of PEAR: Rapid PHP Development.



24/Fev/2009

Mensagem instantânea (IM) pode ser uma excelente maneira de construir interfaces com um aplicativo existente ou novo. Muitas pessoas usam IM e aqueles que usam geralmente têm o aplicativo de IM — como o AOL Instant Messenger (AIM) — aberto e em execução sempre que seus computadores também estiverem. Clientes de IM podem ser encontrados não apenas em computadores, mas também em dispositivos móveis, como Personal Digital Assistants (PDAs) e telefones.

Construindo uma interface com seu aplicativo que permita que usuários se conectem a ele com IM, tira-se proveito de uma grande infraestrutura existente para comunicação através de uma rede. Para usuários que já possuem IDs de IM e têm clientes de IM ativados e em execução, fornece-se uma maneira conveniente de acessar seu aplicativo.

Este artigo demonstra como é possível construir um aplicativo Java™ que usa as bibliotecas do kit de desenvolvimento de software (SDK) do cliente da AOL para obter comandos de usuários. Seu aplicativo poderá processar os comandos e responder aos usuários com resultados. Ao longo do caminho, este artigo introduz alguns padrões de design que podem ser usados para construir o aplicativo para que seja extensível e fácil de manter.

Requisitos do Sistema

Para poder seguir os exemplos deste artigo de forma efetiva, deve ter o ambiente de desenvolvimento integrado (IDE) Eclipse V3.4 ou posterior instalado em seu computador. Deve-se, também, estar familiarizado com a linguagem de programação Java para poder ler e executar os exemplos.


Introdução da API AIM

Há muitos serviços de IM. Este artigo foca o serviço AIM da AOL. A AOL fornece um SDK gratuito que pode ser usado para construir aplicativos que podem conectar-se a e usar os serviços da AOL (consulte Recursos).

Para fazer download do SDK, deve-se concordar com os termos de uso da AOL para a biblioteca. É necessário obter também as chaves de desenvolvedor para serem usadas pela API. Siga as instruções on-line para obter as chaves do cliente customizado, pois, efetivamente, você estará construindo um cliente AIM customizado automatizado.

Após fazer download do SDK em arquivo ZIP ou tar.gz (accsdk_macosx_univ_1_6_8.tar.gz), salve-o em local que se lembrará posteriormente. O arquivo archive inclui o arquivo de Java (JAR) e outros arquivos de biblioteca que serão necessários. Também contém o JavaDoc para a interface de programação de aplicativo (API), portanto, você pode querer extrair os arquivos para ler o JavaDoc que se aplica à versão da API transferida por download. Como o Eclipse permite importar arquivos de biblioteca de dentro de arquivos archive, não é expressamente necessário extrair os arquivos.

Uma versão do SDK de AOL AIM está disponível para o Microsoft® Windows®, o Mac OS® X e o Linux®. Antes de prosseguir, certifique-se de que a versão correta para seu sistema operacional seja transferida por download. Se você planeja desenvolver o aplicativo em um sistema operacional e implementá-lo em um diferente, ambas as versões das bibliotecas serão necessárias. Há outras bibliotecas disponíveis para a comunicação com o AIM, principalmente, bibliotecas de software livre gravadas em código Java. Optei por usar o SDK da AOL, pois estou usando esse serviço.

O site da AOL possui exemplos que podem ser observados para a familiarização com a API.


Obtendo um ID de Bot do AIM

Antes de poder efetuar logon e testar seu serviço, é necessário um ID do AIM. Além disso, é necessário seguir um processo no site do AIM, chamado "Efetuar Bot de meu Nome de Tela" (consulte Recursos), para o ID que irá usar para o aplicativo. Recomendo a criação desse ID extra imediatamente. Ele facilita os testes, pois é possível usar seu nome de tela pessoal do AIM para tentar se comunicar com seu aplicativo.

Criando seu Projeto

Caso ainda não tenha um projeto Java que deseje usar, é necessário incluir um novo projeto Java. Use Arquivo > Novo para abrir o novo assistente de projeto Java e siga as etapas para incluir um novo projeto Java. Se estiver usando um projeto Java existente — como um aplicativo que já esteja construindo — pode ignorar esta etapa.

Importando e Instalando a API Java

As classes e interfaces Java usadas enquanto recursos de IM são incluídos em seu aplicativo estão no arquivo accwrap.jar localizado no diretório dist/release/lib dentro do arquivo archive. Antes de poder construir sua classe que finalmente implementa a interface AccEvents , é necessário importar essas bibliotecas e incluí-las em seu caminho de classe.

Criação de Log

Usei log4j para a criação de log em meu aplicativo. Para reduzir dependências, seria possível usar a Criação de Log Java a partir do espaço de nomes java.util.logging . Consulte Recursos para obter diferentes implementações de criação de log. Recomendo usar uma solução de criação de log sobre System.out.println().

Quando construo aplicativos Java, geralmente crio um diretório lib dentro de meu projeto Java e coloco todos os meus arquivos JAR em diretórios dentro da pasta lib. Denomino os diretórios usando o nome e a versão da biblioteca contida na pasta. Por exemplo, usei o utilitário de criação de log log4J da Apache para registrar mensagens em log para depuração. O caminho para o arquivo JAR relativo à área de trabalho é lib/apache-log4j-1.2.15/log4j-1.2.15.jar.

Para importar a API Java e dependências para o AOL SDK, quebro essa convenção. Em vez disso, coloco o arquivo JAR juntamente com os outros arquivos na pasta dist/release/lib na pasta-raiz do projeto. Isso ocorre porque ao executar seu aplicativo, o Java Runtime Environment (JRE) localiza accwrap.jar em qualquer lugar que o tiver especificado em seu caminho de classe. No entanto, procura os arquivos de biblioteca no diretório de trabalho atual. Se ainda quiser colocar os arquivos em uma pasta específica, pode fazer isso sem problemas se você atualizar sua configuração de execução para o aplicativo para o local da pasta. Acho essa solução um pouco problemática em ambientes de equipe onde a configuração de execução customizada pode se tornar um incômodo.

Se os arquivos da biblioteca se tornarem uma distração ou exagerados no Package Explorer, é possível incluir um filtro de visualização que faça com que os arquivos não sejam exibidos.

Inclua o arquivo accwrap.jar no caminho de construção de seu projeto, selecionando o arquivo JAR no Package Explorer e selecionando Caminho de Construção > Incluir no Caminho de Construção no menu de contexto. Como alternativa, use as preferências do projeto para configurar o caminho de construção e inclua o arquivo accwrap.jar.


Usando a API Java do AIM

A esta altura, é necessário possuir um projeto Java com os arquivos accwrap.jar e de biblioteca importado para seu projeto.

Para iniciar a construção da interface de seu aplicativo para o AIM, é necessário incluir uma classe que implemente a interface AccEvents . A classe de exemplo neste artigo também inclui a função main() , mas isso não é um requisito.

A maneira mais fácil de incluir essa classe é usando o assistente Arquivo > Novo > Classe . Digite o nome do pacote e o nome da classe, conforme mostrado na Figura 1. Clique em Incluir ao lado de Interfaces para incluir uma nova interface e digite o nome AccEvents. Como o arquivo accwrap.jar foi incluído no caminho de construção, a interface deve ser localizada na lista.

Figura 1. Incluindo a Classe que Implementa AccEvents
Incluindo a Classe que Implementa AccEvents

Após incluir a classe, ela parece a Lista 1. Por questão de brevidade, essa lista não inclui os muitos métodos na interface, pois este artigo não usará todos eles.

Lista 1. A Nova Classe de Implementação
package com.nathanagood.shopper;

import com.aol.acc.*;

public class MySuperShopperBot implements AccEvents {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub

    }

    public void OnImReceived(AccSession session, AccImSession imSession,
            AccParticipant participant, AccIm im) {
        // TODO:  Add implementation
    }

    public void OnStateChange(AccSession session, AccSessionState state,
            AccResult result) {
        // TODO:  Add implementation
    }

    /* Many other methods for AccEvents snipped... */
}

Incluindo o Código

O método main() , mostrado na Lista 2, cria uma nova instância da classe e chama o método signOn() .

Lista 2. O Método Principal
    public static void main(final String[] args) {

        logger.info("Starting My Super Shopper bot...");

        MySuperShopperBot bot = new MySuperShopperBot();

        try {
            bot.signOn();
        } catch (AccException ae) {
            logger.error("An error occurred while trying to sign on.", ae);
        }

        logger.info("Shutting down bot...");
    }

O construtor é mostrado na Lista 3. O Construtor usa um factory, MessageHandlerFactory, para criar uma instância de um MessageHandler e designa o mesmo para a variável messageHandler . Esse objeto é usado posteriormente na implementação do método AccEvents.OnIMReceived() para realizar o trabalho de fato.

Lista 3. O Construtor MySuperShopperBot
    public MySuperShopperBot() {
        messageHandler = MessageHandlerFactory.createMessageHandler();
    }

O método signOn() é mostrado na Lista 4. Ele cria uma nova instância do objeto AccSession , configura-o e inicia o loop para atender as mensagens recebidas.

Lista 4. O Método signOn()
    public void signOn() throws AccException {

        session = new AccSession();
        session.setEventListener(this);

        AccClientInfo info = session.getClientInfo();
        info.setDescription(AIM_KEY);

        session.setIdentity(AIM_USERNAME);

        session.setPrefsHook(new MySuperShopperPrefs());
        session.signOn(AIM_PASSWORD);

        while (isRunning) {
            try {
                AccSession.pump(50);
            } catch (Exception e) {
                logger.error("Exception occurred while handling message", e);
            }

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                logger.warn("Thread was interrupted", e);
            }

        }

        info = null;
        session = null;

        System.gc();
        System.runFinalization();

    }

O método signOn() configura diversas propriedades antes de efetuar logon. A chave do desenvolvedor obtida ao fazer download do SDK é configurada em setDescription(). O nome de tela do AIM é configurado pelo método setIdentity() e a senha é fornecida como um argumento de signOn().

O código dentro do loop (pump() the Thread.sleep()) mantém o serviço em execução e atendendo mensagens.

Para o exemplo deste artigo, coloquei implementação somente dentro de dois métodos. Um deles é o método OnIMReceived() mostrado na Lista 5. Ele obtém o valor da cadeia de caracteres de texto simples da mensagem IM recebida. Então pergunta a messageHandler se pode tratar dessa mensagem. Se messageHandler sobre como tratar da mensagem, chama o método handleMessage() . Essa é uma alternativa para a construção de um método grande com instruções if/else usadas para controlar o fluxo. Informações adicionais sobre os padrões usados aqui estão na seção "Padrões de Design".

Lista 5. O Método OnIMReceived()
    public void OnImReceived(AccSession session, AccImSession imSession,
            AccParticipant participant, AccIm im) {
        String message;
        try {
            message = im.getConvertedText(PLAIN_TEXT);

            /* Provide a way to cleanly shut down the client */
            if (message.equals(SHUTDOWN_COMMAND)) {
                session.signOff();
            } else {

                if (messageHandler.canHandle(message)) {

                    String response = messageHandler.handle(message,
                            participant.getName());

                    im.setText(response);
                    imSession.sendIm(im);
                }
            }

        } catch (AccException e) {
            logger.error("Error receiving message.", e);
        }
    }

Por fim, codifiquei de forma permanente um valor que posso usar ao testar o encerramento do serviço de IM de forma limpa. Inclui código dentro do método OnStateChange() mostrado na Lista 6 para configurar o status isRunning para false, para que o aplicativo saia de forma limpa do loop.

Lista 6. O Método OnStateChange()
    public void OnStateChange(AccSession session, AccSessionState state,
            AccResult result) {
        if (state == AccSessionState.Offline) {
            isRunning = false;
        }
    }

Padrões de Design

Neste artigo, demonstrei diversos padrões de design para construir um aplicativo que é extensível, fácil de manter e facilmente modificado para funcionar com um aplicativo existente sem um forte acoplamento do código de implementação de AccEvents com outro código do aplicativo.

O primeiro padrão é o padrão de estratégia, demonstrado pela interface MessageHandler . Ele permite que qualquer implementação trate da mensagem de maneira muito específica. Uma variação do padrão de estratégia é que ele fornece um método, (canHandle(), que permite que a própria implementação informe se tem conhecimento para processar a mensagem corretamente. Usando essa variação, você remove a necessidade de um factory ter esse conhecimento ao escolher uma implementação apropriada para a mensagem.

O padrão decorador é usado pela classe DelegatingMessageHandler mostrada da Lista 7. Em uma construção semelhante a encadeamento de filtro, usa uma lista de objetos MessageHandler no construtor. Em sua própria implementação do método canHandle() da interface MessageHandler , itera através dos manipuladores registrados com ele, procurando um manipulador que saiba como tratar da mensagem recebida. Ao localizar um candidato, passa a mensagem.

Lista 7. A Classe DelegatingMessageHandler
package com.nathanagood.shopper.handlers;

import java.util.List;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

public final class DelegatingMessageHandler implements MessageHandler {

    private final static Logger logger = LogManager
            .getLogger(DelegatingMessageHandler.class);

    private List<MessageHandler> handlers;

    public DelegatingMessageHandler(final List<MessageHandler> handlers) {
        this.handlers = handlers;
    }

    /**
     *
     * @param handler
     */
    public void registerHandler(MessageHandler handler)
    {
        handlers.add(handler);
    }

    public boolean canHandle(final String message) {
        return (handlers != null);
    }

    public String handle(final String message, final String user) {
        String result = "I don't understand that...";

        for (MessageHandler handler : handlers) {
            if (handler.canHandle(message)) {
                logger.debug("Processing message \"" + message
                        + "\" from user \"" + user + "\" with handler \""
                        + handler.getClass().getCanonicalName() + "\"");
                result = handler.handle(message, user);
                logger.debug("Returning result \"" + result + "\"");
                break;
            }
        }

        return result;
    }

}

O padrão de factory é usado pelo método MessageHandlerFactory.createMessageFactory() , mostrado na Lista 8, que cria, inicializa e retorna uma instância de DelegatingMessageHandler. Ao usar um factory para criar a implementação, o responsável pela chamada não precisa conhecer nenhum detalhe sobre a inicialização.

Lista 8. A Classe MessageHandlerFactory
package com.nathanagood.shopper.handlers;

import java.util.ArrayList;
import java.util.List;

/**
 * Factory for creating a {@link MessageHandler}.
 * @author Nathan A. Good
 */
public class MessageHandlerFactory {

    /**
     * Creates a {@link MessageHandler} implementation.
     * @return MessageHandler.
     */
    public static MessageHandler createMessageHandler() {
        List<MessageHandler> handlers = new ArrayList<MessageHandler>();
        handlers.add(new ShoppingListMessageHandler());
        // handlers.add(new EchoMessageHandler()); // useful for testing...

        DelegatingMessageHandler handler = new DelegatingMessageHandler(handlers);

        return handler;
    }

}

Usar padrões como esses, em vez de colocar o código dentro do método OnIMReceived() , tem diversas vantagens. Por exemplo, com uma pequena modificação, é possível introduzir o padrão de estado com alguma persistência para controlar o estado de conversação.


Respondendo a uma Mensagem

Após processar a mensagem com sua classe de implementação, provavelmente deseja responder ao usuário com uma mensagem. Se você processar e responder as mensagens de forma síncrona (como no exemplo deste artigo), pode responder usando o método sendIm() .

Lista 9. Respondendo o Usuário em OnImReceived
    public void OnImReceived(AccSession session, AccImSession imSession,
            AccParticipant participant, AccIm im) {
        String message;
        try {
            message = im.getConvertedText(PLAIN_TEXT);

            if (message.equals("goodbye")) {
                session.signOff();
            } else {

                if (messageHandler.canHandle(message)) {

                    String response = messageHandler.handle(message,
                            participant.getName());

                    im.setText(response);
                    imSession.sendIm(im);
                }
            }

        } catch (AccException e) {
            logger.error("Error receiving message.", e);
        }
    }

Se processar mensagens de forma assíncrona, é necessário criar uma nova instância do objeto AccImSession usando o nome de tela do usuário ao qual o aplicativo precisa responder, conforme mostrado na Lista 10. A variável screenname é o nome de tela do usuário e message é o conteúdo da IM como uma cadeia de caracteres. Processar as mensagens de forma assíncrona pode ser útil se o processamento de mensagens levar algum tempo.

Lista 10. Criando uma Nova Mensagem IM e Enviando-a
        AccImSession imSession = session.createImSession(screenname, AccImSessionType.Im);
        imSession.sendIm(session.createIm(message, "text/plain"));

Incluindo a Implementação de um MessageHandler

A implementação do MessageHandlerShoppingListMessageHandler, mostrada na Lista 11, é uma implementação que permite que MySuperShopper inclua os itens de sua lista de compras em um banco de dados de onde podem ser recuperados a partir de seu dispositivo remoto posteriormente.

Lista 11. A Classe ShoppingListMessageHandler
package com.nathanagood.shopper.handlers;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.nathanagood.shopper.persistence.ShoppingListItem;
import com.nathanagood.shopper.persistence.ShoppingListManager;

public class ShoppingListMessageHandler implements MessageHandler {

    private static final Logger logger = LogManager.getLogger(ShoppingListManager.class);
    private static final Pattern addCommandPattern = Pattern
            .compile("^\\s*add +([\\d]+)[ -]+(.*)$");

    public boolean canHandle(final String message) {
        return addCommandPattern.matcher(message).matches();
    }

    public String handle(final String message, final String user) {
        String result = "An error occurred while adding your item.";

        try {

            if ( addCommandPattern.matcher(message).matches() ) {
                ShoppingListManager manager = new ShoppingListManager();
                manager.addShoppingListItem(user, parse(message));
                result = "Successfully added item.";
            }

        } catch (Exception e) {
            logger.error("Error while handling item.", e);
        }

        return result;
    }

    private ShoppingListItem parse(final String value) {
        int quantity = 0;
        String description = "";
        Matcher match = addCommandPattern.matcher(value);

        if ( match.find() ) {
            quantity = Integer.parseInt(match.group(1));
            description = match.group(2);
        }

        logger.debug("Parsed item with \"" + quantity + "\" number of \"" +
        	description + "\"");

        return new ShoppingListItem(quantity, description);
    }

}

A classe ShoppingListManager está incluída no download do código que acompanha este artigo. A implementação específica não é importante para este exemplo. O que importa é que a classe ShoppingListManager faz algo para persistir o item da lista de compras do usuário.


Executando seu Aplicativo

Antes de executar seu aplicativo, efetue login no AIM usando seu nome de tela pessoal e inclua o nome de tela de seu aplicativo como um amigo. Isso permite ver seu aplicativo on-line quando for iniciado. É possível testá-lo enviando mensagens.

Após incluir todas as classes de implementação, é possível executar o aplicativo usando Projeto > Executar. O projeto é iniciado e deve ser possível vê-lo on-line em seu cliente de IM.

Resolução de Problemas

Como inicialmente não coloquei os arquivos de biblioteca no diretório base do projeto, recebi a seguinte mensagem: Exceção no thread "principal" java.lang.UnsatisfiedLinkError.

No Windows, simplesmente certificar-se de que as bibliotecas nativas (como as bibliotecas de link dinâmico, ou DLLs) estejam no diretório de trabalho resolve o problema. No entanto, em meu Mac, precisei configurar uma variável de ambiente denominada DYLD_LIBRARY_PATH para o local da área de trabalho de meu projeto.

Figura 2. Incluindo uma Variável de Ambiente na Configuração
Incluindo uma Variável de Ambiente na Configuração

Se a descrição não estiver configurada para sua chave corretamente, é encontrado um erro do tipo com.aol.acc.AccException: IAccClientInfo_SetDescription.

Eu copiei a chave diretamente do site da AOL, portanto, minha constante AIM_KEY tinha o valor My Super Shopper (Key:my1XzlXXXXXXXXXX). No download do código, inclui um EchoMessageHandler que simplesmente ecoa a mensagem de volta para. Também registra em log a mensagem recebida e o nome de tela, portanto, é útil para testes.


Resumo

Usando o AIM SDK, é possível criar um cliente Java customizado que permite que seus aplicativos Java aceitem mensagens de seus usuários e respondam a elas usando IM. Usando os padrões fornecidos, é possível criar uma extensão para seu aplicativo que seja fácil de manter e estender.


Download

DescriçãoNomeTamanho
Sample codeos-blackberry2-IBMRssReader_src.zip112KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

  • Os newsgroups da Plataforma Eclipse devem ser sua primeira parada na discussão de questões referentes ao Eclipse. (Selecionar isso ativará seu aplicativo de leitura de notícias Usenet padrão e abrirá eclipse.platform.)
  • Os newsgroups do Eclipse possuem muitos recursos para pessoas interessadas em usar e estender o Eclipse.
  • Participe dos blogs do developerWorks e envolva-se na comunidade do developerWorks.

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Software livre, Tecnologia Java
ArticleID=423774
ArticleTitle=Construindo um Aplicativo Ativado por AIM no Eclipse
publish-date=02242009