Server RMI problem z interfejsem graficznym

0

Witam,

piszę sobie właśnie serwer wykorzystujący RMI i serwer ten powinien mieć interfejs graficzny. Generalnie problem wygląda tak, że klient zadaje pytanie serwerowi, a serwer musi na nie odpowiedzieć tak lub nie. Dlatego chcę zrobić dwa przyciski, które wyślą odpowiednią odpowiedź. Zatem należy nadpisać funkcję z interfejsu. Może pokażę trochę kodu:

Interfejs:

public interface MyInterface extends java.rmi.Remote
{
	boolean zapytaj(String pytanie) throws RemoteException;
}

Implementacja Serwera:

public class Server extends UnicastRemoteObject
implements MyInterface
{
    public Server () throws RemoteException
    {
        super();
        con = new EasyReader();
    }
    public boolean zapytaj(String pytanie) throws RemoteException
    {
    	System.out.println("Pytanie: "+pytanie);
    	String answer = con.readWord();
    	
    	if(answer.trim().toLowerCase().equals("tak"))
    		return true;
    	else{
    		System.out.println("NIE");
    		return false;
    	}
    }

    public static void main ( String args[] ) throws Exception
    {
        if (System.getSecurityManager() == null)
            System.setSecurityManager ( new RMISecurityManager() );

        // Create an instance of our power service server ...
        Server svr = new Server();

        // ... and bind it with the RMI Registry
        Naming.bind ("PowerService", svr);

        System.out.println ("Service bound....");
    }
}

No i teraz pytanie jak dorobić do tego interfejs graficzny? Klasa z interfejsem graficznym musi dziedziczyć po JFrame, a wiadomo, że w Javie dziedziczy się maksymalnie z jednej klasy. Poza tym, jak obsłużyć kliknięcie przycisku? Czy np. kliknięcie na TAK ma ustawić jakąś flagę, którą odczyta funkcja zapytaj(String)? Trochę to dla mnie niejasne. Fajnie by było gdyby ktoś zamieścił jakiś przykładowy kod z GUI. Z góry dzięki

0

Klasa z interfejsem graficznym musi dziedziczyć po JFrame
Wtf?, o kompozycji słyszałeś?

public class Jasiu extends Stasiu
{
     private JFrame okno;
     .... main(...)
     {
          new Jasiu()
     }
     public Jasiu
     {
          okno=new JFrame();
          ...
          okno.setVisible(true); 
     }
}

Przykładowy kod z GUI.

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

public class PobierzStrone extends JFrame implements ActionListener
{
    JTextArea contents=new JTextArea(20,80);
    JTextField host;
    Socket socket;
    //------------------------
    public static void main(String args[])
    {
        new PobierzStrone();
    }
    //------------------------
    public PobierzStrone()
    {
        super("Pobieranie strony");
        Font f=new Font("monospaced",Font.PLAIN,12);
    	setLayout(new BoxLayout(cp,BoxLayout.PAGE_AXIS));

    	add(new JLabel("Zasób do pobrania:"));
        host=new JTextField(80);
        host.addActionListener(this);
        host.setText("www.onet.pl");
        add(host);

        add(new JLabel("Zawartość strony:"));
        JScrollPane sp=new JScrollPane(contents);
        contents.setFont(f);
        sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        add(sp);

        pack();
        setLocationRelativeTo(null);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    //------------------------
    public void actionPerformed(ActionEvent ae)
    {
        String s=host.getText();
        String zapytanie="GET http://"+s+"/ HTTP/1.1"
        +"\r\n"+"Host: www.dom.pl"
        +"\r\n"+"User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; pl; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12"
        +"\r\n"+"Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
        +"\r\n"+"Accept-Language: pl,en-us;q=0.7,en;q=0.3"
        +"\r\n"+"Accept-Encoding: gzip,deflate"
        +"\r\n"+"Acept-Charset: ISO-8859-2,utf-8;q=0.7,*;q=0.7"
        +"\r\n"+"Keep-Alive: 300"
        +"\r\n"+"Proxy-Connection: keep-alive"
        +"\r\n\r\n";
        try
        {
            socket=new Socket(s,80);
            StringBuffer bufor=new StringBuffer();
            BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            BufferedWriter out=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            String linia;;
            out.write(zapytanie);
            out.flush();
            while ((linia=in.readLine())!=null)
            {
                bufor.append(linia+"\n");
            }
            contents.setText(bufor.toString());
            host.selectAll();
        }
        catch(Exception e)
        {
            contents.setText("Błąd: "+e.toString());
        }
    }
}
0

No zrobiłem jak napisałeś, ale jeszcze pozostaje problem z obsługą przycisków (kliknięcie tak lub nie wysyła odpowiedź do klienta). Jak to zaimplementować w funkcji zapytaj tak, żeby funkcja czekała na kliknięcie któregoś z przycisków i wysłała odpowiednią wiadomość?

0

Do przycisków podpinasz ActionListenera, mogą byc dwa różne Listenery, może być jeden

