Transformar programa console em JSP ( E ganhe chat do facebook em java de brinde)

Fala galera, bom dia pra todo mundo [:

Eu to fazendo uma rede social que faz integração com facebook e
uma das suas funcionalidades é a usar o chat do face ( e Google+) .

Eu reuní vários exemplos e montei um programinha simples em console pra usar o XMPP, que é o protocolo que
o face usa para chat e tá funcionando até que legal.

O problema é que eu to apanhando mais que marido quando chega bêbado com marca de batom de madrugada
pra transformar isso em JSP. Perdoem esse jovem Padawan, eu faço direito e me aventuro nessas Terras
de java por teimosia ou por maldição, e queria poder contar com uma ajuda de vocês.

Vou jogar o programa aí, usando as duas formas de autenticação DIGEST-MD5 quanto X-FACEBOOK-PLATAFORM . A X-FACEBOOK-PLATAFORM deu um trabalhão
mas fica de presente pra vocês.

O exemplo que implementei é com DIGEST-MD5: só pra quem não conhecer a API do face não ficar perdido.
Pra quem se interessar pelo X-FACEBOOK-PLATAFORM como método de login é basicamente um jeito de obter
um access token do face que vale como uma chave que identifica o usuário. Igual aqueles tokens de banco.
Aí ele não precisa ficar entrando com usuário e senha o tempo todo: o face confia no access token e o usuário
confia em você ao deixar o access token ser gravado no seu banco, e a coisa toda funciona.

Funcionamento:

Vou deixar o Listener rodando na página do perfil do cara.
Ao receber uma mensagem, esse listener alimenta o FacebookMessageBean que é o objeto que representa a mensagem
e depois insere no banco de dados usando o FacebookMessageDAO.
Vai ter um processo rodando ativamente em javascript para ir no banco e pegar o que tiver no banco e jogar na tela
dentro da caixinha de chat do cara;

Você deve estar se perguntando porque simplesmente não jogo na tela, sem toda essa volta do Bean e do DAO.
Bem, é uma questão técnica: porque se eu fizer isso eu não tenho como saber se a mensagem chegou
e nem posso ler as mensagens que eu mesmo enviei ( depois que desloguei).

O que acontece de errado:
Quando eu tento transformar isso em JSP ele entra em um loop demoníaco e não sai nunca. Aí, consequentemente eu olho lá pro banco e não chega nada.

Gostaria de saber como seria o JSP de vocês. ( ignorem a GUI, pensem apenas no funcional).

Agradeço a grande ajuda de todos, qualquer colaboração é muito bem vinda. Quero terminar uma versão funcional até sexta ,posto aqui
pra ver se vcs curtem.

//principal FBConsoleChatApp.java
package com.fb.xmppchat.app;

import com.fb.xmppchat.helper.CustomSASLDigestMD5Mechanism;
import com.fb.xmppchat.helper.FBMessageListener;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.bidimap.DualHashBidiMap;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;

public class FBConsoleChatApp {

   public static final String FB_XMPP_HOST = "chat.facebook.com";
   public static final int FB_XMPP_PORT = 5222;

   private ConnectionConfiguration config;
   private XMPPConnection connection;
   private BidiMap friends = new DualHashBidiMap();
   private FBMessageListener fbml;

   public String connect() throws XMPPException {
      config = new ConnectionConfiguration(FB_XMPP_HOST, FB_XMPP_PORT);
    // esse X-FACEBOOK-PLATAFORM é outro jeito de autenticar. Em vez de passar usuario e senha,
      // você se loga no face uma vez e ganha uma chave de acesso temporária.

    //  SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM", SASLXFacebookPlatformMechanism.class);
    //  SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
    SASLAuthentication.registerSASLMechanism("DIGEST-MD5"
        , CustomSASLDigestMD5Mechanism.class);
      config.setSASLAuthenticationEnabled(true);
      config.setDebuggerEnabled(false);
      connection = new XMPPConnection(config);
      connection.connect();
      fbml = new FBMessageListener(connection);
      return connection.getConnectionID();
   }

   public void disconnect() {
      if ((connection != null) && (connection.isConnected())) {
         Presence presence = new Presence(Presence.Type.unavailable);
         presence.setStatus("offline");
         connection.disconnect(presence);
      }
   }

   public boolean login(String userName, String password)
     throws XMPPException {
      if ((connection != null) && (connection.isConnected())) {
         connection.login(userName, password);
         return true;
      }
      return false;
   }



   public String readInput() throws IOException {
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      return br.readLine();
   }

   public void showMenu() {
      System.out.println("Escolha uma opção.");
      System.out.println("1. Ver amigos online");
      System.out.println("2. Mandar mensagem");
      System.out.println("3. SAIR");
      System.out.print("Escolha: ");
   }

   public void getFriends() {
      if ((connection != null) && (connection.isConnected())) {
         Roster roster = connection.getRoster();
         int i = 1;
         for (RosterEntry entry : roster.getEntries()) {
            Presence presence = roster.getPresence(entry.getUser());
            if ((presence != null)
               && (presence.getType() != Presence.Type.unavailable)) {
               friends.put(entry.getUser(), entry);
               System.out.println(entry.getName() + "(#" + entry.getUser() + ")");
               i++;
            }
         }
         fbml.setFriends(friends);
      }
   }

   public void sendMessage() throws XMPPException
     , IOException {
      System.out.println("Escreva o código do seu amigo ( em números) para enviar uma mensagem");
      String friendKey = null;
      String text = null;
      System.out.print("Codigo do seu amigo: ");
      friendKey = readInput();
      System.out.print("Mensagem: ");
      text = readInput();
      sendMessage((RosterEntry) friends.get(friendKey), text);
   }

   public void sendMessage(final RosterEntry friend, String text)
     throws XMPPException {
      if ((connection != null) && (connection.isConnected())) {
         ChatManager chatManager = connection.getChatManager();
         Chat chat = chatManager.createChat(friend.getUser(), fbml);
         chat.sendMessage(text);
         System.out.println("Mensagem enviada para "
            + friend.getName());
      }
   }

   public static void main(String[] args) {
 
//o usuário é o que aparece na página depois do www.facebook.com
//http://www.facebook.com/eduardo.pacheco.587 no meu caso é eduardo.pacheco.587
      String username = "eduardo.pacheco.587";
      String password = "minhasenha"; //senha


       System.out.println("recebeu parametros");
      FBConsoleChatApp app = new FBConsoleChatApp();

      try {
         app.connect();

         if (!app.login(username, password)) {

            System.out.println("Acesso Negado...");
            System.exit(-2);
         }
         app.showMenu();
         String data = null;
         menu:
         while((data = app.readInput().trim()) != null) {
            if (!Character.isDigit(data.charAt(0))) {
               System.out.println("Entrada inválida, use apenas 1-3 !");
               app.showMenu();
               continue;
            }
            int choice = Integer.parseInt(data);
            if ((choice != 1) && (choice != 2) && (choice != 3)) {
               System.out.println("Entrada inválida, use apenas 1-3 !");
               app.showMenu();
               continue;
            }
            switch (choice) {
               case 1: app.getFriends();
                       app.showMenu();
                       continue menu;
               case 2: app.sendMessage();
                       app.showMenu();
                       continue menu;
               default: break menu;
            }
         }
         app.disconnect();
      } catch (XMPPException e) {
        if (e.getXMPPError() != null) {
           System.out.println("ERROR-CODE : " + e.getXMPPError().getCode());
           System.out.println("ERROR-CONDITION : " + e.getXMPPError().getCondition());
           System.out.println("ERROR-MESSAGE : " + e.getXMPPError().getMessage());
           System.out.println("ERROR-TYPE : " + e.getXMPPError().getType());
        }
        app.disconnect();
      } catch (IOException e) {
        System.out.println(e.getMessage());
        app.disconnect();
      }
  }
}


//SASLXFacebookPlatformMechanism.java
package com.fb.xmppchat.app;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;

import javax.security.sasl.Sasl;

import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;

public class SASLXFacebookPlatformMechanism extends SASLMechanism {

    public static final String NAME = "X-FACEBOOK-PLATFORM";
    private String apiKey = "";
        private String accessToken = "";


    /**
     * Constructor.
     */
    public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication) {
            super(saslAuthentication);
    }

    @Override
    protected void authenticate() throws IOException, XMPPException {
        // Send the authentication to the server
        getSASLAuthentication().send(new AuthMechanism(getName(), ""));
    }

    @Override
    public void authenticate(String apiKey, String host, String accessToken) throws IOException, XMPPException {
        this.apiKey = apiKey;
        this.accessToken = accessToken;
        this.hostname = host;

        String[] mechanisms = { "DIGEST-MD5" };
        Map<String, String> props = new HashMap<String, String>();
        this.sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
        authenticate();
    }

    @Override
    protected String getName() {
            return NAME;
    }

    @Override
    public void challengeReceived(String challenge) throws IOException {
        byte[] response = null;

        if (challenge != null) {
                    String decodedChallenge = new String(Base64.decode(challenge));
                    Map<String, String> parameters = getQueryMap(decodedChallenge);

                    String version = "1.0";
                    String nonce = parameters.get("nonce");
                    String method = parameters.get("method");

                    long callId = new GregorianCalendar().getTimeInMillis() / 1000L;

                    String composedResponse = "api_key=" + URLEncoder.encode(apiKey, "utf-8")
                                                                            + "&call_id=" + callId
                                                                            + "&method=" + URLEncoder.encode(method, "utf-8")
                                                                            + "&nonce=" + URLEncoder.encode(nonce, "utf-8")
                                                                            + "&access_token=" + URLEncoder.encode(accessToken, "utf-8")
                                                                            + "&v=" + URLEncoder.encode(version, "utf-8");

                    response = composedResponse.getBytes("utf-8");
        }

        String authenticationText = "";

        if (response != null){
                    authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
                }
        // Send the authentication to the server
        getSASLAuthentication().send(new Response(authenticationText));
    }

    private Map<String, String> getQueryMap(String query) {
            Map<String, String> map = new HashMap<String, String>();
            String[] params = query.split("\\&");

            for (String param : params) {
                    String[] fields = param.split("=", 2);
                    map.put(fields[0], (fields.length > 1 ? fields[1] : null));
            }
            return map;
    }
    }

