Trabalho de Sistemas Distribuídos Implementação de um Chat utilizando JMS Eduardo Rigamonte e Pietro Christ INTRODUÇÃO Neste roteiro iremos explicar a criação de um chat utilizando a tecnologia Java Message Service (JMS). A arquitetura JMS disponibiliza dois tipos de troca de mensagens, o Queue (modelo ponto a ponto ou modelo de filas) e o modelo Topic ou Publish/Subscribe. Com o modelo de troca de mensagens por filas, poderíamos ter um ou vários produtores enviando mensagens para uma mesma fila, mas teríamos apenas um único consumidor. Neste modelo, cada vez que uma mensagem é consumida, ela deixa de existir na fila, por isso, podemos ter apenas um único consumidor. O modelo Topic, permite uma situação em que teríamos de um a vários consumidores para o mesmo tópico. Funciona como um sistema de assinaturas, cada cliente assina um tópico e a mensagem fica armazenada no servidor até todos assinantes a tenham consumido. Este será o modelo usado neste tutorial. Arquitetura utilizando Queue (filas) e Topic (tópicos) ROTEIRO 1 Instalação dos artefatos e ferramentas necessárias: 1.1 Fazer o download do Apache ActiveMQ 5.4.2 http://www.4shared.com/zip/ksBEB_FW/apache-activemq-542-bin.html 1.2 Descompactar o arquivo baixado no diretório C:. 1.3 Para iniciá-lo basta executar o arquivo activemq.bat no caminho: C:\apache-activemq-5.4.2\bin\win32
Tutorial sobre como fazer um chat ussando JMS e ActiveMQ
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Trabalho de Sistemas Distribuídos Implementação de um Chat utilizando JMS
Eduardo Rigamonte e Pietro Christ
INTRODUÇÃO
Neste roteiro iremos explicar a criação de um chat utilizando a tecnologia Java Message
Service (JMS). A arquitetura JMS disponibiliza dois tipos de troca de mensagens, o Queue (modelo
ponto a ponto ou modelo de filas) e o modelo Topic ou Publish/Subscribe.
Com o modelo de troca de mensagens por filas, poderíamos ter um ou vários produtores
enviando mensagens para uma mesma fila, mas teríamos apenas um único consumidor. Neste
modelo, cada vez que uma mensagem é consumida, ela deixa de existir na fila, por isso, podemos
ter apenas um único consumidor.
O modelo Topic, permite uma situação em que teríamos de um a vários consumidores para o
mesmo tópico. Funciona como um sistema de assinaturas, cada cliente assina um tópico e a
mensagem fica armazenada no servidor até todos assinantes a tenham consumido. Este será o
modelo usado neste tutorial.
Arquitetura utilizando Queue (filas) e Topic (tópicos)
ROTEIRO
1 Instalação dos artefatos e ferramentas necessárias:
public class JmsSubscriber implements MessageListener {
private TopicConnection connect;
public JmsSubscriber(String factoryName, String topicName) throws JMSException, NamingException { // Obtém os dados da conexão JNDI através do arquivo jndi.properties Context jndiContext = new InitialContext(CtrlChat.getInstance().getProperties()); // O cliente utiliza o TopicConnectionFactory para criar um objeto do tipo TopicConnection com o provedor JMS TopicConnectionFactory factory = (TopicConnectionFactory) jndiContext.lookup(factoryName); // Pesquisa o destino do tópico via JNDI Topic topic = (Topic) jndiContext.lookup(topicName); // Utiliza o TopicConnectionFactory para criar a conexão com o provedor JMS this.connect = factory.createTopicConnection(); // Utiliza o TopicConnection para criar a sessão para o consumidor // Atributo false -> uso ou não de transações(tratar uma série de envios/recebimentos como unidade atômica e // controlá-la via commit e rollback) // Atributo AUTO_ACKNOWLEDGE -> Exige confirmação automática após recebimento correto TopicSession session = connect.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); // Cria(Assina) o tópico JMS do consumidor das mensagens através da sessão e o nome do tópico TopicSubscriber subscriber = session.createSubscriber(topic); // Escuta o tópico para receber as mensagens através do método onMessage() subscriber.setMessageListener(this); // Inicia a conexão JMS, permite que mensagens sejam entregues connect.start(); }
@Override // Recebe as mensagens do tópico assinado public void onMessage(Message message) { try {
Anexo
// As mensagens criadas como TextMessage devem ser recebidas como TextMessage TextMessage textMsg = (TextMessage) message; String text = textMsg.getText(); CtrlChat.getInstance().printtTela(text); } catch (JMSException ex) { ex.printStackTrace(); } } // Fecha a conexão JMS public void close() throws JMSException { this.connect.close(); }}
/* * Classe criada para o 2º trabalho da disciplina de Sistemas Distribuidos. */package chat.rede;
public JmsPublisher(String factoryName, String topicName) throws JMSException, NamingException { // Obtém os dados da conexão JNDI através do arquivo jndi.properties Context jndiContext = new InitialContext(CtrlChat.getInstance().getProperties()); // O cliente utiliza o TopicConnectionFactory para criar um objeto do tipo TopicConnection com o provedor JMS TopicConnectionFactory factory = (TopicConnectionFactory) jndiContext.lookup(factoryName); // Pesquisa o destino do tópico via JNDI Topic topic = (Topic) jndiContext.lookup(topicName); // Utiliza o TopicConnectionFactory para criar a conexão com o provedor JMS this.connect = factory.createTopicConnection(); // Utiliza o TopicConnection para criar a sessão para o produtor // Atributo false -> uso ou não de transações(tratar uma série de envios/recebimentos como unidade atômica e // controlá-la via commit e rollback) // Atributo AUTO_ACKNOWLEDGE -> Exige confirmação automática após recebimento correto this.session = connect.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); // Cria o tópico JMS do produtor das mensagens através da sessão e o nome do tópico this.publisher = session.createPublisher(topic); }
// Cria a mensagem de texto e a publica no tópico. Evento referente ao produtor public void publish(String message) throws JMSException { // Recebe um objeto da sessao para criar uma mensagem do tipo TextMessage TextMessage textMsg = this.session.createTextMessage(); // Seta no objeto a mensagem que será enviada textMsg.setText(message);
// Publica a mensagem no tópico this.publisher.publish(textMsg, DeliveryMode.PERSISTENT, 1, 0); }
// Fecha a conexão JMS public void close() throws JMSException { this.connect.close(); }}
/* * Classe criada para o 2º trabalho da disciplina de Sistemas Distribuidos. */package chat.ctrl;
public static CtrlChat getInstance() { return CtrlChatHolder.INSTANCE; }
private static class CtrlChatHolder {
private static final CtrlChat INSTANCE = new CtrlChat(); }
public Properties getProperties() { if (prop == null) { prop = new Properties();
//classe jndi para o ActiveMQ prop.put("java.naming.factory.initial", "org.apache.activemq.jndi.ActiveMQInitialContextFactory"); //url para conexão com o provedor prop.put("java.naming.provider.url", "tcp://" + ipServ + ":61616"); //nome da conexão prop.put("connectionFactoryNames", factoryName); //nome do tópico jms prop.put("topic.topicChat", topicName); }
return prop; }
public void changeServer(String ip) { //inicia ou pega as propriedades Properties p = getProperties(); //remove o endereço anterior p.remove("java.naming.provider.url"); //adiciona um novo p.put("java.naming.provider.url", "tcp://" + ip + ":61616"); ipServ = ip;
}
public void iniciarJMS() { try { subscriber = new JmsSubscriber(factoryName, topicName); publisher = new JmsPublisher(factoryName, topicName);
publisher.publish(this.nome + " entrou na sala ..\n");
//habilita os componentes da janela como enable true/false janChat.setComponentesEnable(true); } catch (JMSException ex) { JOptionPane.showMessageDialog(janChat, "Servidor não Encontrado!", "ERROR", JOptionPane.OK_OPTION); ipServ = ""; Logger.getLogger(CtrlChat.class.getName()).log(Level.SEVERE, null, ex); } catch (NamingException ex) { Logger.getLogger(CtrlChat.class.getName()).log(Level.SEVERE, null, ex); } }
public boolean servidorIniciado() { if (publisher != null && subscriber != null) { return true; } return false; }
public void abrirJanConfigurar() { new JanConfigurar().setVisible(true); }
public String getNome() { return nome; }
public void setNome(String nome) { if (servidorIniciado()) { try { //envia para o servidor publisher.publish(this.nome + " mudou o nome para " + nome + "\n"); } catch (JMSException ex) { Logger.getLogger(JanChat.class.getName()).log(Level.SEVERE, null, ex); } } this.nome = nome; }
public String getIpServidor() { return ipServ; }
public void setIpServidor(String ipServ) { this.ipServ = ipServ; }
public void setTela(JanChat aThis) { janChat = aThis; }
public void printtTela(String msg) { janChat.printTela(msg); } //dao responsavel por salvar as mensagens no banco private DAO dao = DAOFactory.getInstance().obterDAO(Mensagem.class);
public void salvarHistorico(String msg) { Mensagem m = new Mensagem(); m.setTexto(msg);
panelPrincipal = new javax.swing.JPanel(); panelCenter = new javax.swing.JPanel(); filler5 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 10), new java.awt.Dimension(0, 10), new java.awt.Dimension(32767, 10)); filler7 = new javax.swing.Box.Filler(new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 32767)); filler8 = new javax.swing.Box.Filler(new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 32767)); panelTitulo = new javax.swing.JPanel(); tituloLabel = new javax.swing.JLabel(); conversaScrollPane = new javax.swing.JScrollPane(); conversaText = new javax.swing.JTextPane(); panelSouth = new javax.swing.JPanel(); filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 10), new java.awt.Dimension(0, 10), new java.awt.Dimension(32767, 10)); filler4 = new javax.swing.Box.Filler(new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 32767)); panelMensagem = new javax.swing.JPanel(); panelTexto = new javax.swing.JPanel(); mensagemScrollPane = new javax.swing.JScrollPane(); mensagemText = new javax.swing.JTextPane(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 32767)); panelBotao = new javax.swing.JPanel(); enviarButton = new javax.swing.JButton(); filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 0), new java.awt.Dimension(10, 32767)); barraMenuChat = new javax.swing.JMenuBar(); arquivoMenu = new javax.swing.JMenu(); configItem = new javax.swing.JMenuItem(); separador = new javax.swing.JPopupMenu.Separator(); sairItem = new javax.swing.JMenuItem(); historicoMenu = new javax.swing.JMenu(); ativarCheckBox = new javax.swing.JCheckBoxMenuItem(); exportarHistoricoItem = new javax.swing.JMenuItem();
/* * Esse método é chamado para configurar o frame. */ // <editor-fold defaultstate="collapsed" desc="Método de configuração do Frame"> private void initFrame() { super.setTitle("CHAT JMS"); super.setExtendedState(Frame.MAXIMIZED_BOTH);
try { //adição do icone ao programa super.setIconImage(ImageIO.read(getClass().getResource("/chat/img/icone.png"))); }
/* * Esses Métodos são os eventos dos componentes do frame. */ // <editor-fold defaultstate="collapsed" desc="Eventos de Componentes"> private void sairItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sairItemActionPerformed //fecha a janela this.dispose(); }//GEN-LAST:event_sairItemActionPerformed
private void enviarButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enviarButtonActionPerformed if (CtrlChat.getInstance().servidorIniciado()) { //pega a mesagem que o usuario quer enviar String msg = mensagemText.getText();
if (!msg.trim().equals("")) { //adiciona o nome do usuario java.util.Date agora = new java.util.Date();; SimpleDateFormat formata = new SimpleDateFormat("HH:mm");
private void exportarHistoricoItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportarHistoricoItemActionPerformed JFileChooser jfc = new JFileChooser(); int result = jfc.showSaveDialog(this);
if (result == JFileChooser.APPROVE_OPTION) { File file = jfc.getSelectedFile();
private void mensagemTextKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_mensagemTextKeyPressed //se for clicado um enter dentro do componente de mensagem //o evento do botão enviar e acionado if (evt.getKeyCode() == KeyEvent.VK_ENTER) { if (!shiftOn) { enviarButtonActionPerformed(null); try { //retira a acao de pula de linha new Robot().keyPress(KeyEvent.VK_BACK_SPACE); } catch (AWTException ex) { //Logger.getLogger(JanChat.class.getName()).log(Level.SEVERE, null, ex); } } else { //add o \n no fim da msg mensagemText.setText(mensagemText.getText() + "\n"); } } else if (evt.getKeyCode() == KeyEvent.VK_SHIFT) { //shift pressionado shiftOn = true; } }//GEN-LAST:event_mensagemTextKeyPressed
public void printTela(String msg) { //salva a mensagem no historico if(ativarCheckBox.isSelected()){ CtrlChat.getInstance().salvarHistorico(msg); } //concatena com a conversa ja existente msg = conversaText.getText() + msg;
//mostra na propria tela conversaText.setText(msg); //autoscroll conversaText.setCaretPosition(conversaText.getDocument().getLength()); }
public void setComponentesEnable(boolean ativo) { mensagemText.setEnabled(ativo); conversaText.setEnabled(ativo); enviarButton.setEnabled(ativo);
}
public static void main(String args[]) { /* * Set the Windows look and feel */ //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) "> /* * If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html */ try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Windows".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break;
jPanel1 = new javax.swing.JPanel(); panelPrincipal = new javax.swing.JPanel(); opcoesPanel = new javax.swing.JPanel(); filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 10), new java.awt.Dimension(0, 10), new java.awt.Dimension(32767, 10)); primeiraLinhaPanel = new javax.swing.JPanel(); nomePanel = new javax.swing.JPanel(); nomeLabel = new javax.swing.JLabel(); nomeText = new javax.swing.JTextField(); segundaLinhaPanel = new javax.swing.JPanel(); ipPanel = new javax.swing.JPanel(); ipLabel = new javax.swing.JLabel(); ipText = new javax.swing.JTextField(); botaoPanel = new javax.swing.JPanel(); aplicarBotao = new javax.swing.JButton();
/* * Esses Métodos são os eventos dos componentes do frame. */ // <editor-fold defaultstate="collapsed" desc="Eventos de Componentes"> private void aplicarBotaoActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aplicarBotaoActionPerformed this.setVisible(false); if (!CtrlChat.getInstance().getNome().equals(nomeText.getText())) { CtrlChat.getInstance().setNome(nomeText.getText()); } if (!CtrlChat.getInstance().getIpServidor().equals(ipText.getText())) {
/* * Esse método é chamado para configurar o frame. */ // <editor-fold defaultstate="collapsed" desc="Método de configuração do Frame"> private void initFrame() { super.setTitle("CHAT JMS - Configurações"); this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); this.setModal(true);
try { //adição do icone ao programa super.setIconImage(ImageIO.read(getClass().getResource("/chat/img/icone.png"))); } catch (IOException ex) { System.err.println("Icone não encontrado!"); } }// </editor-fold> // <editor-fold defaultstate="collapsed" desc="Declaração dos Componentes do Frame"> // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton aplicarBotao; private javax.swing.JPanel botaoPanel; private javax.swing.Box.Filler filler1; private javax.swing.JLabel ipLabel; private javax.swing.JPanel ipPanel; private javax.swing.JTextField ipText; private javax.swing.JPanel jPanel1; private javax.swing.JLabel nomeLabel; private javax.swing.JPanel nomePanel; private javax.swing.JTextField nomeText; private javax.swing.JPanel opcoesPanel; private javax.swing.JPanel panelPrincipal; private javax.swing.JPanel primeiraLinhaPanel; private javax.swing.JPanel segundaLinhaPanel; // End of variables declaration//GEN-END:variables // </editor-fold>}
public interface DAO<T> extends Serializable{ public T salvar(T obj) throws Exception; public void excluir(T obj) throws Exception; public List<T> obter(Class<T> classe) throws Exception; public T obter(Class<T> classe, Long id) throws Exception ; }
try { // Inicia uma transação com o banco de dados. entityManager.getTransaction().begin(); // Verifica se o objeto ainda não está salvo no banco de dados. if (obj.getId() == null) { //Salva os dados do objeto. entityManager.persist(obj); } else { //Atualiza os dados do objeto. obj = entityManager.merge(obj); } // Finaliza a transação. entityManager.getTransaction().commit(); } catch (Exception e) { obj = null; System.err.println("Erro ao obter " + e); throw new Exception("Erro ao obter " + e); } return obj; }
public void excluir(T obj) throws Exception { try { // Inicia uma transação com o banco de dados.
entityManager.getTransaction().begin(); // Remove o objeto da base de dados. entityManager.remove(obj); // Finaliza a transação. entityManager.getTransaction().commit(); } catch (Exception e) { System.err.println("Erro ao excluir " + e); throw new Exception("Erro ao excluir " + e); } }
public List<T> obter(Class<T> classe) throws Exception { List<T> lista = null; try { Query query = entityManager.createQuery("SELECT t FROM " + classe.getSimpleName() + " t"); lista = query.getResultList(); } catch (Exception e) { System.err.println("Erro ao obter " + e); throw new Exception("Erro ao obter " + e); } return lista; } @Override public T obter(Class<T> classe, Long id) throws Exception { T obj = null; try { Query query = entityManager.createQuery("SELECT t FROM " + classe.getSimpleName() + " t where id = " + id); obj = (T)query.getSingleResult(); } catch (Exception e) { System.err.println("Erro ao obter " + e); throw new Exception("Erro ao obter " + e); } return obj; }}