
// URLChat.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th
/*
  Diskusní klient používá servlet jako server. Zprávy
  se posílají jako argumenty URL-adresy servletu.
  Reakce mají textovou podobu (často jen "ok" nebo "no").

  Při prvním připojení získá klient cookie hodnotu, kterou
  bude používat v následující komunikaci (společně se svým 
  jménem) jako jednoduchý způsob potvrzení své identity.
  Cookie obsahuje uživatelovo ID (UID).

  
  Zprávy klientů:
    * ChatServlet?cmd=hi&name=??         
        // pozdrav žádá o připojení k diskusní skupině. Server
           vrátí uid cookie, nebo klienta odmítne.

    * ChatServlet?cmd=bye&name=?? + uid cookie
        // rozloučení signalizuje odchod klienta

    * ChatServlet?cmd=who
       // nevyžaduje jméno ani cookie. vrátí seznam aktuálně přihlášených 
          klientů

    * ChatServlet?cmd=msg&name=??&msg=?? + uid cookie
        // přidá příspěvek do seznamu

   
  Existuje zvláštní vlákno URLChatWacther, které pravidelně zasílá
  servletu zprávy "read":

      ChatServlet?cmd=read&name=??  + uid cookie
        // požádá o všechny veřejné příspěvky uložené na server od posledního
           čtení

  Údaje o URL-adresách a hodnotách cookies na straně klienta:
    http://chantal.nobilitas.com/~martin/java/cookies.html
    http://javaalmanac.com/egs/java.net/GetCookies.html


  ---- Změny: 30. dubna 2004 ---

  Upravena metoda showMsg() tak, aby posunula pozici kurzoru a využila
  metodu invokeLater, čímž zabrání problémům, které vyvstávají z knihovnou
  Swing a vlákny. Více informace najdete na adrese:  
  http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
    - poděkování patří Rachel Struthersové (rmstruthers@mn.rr.com)

  Volání konstruktoru URLChatWatcher() přesunuto za otevření okna.

  Port serveru změněn z 8080 na 8100.
*/

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;


public class URLChat extends JFrame implements ActionListener
{
  private static final String SERVER = "http://localhost:8100/servlet/ChatServlet";
    // argumenty pro komunikaci s objektem typu ChatServlet se přidávají k tomuto řetězci

  private String userName;  // pro tohoto klienta
  private String cookieStr = null;
  
  private JTextArea jtaMesgs;   // komponenty GUI
  private JTextField jtfMsg;
  private JButton jbWho;


  public URLChat(String nm)
  {
     super( "Diskusní klient na bázi URL-adresy: "+ nm);

     userName = nm;
     initializeGUI();

     // nastav vlatnosti pro URL timeouty (v milisekundách)
     Properties props = System.getProperties();
     props.put("sun.net.client.defaultConnectTimeout", "2000");
     props.put("sun.net.client.defaultReadTimeout", "2000");
     System.setProperties(props);

     sayHi();
     // new URLChatWatcher(this, userName, cookieStr).start();    

     addWindowListener( new WindowAdapter() {
       public void windowClosing(WindowEvent e)
       { sayBye(); }
     });

     setSize(300,450);
     setVisible(true);

     new URLChatWatcher(this, userName, cookieStr).start();    
         // začni zasílat dotazy na server a zjišťovat tak nové zprávy,
         // které následně zapisuj do textové oblasti jtaMesgs
             // změna: upraveno, aby se před vytvořením spojení zobrazilo okno

  } // konec metody: URLChat();