//CustomSASLDigestMD5Mechanism.java
package com.fb.xmppchat.helper;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.Sasl;

import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.sasl.SASLMechanism;

public class CustomSASLDigestMD5Mechanism extends SASLMechanism {

   public CustomSASLDigestMD5Mechanism(SASLAuthentication saslAuthentication) {
      super(saslAuthentication);
   }

   @Override
   public void authenticate(String username, String host, String password)
     throws IOException, XMPPException {
      this.authenticationId = username;
      this.password = password;
      this.hostname = host;

      String[] mechanisms = { getName() };
      Map<String, String> props = new HashMap<String, String>();
      sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, this);
      super.authenticate();
   }

   @Override
   public void authenticate(String username, String host, CallbackHandler cbh)
     throws IOException, XMPPException {
      String[] mechanisms = { getName() };
      Map<String, String> props = new HashMap<String, String>();
      sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
      super.authenticate();
   }

   protected String getName() {
     return "DIGEST-MD5";
   }
}

//FBMessageListener.java
package com.fb.xmppchat.helper;

import org.apache.commons.collections.BidiMap;
import org.apache.commons.collections.MapIterator;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Message;

public class FBMessageListener implements MessageListener, Runnable {

   private FBMessageListener fbml = this;
   private XMPPConnection conn;
   private BidiMap friends;

