Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Witajcie,

Zaczynam właśnie naukę Javy więc proszę o wyrozumiałość ;)
Nie wiem jak rozwiązać pewien problem z otwieraniem okienka. Piszę program w uproszczeniu coś ala chat.
Mam aplikację, która jest serwerem oraz klienta. Klienci podłączają się do serwera dla każdego klienta odpala się osobny wątek, który jest odpowiedzialny za przyjmowanie nowych wiadomości. w Serwerze mam listę, na której wyświetlani są podłączeni klienci. W serwerze stworzyłem klasę odpowiedzialną za tworzenie okienka w którym będzie prowadzona konwersacja. Za każdym razem gdy podłączy się klient do serwera tworzony jest nowy obiekt tego okienka przez wątek obsługujący komunikację z klientem. Następnie wiadomość która przychodzi jest dopisywana do pola w okienku i okienko jest wizualizowane na ekranie. W tą stronę działa OK. Mam problem jak to okienko wywołać gdy kliknę na liście podłączonych klientów w daną osobę, z którą chcę rozmawiać. Muszę jakoś dotrzeć do odpowiedniego wątku i ustawić setVisibility(true) dla obiektu okienka. Nie mam pojęcia jak to zrobić.... :/

Pozdr.
JN
Marcin Mroczkowski

Marcin Mroczkowski Programista JAVA/JEE

Temat: Jak dotrzeć do obiektu...

Jakiej technologii użyłeś do połączenia klienta z serwerem ? Z twojego opisu wynika, że użyłeś socketów i na serwerze dla każdego łączącego się klienta powstaje wątek obsługujący socket dla danego klienta. Sockety TCP obsługują obustronną komunikację i możesz po prostu przez ten socket przesłać informację zwrotną do klienta.
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Tak użyte są sockety ale nie w tym dokładnie jest problem. Nie wiem jak otworzyć to okienko z poziomu innej klasy niż ten wątek.

JN

Temat: Jak dotrzeć do obiektu...

Jakub N.:
Tak użyte są sockety ale nie w tym dokładnie jest problem. Nie wiem jak otworzyć to okienko z poziomu innej klasy niż ten wątek.

JN

Z opisu wynika, że okienko jest własnością konkretnego wątku dla konkretnego kanału klient <-> serwer (konwersjacji). Jeśli tak to masz poważny błąd w architekturze.

Ponad wątkiem głównym serwera czyli tym, który odpowiada za przyjmowanie połączeń i odpalanie właściwych wątków komunikacyjnych (czyli pewnie tam gdzie tworzysz listę podłączonych klientów) stwórz kolekcję synchrnizowaną do której będziesz wrzucał okienka w stanie niewidocznym w chwili gdy klient się połączy.

Tyle jeśli chodzi o "znajdowanie okienka". Natomiast przydał by się jeszcze synchronizowany mechanizm kolejkowania wiadomości aby oddzielić warstwę prezentacji (okienka) od logiki.
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Ok jest wskazówka, teraz muszę trochę pogrzebać w necie aby rozszyfrować kwestię tworzenia kolekcji synchronizowanej ;) Chyba do tego jeszcze nie dotarłem czytając książkę bo brzmi strasznie tajemniczo;)
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Pewnie chodzi o ArrayList...

Jeżeli dobrze rozumiem mam stworzyć kolekcję czyli np. ArrayList (chociaż HashMap wydaję się bardziej odpowiednia do tego celu) z tymi okienkami. Tylko co to znaczy ponad wątkiem głównym? Czy mam stworzyć klasę nadrzędną zawierającą tą listę?Ten post został edytowany przez Autora dnia 05.06.13 o godzinie 14:16
Marcin Mroczkowski

Marcin Mroczkowski Programista JAVA/JEE

Temat: Jak dotrzeć do obiektu...