  private void initializeGUI()
  /* Textová oblast ve středu a ovládaní pod ní.
     Ovládání:
         - textové pole pro zadávání zpráv
         - tlačítko "Kdo je přítomen"
  */
  {
    Container c = getContentPane();
    c.setLayout( new BorderLayout() );

    jtaMesgs = new JTextArea(7, 7);
    jtaMesgs.setEditable(false);
    JScrollPane jsp = new JScrollPane( jtaMesgs);
    c.add( jsp, "Center");

    JLabel jlMsg = new JLabel("Zpráva: ");
    jtfMsg = new JTextField(15);
    jtfMsg.addActionListener(this);    // stisknutí klávesy enter spustí odeslání zprávy

    jbWho = new JButton("Kdo je přítomen");
    jbWho.addActionListener(this);

    JPanel p1 = new JPanel( new FlowLayout() );
    p1.add(jlMsg); p1.add(jtfMsg);

    JPanel p2 = new JPanel( new FlowLayout() );
    p2.add(jbWho);

    JPanel p = new JPanel();
    p.setLayout( new BoxLayout(p, BoxLayout.Y_AXIS));
    p.add(p1); p.add(p2);

    c.add(p, "South");

  }  // konec metody: initializeGUI()



  private void sayHi()
  /* Formát zprávy: ChatServlet?cmd=hi&name=??         
     Klient žádá o připojení k diskusní skupině. 
     Server vrátí uid cookie jako hlavičku nebo klienta odmítne.
     Odmítnutí způsobí ukončení klienta.
     Textovými odpověďmi mohou být "ok" nebo "no".
  */
  {
    try {
      URL url  = new URL(SERVER + "?cmd=hi&name=" +  
							URLEncoder.encode(userName, "UTF-8") );

      URLConnection conn = url.openConnection();
      cookieStr = conn.getHeaderField("Set-Cookie");  // vezmi hodnotu cookie

      System.out.println("Received cookie: " + cookieStr);
      if (cookieStr != null) {
        int index = cookieStr.indexOf(";"); 
        if (index != -1) 
          cookieStr = cookieStr.substring(0, index);  // odřež vše navíc
      }

	  BufferedReader br = new BufferedReader(
              new InputStreamReader( conn.getInputStream() ));	
      String response = br.readLine().trim();
      br.close();

      if (response.equals("ok") && (cookieStr != null))
        showMsg("Server Login Successful\n");
      else { 
        System.out.println("Server přihlášení odmítl"); 
        System.exit(0);
      }
    }
    catch(Exception e)
    { System.out.println(e);  
      System.exit(0);
    }
  }  // konec metody: sayHi()



  private void sayBye()
  /* ChatServlet?cmd=bye&name=?? + uid cookie
     Zprává "bye" signalizuje, že klient opouští server.
     Reakcí bude "ok" nebo "no", i když klient odejde ať tak či tak.
  */
  {
    try {
      URL url  = new URL(SERVER + "?cmd=bye&name=" + 
							URLEncoder.encode(userName, "UTF-8") );
      URLConnection conn = url.openConnection();
      conn.setRequestProperty("Cookie", cookieStr); 

	  BufferedReader br = new BufferedReader(
              new InputStreamReader( conn.getInputStream() ));	
      String response = br.readLine().trim();
      br.close();

      if (response.equals("ok"))
        System.out.println("Úspěšné odhlášení od serveru");
      else // assume "no"
        System.out.println("Server odhlášení odmítl"); 
    }
    catch(Exception e)
    {  System.out.println( e );  }

    System.exit(0); 
  } // konec metody: sayBye()




   public void actionPerformed(ActionEvent e)
   /* Buď se má odeslat zpráva nebo bylo stisknuto tlačítko "Kdo je přítomen" */
   { if (e.getSource() == jbWho)
       askWho();
     else if (e.getSource() == jtfMsg)
       sendMessage();
   } // konec metody: actionPerformed()