   public FBMessageListener(XMPPConnection conn) {
      this.conn = conn;
      new Thread(this).start();
   }

   public void setFriends(BidiMap friends) {
      this.friends = friends;
   }

   public void processMessage(Chat chat, Message message) {
      System.out.println();
      MapIterator it = friends.mapIterator();
      String key = null;
      RosterEntry entry = null;
      while (it.hasNext()) {
         key = (String) it.next();
         entry = (RosterEntry) it.getValue();
         if (entry.getUser().equalsIgnoreCase(chat.getParticipant())) {
            break;
         }
      }
      if ((message != null) && (message.getBody() != null)) {
         System.out.println(  entry.getName()+"( "+entry.getUser()+" )");
         System.out.println(message.getBody() );
         System.out.print("Sua escolha[1-3]: ");
      }
   }

   public void run() {
      conn.getChatManager().addChatListener(
         new ChatManagerListener() {
            public void chatCreated(Chat chat, boolean createdLocally) {
               if (!createdLocally) {
                  chat.addMessageListener(fbml);
               }
            }
         }
      );
   }
}

Ah, precisa das bibliotecas Smack ( xmpp para java)
http://www.igniterealtime.org/projects/smack/

e usar o Bidmap do Apache commons-collections, baixa aqui
http://commons.apache.org/collections/download_collections.cgi

obrigado!

:confused:

Olá shamanpyro,

Estou executando seu código e estou obtendo o seguinte erro:

SASL authentication X-FACEBOOK-PLATFORM failed: not-authorized:

Você tem alguma ideia do que pode ser este problema?

Estou iniciando na api do facebook.

Obrigado.