Masz rację, mapa będzie znacznie lepsza (żeby uniknąć synchronizacji możesz użyć ConcurrentHashMap), bo będziesz mógł z niej wyciągać szukane obiekty po kluczu. W tym przypadku najlepiej będzie przypisać klientom jakieś klucze (integer, czy String, nie ważne) i po tym ich identyfikować.
Jeśli nie masz pomysłu gdzie powinna być ta kolekcja, to mam pytanie, gdzie teraz przechowujesz listę klientów wyświetlaną na głównym oknie serwera, lub jeśli takowej nie ma, to gdzie dodajesz klientów do okna ? W tej samej klasie utwórz pole, które będzie zawierało mapę i będziesz miał do niej dostęp w całym kodzie klasy.

Niestety, tak jak mówi Piotr, te podejście jest bardzo niepoprawne architekturalnie. Co więcej, standardowe okna Javy (czyli tak zwany SWING), nie są bezpieczne przy użyciu wielu wątków i jeśli używasz setVisible() bezpośrednio z twojego wątku, to aplikacje może być niestabilna i powodować losowe błędy. Jednakże, moim zdaniem nie powinieneś ogarniać tego wszystkiego na raz. Wróć do tych zagadnień, jak będziesz czuł się pewnie w samym języku i jego wielowątkowości.
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Marcin M.:
Masz rację, mapa będzie znacznie lepsza (żeby uniknąć synchronizacji możesz użyć ConcurrentHashMap), bo będziesz mógł z niej wyciągać szukane obiekty po kluczu. W tym przypadku najlepiej będzie przypisać klientom jakieś klucze (integer, czy String, nie ważne) i po tym ich identyfikować.
Jeśli nie masz pomysłu gdzie powinna być ta kolekcja, to mam pytanie, gdzie teraz przechowujesz listę klientów wyświetlaną na głównym oknie serwera, lub jeśli takowej nie ma, to gdzie dodajesz klientów do okna ? W tej samej klasie utwórz pole, które będzie zawierało mapę i będziesz miał do niej dostęp w całym kodzie klasy.

Prawdę mówiąc mój pierwotny pomysł opierał się na przechowywaniu okienek w tablicy arraylist niestety nie mogłem sobie poradzić z przekazywaniem informacji do pola jTextPane w okienku. Nie chciało się wyświetlić...
Listę klientów w tej chwili przechowuję w głównej klasie jeżeli tak to można nazwać, w tablicy arraylist jako obiekty. W ramach tej klasy tworzone są wątki obsługujące klientów, wątek sprawdzający czy klienci są aktywni oraz z jej poziomu tworzony jest obiekt z okienkiem głównym programu. W tym okienku jest między innymi jList w której wyświetla się aktualna lista podłączonych klientów na podstawie tej tablicy arrayList.

Pierwotnie po 2x kliknięciu na tej liscie na danego klienta tworzyłem nowy obiekt okienka i zapisywałem go w tablicy arraylist wraz z numerem indywidualnym klienta. Następnie wyświetlałem okienko. W momencie gdy przychodziła wiadomość od klienta wątek obsługujący komunikację brał po kolei z arraylisty obiekty okienek, sprawdzał numer klienta i jeżeli okazywało się że to okienko co trzeba zapisywał wiadomość do zmiennej jTextPane tego obiektu niestety nie powodowało to wyświetlenia informacji. Co ciekawe mogłem bez problemu manipulować tekstem w nagłówku okienka.

Wyglądało to mniej więcej tak:
Tworzę okienko i zapisuje je w tablicy
PersonalWindow m = new PersonalWindow();
SerwerWiadomosci.OkienkaPrywatne = new ArrayList<PersonalWindow>();
SerwerWiadomosci.OkienkaPrywatne.add(m);
m.setVisible(true);

Następnie gdy chcę coś napisać:
PersonalWindow p = new PersonalWindow();
p = OkienkaPrywatne.get(0);
p.jWiadomosc.setText("jakiś tekst");