  button.addActionListener(...);

Jeżeli jest jeden wspólny Listener, to w jego metodzie actionPerformed() rozpoznaj który przycisk był źródłem

public void actionPerformed(ActionEvent ae)
{
    JButton src=(JButton (ae.getSource()))
    if(src==buttonTak)
    {
        //coś wyślij
    }
    else
    {
        //wyślij coś zupełnie innego
    }
}
0

Ok, to mam zrobione:

@Override
	public void actionPerformed(ActionEvent e) {
		// TODO Auto-generated method stub
		if(e.getSource().equals("tak")){
			odpowiedz = "tak";
		}
		else if(e.getSource().equals("nie")){
			odpowiedz = "nie";
		}
	}

Problem sprawia mi połączenie tego listenera z funkcją zapytaj udostępnianą przez serwer.
Funkcja zapytaj:

public boolean zapytaj(String pytanie) throws RemoteException
    {
    	System.out.println("Pytanie: "+pytanie);
    	pytania.append(pytanie+"\n");
    	
        //odpowiedz czytana jest tutaj z konsoli, ale trzeba zrobic tak, zeby klikniecie przycisku powodowalo wyslanie wiadomosci
    	String odpowiedz = con.readWord();
    	
    	if(odpowiedz.trim().toLowerCase().equals("tak"))
    		return true;
    	else{
    		System.out.println("NIE");
    		return false;
    	}
    }
0

Po pierwsze nie zgadzają się typy

        public  void actionPerformed(ActionEvent  e) {
                // TODO Auto-generated method stub
                if(e.getSource().equals("tak")){
                       //tu wywołaj zapytaj()
                        odpowiedz = "tak";
                }
                else if(e.getSource().equals("nie")){
                        odpowiedz = "nie";
                }
        }

e.getSource() jest typu Object, a porównujesz ze Stringiem
P drugie, kto Ci broni wywołać punkcję zapytaj() z funkcji actionPerformed()?

0

Nie wiem czy pisałeś kiedyś aplikację serwer-klient z wykorzystaniem RMI. Po pierwsze serwer nie wywołuje metody zapytaj. Metoda ta jest zdefiniowana na serwerze i może wywołuje ją klient.RMI polega na tym, że przed użytkownikiem ukryta jest cała komunikacja pomiędzy klientem a serwerem (sockety, porty itd). Klient wywołuje metodę z serwera tak jakby to była jego własna metoda. Dlatego nie da się wywołać metody zapytaj w listenerze. Metoda zapytaj powinna w jakiś sposób czekać na akcję przycisku. Czyli wypisuje pytanie od klienta na ekranie serwera, a serwer klika "Tak" lub "Nie" i metoda zapytaj wykonuje się dalej. Nie wiem właśnie jak zrobić to oczekiwanie.

0

Pisałem, ale tak dawno, że wszystko zapomniałem.
Może utwórz zmienną trójwartościową (np. "Tak","Nie",""), normalnie ma ona wartość "", kliknięcie zmienia na "Tak" lub "Nie", funkcja zapytaj() przywraca wartość "".

0

No ok ale jak w funkcji zapytaj(String..) zaczekać na kliknięcie przycisku? Próbowałem coś takiego:

while(odpowiedz.equals("")
     wait();

a w actionListener chciałem zrobić coś takiego, że po ustawieniu odpowiedzi na "tak" lub "nie" daję jeszcze notifyAll();. Niestety nie działało.

0

Ty ale co Ty masz za problem. Masz serwer ktory implementuje metode zaptyaj(), wiec w niej wywolaj JoptionPane.showConfirmDialog(pytanie, JOptionPane.YES_NO_OPTION), pokaze sie okienko i zablokuje metode dopoki ktos na serwerze nie kliknie tak lub nie, nastepnie zwroci inta ktory bedzie odpowiadal wybranej opcji, sprawdz tego inta i zwroc odpowiedni wynik. Wszystko na temat.
Mysle ze aby zapobiec wywolywaniu tej metody przez wilu klientow na raz, a wiec pojawianiu sie wielu okienek z pytaniem na raz, mozesz ja zrobic synchronized - dopoki jedno okienko sie nie ukryje i metoda nie wroci, nie pokaze sie nastepne.

0

Ok dzięki ostatniej wskazówce problem został rozwiązany. Jednak nie zaprojektowałbym tak swojej aplikacji, bo według mnie pojawianie się mnóstwa okienek jest nieco uciążliwe dla użytkownika. Wolałbym mieć te przyciski na panelu i tam klikać. W dalszym ciągu pozostaje zatem pytanie jak zaczekać na kliknięcie przycisku?

0

Jest wiele sposobow. Np. mozesz uzyc jakiejs kolejki blokujacej.
Sam program ktory piszesz wydaje mi sie bez sensu, z serwerem ktory pokazuje okienka z pytaniami i czeka na odpowiedz usera przed kompem - dziwny jakis ten serwer.

0

Wiem, że dziwny. Serwer powinien wszystko sam robić bez jakiegoś wsparcia człowieka, ale takie jest wymaganie prowadzącego. Możesz podać jakieś nazwy tych kolejek blokujących? Jakieś linki do przykladów itp?

1 użytkowników online, w tym zalogowanych: 0, gości: 1