  private void askWho()
  /*  Formát zprávy: ChatServlet?cmd=who
      Zpráva nevyžaduje jméno ani hodnotu cookie.
      Reakcí je seznam aktuálně přihlášených klientů.
      nebo "no". Seznam se zobrazí v textové oblasti jtsMesgs.
  */
  {
    try {
      URL url  = new URL(SERVER + "?cmd=who" );

	  BufferedReader br = new BufferedReader(
              new InputStreamReader( url.openStream() ));

      String line;
      StringBuffer resp = new StringBuffer();
      while ((line = br.readLine()) != null)   // dostali jsme vícero řádků
        resp.append(line+"\n");
      br.close();

      String response = resp.toString();
      if (response.equals("no"))
        showMsg("Server požadavek who odmítl\n");
      else // předpokládáme, že se má zobrazit seznam
        showMsg(response); 
    }
    catch(Exception e)
    { showMsg("Chyba servletu. Nebylo stisknuto tlačítko Kdo je přítomen\n");
      System.out.println(e);  
    }
  }  // konec metody: askWho()


  private void sendMessage()
  /* Ověří, zadal-li uživatel zprávu, a poté ji odešle servletu. */
  {
    String msg = jtfMsg.getText().trim();
    // System.out.println("'"+msg+"'");

    if (msg.equals(""))
      JOptionPane.showMessageDialog( null, 
           "Nebyla zadána zpráva", "Send Message Error", 
			JOptionPane.ERROR_MESSAGE);
    else {
      sendURLMessage(msg);
      // showMsg("Sent: " + msg + "\n");
    }
  }  // konec metody: sendMessage()



  private void sendURLMessage(String msg)
  /* Formát zprávy: 
        ChatServlet?cmd=msg&name=??&msg=?? + uid cookie

     Zpráva bude přidána do seznamu servletu.
     Reakcí je "ok" nebo "no".
  */
  {
    try {
      URL url  = new URL(SERVER + "?cmd=msg&name=" +  
							URLEncoder.encode(userName, "UTF-8") +
                            "&msg=" + 
							URLEncoder.encode(msg, "UTF-8") );

      URLConnection conn = url.openConnection();
      conn.setRequestProperty("Cookie", cookieStr); 

	  BufferedReader br = new BufferedReader(
              new InputStreamReader( conn.getInputStream() ));	
      String response = br.readLine().trim();
      br.close();

      if (!response.equals("ok"))
        showMsg("Odeslání zprávy odmítnuto\n");
      else // okamžitě zobraz zprávu
        showMsg("(" + userName + ") " + msg + "\n");
    }
    catch(Exception e)
    { showMsg("Chyba servletu. Neposlal: " + msg + "\n");
      System.out.println(e);  
    }
  }  // konec metody: sendURLMessage()


/*
  synchronized public void showMsg(String msg)
  // Metodu je nunté synchronizovat, neboť může být volána jak
     tímto objektem, tak i vláknem URLChatWatchcer.
  { jtaMesgs.append(msg);  }
*/

  public void showMsg(final String msg)
  /* Aktualizujeme textovou oblast se zprávami, takže kód by
     mělo provést vlákno obsluhující události knihovny Swing,
     čehož docílíme zavoláním metody invokeLater().

     Parametr msg musí být finalní, aby jej bylo možné použít ve vložené
     třídě pro rozhraní Runnable.
   
     Metoda showMsg() může být volána tímto objektem a vláken URLWatcher,
     ovšem aktualizace se serializují tím, že se umísťují do fronty 
     dispečera událostí. To znamená, že tuto metodu synchronizovat nemusíme.

     Poděkování náleží Rachle Struthersové (rmstruthers@mn.rr.com)
  */
  { 
    // System.out.println("showMsg(): " + msg);
    Runnable updateMsgsText = new Runnable() {
      public void run() 
      { jtaMesgs.append(msg);  // připoj zprávu do textové oblasti
        jtaMesgs.setCaretPosition( jtaMesgs.getText().length() );
            // přesuň vkládací pozici na konec textu
      }
    };
    SwingUtilities.invokeLater( updateMsgsText );
  } // konec metody: showMsg()



  // ------------------------------------

  public static void main(String args[]) 
  { if (args.length != 1) {
       System.out.println("použití:  java URLChat <your userName>");
       System.exit(0);
     }
     new URLChat(args[0]);  
  }

} // konec třídy: URLChat