Prawdopodobnie to nie ten sam obiekt tylko dwa niezależne od siebie..
Jeżeli dobrze rozumiem to proponujecie coś na kształt tego rozwiązania więc nie wiem dokładnie jak to zrobić aby działało. Chyba, że to wątek obsługi klientów ma tworzyć te okno i zapisywać w kolekcji. Następnie z poziomu okna głównego po dwukrotnym kliknięciu na liście w danego klienta pobierany ma być obiekt z kolekcji z okienkiem i wizualizowany. Dobrze rozumiem??
Niestety, tak jak mówi Piotr, te podejście jest bardzo niepoprawne architekturalnie. Co więcej, standardowe okna Javy (czyli tak zwany SWING), nie są bezpieczne przy użyciu wielu wątków i jeśli używasz setVisible() bezpośrednio z twojego wątku, to aplikacje może być niestabilna i powodować losowe błędy. Jednakże, moim zdaniem nie powinieneś ogarniać tego wszystkiego na raz. Wróć do tych zagadnień, jak będziesz czuł się pewnie w samym języku i jego wielowątkowości.

Oczywiście w miarę zdobywania wiedzy program mogę poprawiać i zapewne tak się będzie działo... ;)
Wydawało mi się, że będzie to na początek coś łatwego do pewnego momentu szło całkiem nieźle, problemem stały się dopiero te okienka...

Temat: Jak dotrzeć do obiektu...

Jakub N.:

<ciachownica>
Prawdopodobnie to nie ten sam obiekt tylko dwa niezależne od siebie..
Jeżeli dobrze rozumiem to proponujecie coś na kształt tego rozwiązania więc nie wiem dokładnie jak to zrobić aby działało.

Wystaw proszę kod na GitHub lub zwyczajnie .zip/tar.gz/.rar gdzieś do ściągnięcia bo tak to się możemy w koło ganiać.
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Projekt z netbeans.
http://nykiel.nazwa.pl/tmp/serwer.zip
Szymon Lisiecki

Szymon Lisiecki Senior Software
Engineer

Temat: Jak dotrzeć do obiektu...

Jakub N.:
PersonalWindow p = new PersonalWindow();
p = OkienkaPrywatne.get(0);

Po co tworzysz nowy obiekt skoro za chwilę go nadpisujesz już istniejącym?

Jeśli już to powinno być tak:
PersonalWindow p = OkienkaPrywatne.get(0);


Kilka moich uwag:
Od razu przyznam że nie zagłębiałem się bardzo w kod, jednak wydaje mi się dziwne, że serwer tworzy jakiekolwiek okienka.
To klienci (na swoich komputerach) powinni mieć okienko z zapisem rozmowy (ewentualnie mogą tworzyć kolejne okienka do rozmów prywatnych), a serwer powinien być odpowiedzialny jedynie za przekazywanie komunikatów. Tylko i wyłącznie przekazywanie komunikatów - sam nic nigdzie nie wyświetla.

Komunikacja powinno wyglądać tak:
Klient pisze komentarz i klika wyślij -> serwer odbiera komunikat i rozsyła go do wszystkich podłączonych klientów. Klienci odczytują komunikat i wyświetlają w swoim "głównym" oknie.

Rozmowy prywatne powinny się odbywać w ten sam sposób z tym że serwer rozsyła komunikat tylko do 2 stron rozmowy.

Komunikację oparłeś na socket'ach - ok, jednak polecam bibliotekę, która jest idealna do zastosowania w tym przypadku - http://code.google.com/p/kryonet/. "Pod spodem" tej biblioteki są oczywiście użyte sockety, jednak używając tej biblioteki oszczędzasz sporo czasu :)

Powodzenia
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Hej,

Ten program specjalnie tak zrobiłem aby serwer dawał możliwość komunikacji z klientami.
Chcę ten program wykorzystać kiedyś u siebie w pracy do wysyłania komunikatów dla użytkowników.
Oni nie będą mieli możliwości gadania między sobą, to tylko dla mnie. Właściwie w pierwotnym zamyśle nie miało być opcji odpalania osobnych okienek dla konkretnych użytkowników ale stwierdziłem, że tak będzie lepiej chcąc wysłać komunikat do konkretnej osoby i dostać odpowiedź do osobnego okienka zamiast do ogólnego.

Przyjrzę się tej bibliotece o której piszesz, jak ma sprawę uprościć to tylko lepiej.
Zaraz sprawdzę Twoją sugestię dotyczącą ładowania obiektu być może tutaj tkwi problem.

Pozdr.
JN
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

teraz działa, problem polegał na tym, że tworzyłem nowy obiekt niepotrzebnie ;)
Dzięki za pomoc ;)
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Pojawił się następny problem.
Gdy otworzę np. dwa okienka do dwóch różnych klientów to wysyłane wiadomości pojawiają mi się w drugim okienku. Tak jakby okienko oddziaływało na pola drugiego zamiast na swoje. Wystawiłem kod do ściągnięcia gdy ktoś chciał zobaczyć jak jest to teraz zrobione.

w skrócie, okienko tworzone jest podczas podłączenia się klienta

PersonalWindow m = new PersonalWindow();
m.SetNr(k.getNrKlienta());
m.setGniazdo(k.getKlientSocket());
m.setTitle("Rozmawiam z: " + k.getHostName());
SerwerWiadomosci.PersonalW.put(k.getNrKlienta(), m);

Następnie po kliknięciu na liście z klientami odczytuje obiekt z listy i wizualizuję okienko.

PersonalWindow s = (PersonalWindow) SerwerWiadomosci.PersonalW.get(k.getNrKlienta());
s.setVisible(true);

Po kliknięciu w przycisk wyślij w okienku

try {
pisarz2 = new ObjectOutputStream(gniazdo.getOutputStream());
message b = new message("w", Twiadomosc.getText(), false);
pisarz2.writeObject(b);

this.jWiadomosc.setText(this.jWiadomosc.getText() + "\n" + "Ja: " + this.Twiadomosc.getText());
this.Twiadomosc.setText("");
} catch (IOException ex) {
Logger.getLogger(PersonalWindow.class.getName()).log(Level.SEVERE, null, ex);
}

Sytuacja wygląda tak, że w jedno okienko działa ok a w drugim jak wpisuje wiadomość i dam wyslij to pojawia mi się ona w polu jWiadomosc ale w okienku drugim. Wiadomość jest wysyłana poprawnie. Natomiast od klientów wiadomości pojawiają się w jednym okienku.

http://nykiel.nazwa.pl/tmp/serwer.zipTen post został edytowany przez Autora dnia 10.06.13 o godzinie 14:24
Jakub Nykiel

Jakub Nykiel Specjalista
Informatyk,
Wodociągi Płockie
Sp. z o.o.

Temat: Jak dotrzeć do obiektu...

Poradziłem sobie, moja wina.... modyfikowałem pola odwołując się do nich bezpośrednio zamiast stworzyć metodę zmieniającą wartość...

konto usunięte

Temat: Jak dotrzeć do obiektu...

To jakis blog czy cos?

konto usunięte

Temat: Jak dotrzeć do obiektu...

Nie, to wątek o tym, jak modyfikować pola odwołując się bezpośrednio do nich, zamiast stworzyć metodę zmieniającą wartość ;)

Adrian Stolarski

Wypowiedzi autora zostały ukryte. Pokaż autora

konto usunięte

Temat: Jak dotrzeć do obiektu...

Adrian S.:
Co to jest? :D

Rozwarstw i napisz testy. Najpierw jednostkowe dla metod, potem jakieś integracyjne i na końcu funkcjonalny dla wysyłania wiadomości. programuj i co jakiś czas odpalaj testy, jak bedziesz uważał, że jest ok. Test Ci pokaże, czy to jest ok.

Trzeba dodać - ten test który sam sobie wymyśliłeś i napisałeś. :)Ten post został edytowany przez Autora dnia 11.06.13 o godzinie 22:24

Adrian Stolarski

Wypowiedzi autora zostały ukryte. Pokaż autora

Następna dyskusja:

jak to napisać w j2ee




Wyślij zaproszenie do