Projektowanie i wytwarzanie aplikacji internetowych (część 7)

Sebastian Wyrwał

W poprzednim odcinku rozpoczęto prezentowanie przykładu
projektowego – aplikacji służącej do kształcenia zdalnego przy pomocy
Internetu. Językiem implementacji jest Java (j2sdk1.4.0), Do komunikacji użyto
architektury CORBA. Interakcji studenta z prowadzącym odpowiada obiekt klasy
Jednostka, którego klasą interfejsową jest JPanelPrezentacji. Współpraca
pomiędzy tymi klasami musi być zaprojektowana bardzo dokładnie, gdyż odbywa
się przy pomocy rozwiązań opartych o architekturę CORBA – pierwsza z klas
znajduje się po stronie klienta, druga zaś po stronie serwera. Dodatkowym
utrudnieniem jest fakt, iż interakcja może być w pewnych przypadkach
inicjowana przez serwer. Należy również pamiętać, iż klasa (bądź
interfejs) Jednostka jest bazowa dla wszystkich klas odpowiadających za
interakcję związaną z nauczaniem. Istotne jest również to, iż w systemie
istnieje spora ilość klas należących do pewnej hierarchii: np. klasy Student
oraz Prowadzący dziedziczą z klasy Osoba. Powoduje to, iż należy korzystać
z klas wiążących. Niniejszy artykuł ukazuje sposób realizacji współpracy
Jednostki z jej klasą interfejsową. Zaprezentowano działający szkielet
klienta studenckiego oraz prosty serwer. Umożliwią one przetestowanie przyjętych
rozwiązań. Prezentacja przykładowego rozwiązania wymusza zaprezentowanie większej
niż zwykle ilości kodu. Przeanalizowanie go nie powinno stanowić trudności
jako, że wszystkie wykorzystane rozwiązania zostały omówione na prostszych
przykładach. Pomocna w zrozumieniu prezentowanych przykładów będzie
encyklopedyczna prezentacja wykorzystywanych klas biblioteki Java Swing.
Pokazano również przykład obsługi zdarzeń.

W tym odcinku, dla zaoszczędzenia miejsca przy prezentacji
klas, zostaną pominięte importy1 – chcąc przetestować
prezentowane rozwiązania należy dopisać przed definicją każdej klasy następujące
polecenia:

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.CORBA.*;
import org.omg.PortableServer.*;
import org.omg.PortableServer.POA;
import java.util.*;

Bliżej implementacji – również o Swing’u

Zaprezentowane poniżej przykłady używają biblioteki Swing.
Bardzo pobieżna prezentacja klas tej biblioteki oraz klasy Vector będzie
ograniczać się do tych, które zostaną bezpośrednio użyte w projekcie. Używane
będą następujące klasy: JLabel, JButton JTextField, JPanel, JFrame,
JTextPane, Dokument, JScrollPane, Vector, Color, Style.

Powyższy, bardzo niewielki zestaw klas, daje dość szerokie
możliwości prezentacji – wyłączając jednakże odtwarzanie plików
muzycznych. Poniżej zostaną skrótowo omówione niektóre z wymienionych klas:

  • JLabel – jest etykietą, tworzy się ją w następujący
    sposób: JLabel label = new JLabel(„Proszę wpisać liczbę:”); jako
    parametru wywołania konstruktora można użyć zarówno napisu jak i
    obiektu typu imageIcon np.: JLabel label1 = new JLabel(new ImageIcon(„foto.jpg”));
  • JButton – przycisk, tworzy się go analogicznie
    jak etykietę np.: JButton b = new JButton(„Press me”); Metoda setMaximumSize(newDimension(d,w))
    służy do ustawienia maksymalnego rozmiaru komponentu.
  • JTextField – jest tekstowym polem edycyjnym.
    Tworzy się je następująco: JTextField jtf = new JTextField(„lola”); W
    konstruktorze można określić tekst jakim ma być ono zainicjowane lub
    rozmiar komponentu. Metoda getText() służy do pobrania tekstu
    wpisanego do komponentu
  • JPanel – jest pojemnikiem, który
    „przechowuje” inne komponenty. Komponenty dodaje się przy użyciu
    metody add() np. controlArea.add(loginButton); Ustawienie ramki z napisem
    realizuje się następująco:

    controlArea.setBorder(BorderFactory.createTitledBorder(„Sterowanie”));

  • JFrame – reprezentuje okno wyświetlane na
    ekranie użytkownika. Niewidoczne okno tworzy się następująco JFrame
    frame = new JFrame(); Trzeba jednak pamiętać, iż komponentów ani rozkładu
    nie dodaje się do samego okna, lecz do specjalnego pojemnika, który
    przechowuje komponenty. Aby go pobrać trzeba wywołać metodę: Container
    getContentPane(). Aby dodać komponent do okna frame pisze się
    frame.getContentPane().add(new JLabel(„Sunny”)); Metoda
    setSize(Dimension d) służy do ustawienia rozmiarów okna – można tego
    dokonać w następujący sposób: frame.setSize(new Dimension(100,200)); Do
    ustawiania tytułu okna służy metoda setTitle(String tytuł) a
    setResizable(boolean res) do włączenia/wyłączenia możliwości
    przeskalowania okna przez użytkownika. Metoda
    frame.getContentPane().setLayout(new FlowLayout()); służy do ustawienia
    rozkładu. W języku Java za rozmieszczanie wstawianych komponentów
    odpowiedzialny jest zarządca który ma kilka predefiniowanych rozkładów.
    W poniższych przykładach zostaną użyte następujące rozkłady: ciągły,
    torebkowy, brzegowy, pudełkowy. Zastosowanie rozkładu ciągłego (FlowLayout)
    powoduje, iż kolejne komponenty układane są poziomo jeden za drugim. Rozkład
    torebkowy (GridBagLayout) umożliwia większą kontrolę nad rozmieszczaniem
    komponentów, ale jest bardziej skomplikowany. Dla rozkładu tego definiuje
    się wymuszenia (GridBagConstraints) określające sposób układania
    komponentów. Ilustruje to poniższy przykład:


    GridBagLayout gridbag = new GridBagLayout();//utworzenie rozkładu
    torebkowego
    GridBagConstraints c = new GridBagConstraints();//utworzenie ograniczeń
    jPanel1.setLayout(gridbag);//ustawienie rozkładu torebkowego w panelu
    jPanel1
    c.fill = GridBagConstraints.BOTH;//komp. tak powiększyć aby zajmował całą
    komórkę
    c.weightx=0.05;//stosunek szerokości komponentu do innych komponentów
    c.gridx = 1;//współrzędna x komórki, którą zajmuje lewy narożnik
    komponentu
    c.gridy = 0;//współrzędna y
    c.weighty = 1;//waga wysokości
    c.ipady=10; //odstępy na brzegach
    c.gridheight=4;//liczba komórek jakie torebka zajmuje w pionie
    gridbag.setConstraints(controlArea,c);//ustawienie ograniczeń komp.
    controlArea
    jPanel1.add(controlArea);//dodanie komp. do panela
    ...
    c.fill = GridBagConstraints.BOTH;
    c.weightx=0.95;
    c.gridx = 0;
    c.gridy = 1;
    c.weighty = 0.7;
    c.gridheight=1;
    c.ipady=10;
    gridbag.setConstraints(orderAreaScroll,c);
    jPanel1.add(orderAreaScroll);

    Rozkład brzegowy (BorderLayut) jest o wiele prostszy od zaprezentowanego
    powyżej; wstawiając komponenty określa się obszar, do którego mają być
    one wstawiane. Identyfikatory obszarów są predefiniowane:
    BorderLayout.NORTH (Północ), BorderLayout.SOUTH (Południe),
    BorderLayout.WEST (Zachód), BorderLayout.EAST (Wschód),
    BorderLayout.CENTER (Środek).

    Rozkład pudełkowy pozwala zarówno na układanie komponentów pionowo
    jeden pod drugim, jak i w rzędzie jeden obok drugiego. Poniżej
    zaprezentowano rozkład pudełkowy, w którym komponenty będą układane
    jeden na drugim: controlArea.setLayout(new BoxLayout(controlArea,BoxLayout.
    Y_AXIS));.

  • JScrollPane – zapewnia możliwość przesuwania
    widoku komponentu. W jego konstruktorze można określić wyświetlany
    (przesuwany) komponent tzn.:


    JScrollPane orderAreaScroll = new JScrollPane(orderArea);

    Wywołanie następującej metody:


    orderAreaScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDE);

    Powoduje, iż pionowy pasek przewijania będzie wyświetlany w miarę
    potrzeby.

  • JTextPane – jest komponentem mającym
    funkcjonalność edytora tekstu o dużych możliwościach; przy pomocy
    metody getDocument() pobiera się dokument, który zawiera
    ów edytor. W obrębie edytora można zdefiniować różne style
    prezentacji:


    def=StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
    //pobranie stylu domyślnego na bazie którego definiujemy inne style
    regular=tp.addStyle("regular",def);
    StyleConstants.setFontFamily(def,"Java");//tekst z pisany czcionką Java
    _red=tp.addStyle("red",def);//czerwony tekst
    StyleConstants.setForeground(_red,Color.red);

  • Document – interfejs, który implementują
    dokumenty. W prezentowanym przykładzie istotne znaczenie ma metoda
    insertString, która służy do wstawiania zarówno tekstu jak i komponentów.
    Poniższa funkcja wstawia tekst na koniec dokumentu i korzysta z omawianej właśnie
    metody oraz z metody getLength() – która zwraca długość dokumentu:


    public void doText(String s)throws BadLocationException{
           
    doc.insertString(doc.getLength(),s,curStyle);
            }

    Wstawienie komponentu (np. przycisku) polega na zdefiniowaniu i dodaniu
    nowego stylu a następnie na wstawieniu łańcucha złożonego z samych
    spacji przy użyciu właśnie zdefiniowanego stylu.


    public int insertComponent(JComponent comp) throws BadLocationException{
                Style compstyle = tp.addStyle("compstyle"+styleno,def);
       
    StyleConstants.setAlignment(compstyle,StyleConstants.ALIGN_CENTER);
        StyleConstants.setComponent(compstyle,comp);
        doc.insertString(doc.getLength()," ",tp.getStyle("compstyle"+styleno));
        return 1;
    }

  • Vector – służy do przechowywania zbiorowości
    elementów klasy Object. Ponieważ każda klasa w języku Java
    dziedziczy z tej klasy, przechowywać można obiekt dowolnej klasy,
    z tym, że przy „wyjmowaniu” konieczne jest rzutowanie do właściwego
    typu. Klasa ta ma m.in. następujące metody: Vector() – konstruktor,
    add(Object o) – dołożenie nowego elementu, clear() – usunięcie
    wszystkich elementów, elementAt(int indeks) – zwraca element o indeksie
    i, size() – zwraca ilość elementów przechowywanych w wektorze.

    W przedstawianym systemie służyć on będzie np. do przechowywania
    jednostek wchodzących w skład wykładu. Można powiedzieć, iż wektor
    jest ulepszoną, wygodną w użyciu tablicą.

O zdarzeniach raz jeszcze

Idea obsługi zdarzeń w języku Java została zaprezentowana
w poprzednim odcinku. Jednak praktyczny przykład może być przedstawiony
dopiero teraz, po encyklopedycznym przedstawieniu kilku klas z biblioteki Swing.
Poniżej zostanie zaprezentowana prosta klasa wyświetlająca pole edycyjne i
przycisk. Zostanie ona z niewielkimi przeróbkami użyta w jednym z następnych
przykładów. W konstruktorze tej klasy tworzone jest okno (JFrame), pole
edycyjne (JTextField), przycisk (JButton). Implementuje ona interfejs
ActionListener, z czego wynika po pierwsze, iż musi ona implementować metodę2 actionPerformed, a po drugie, że może ona „nasłuchiwać” na zdarzenia
typu action wysłane między innymi przez przyciski. W wytłuszczonej linii w
przycisku b zarejestrowano obiekt nasłuchujący – jest nim właśnie tworzony
obiekt klasy MyImpl, do którego można się odwołać przy pomocy słowa
kluczowego this. Tak więc naciśnięcie przycisku spowoduje wywołanie metody
actionPerformed przedstawionej klasy i w rezultacie tego wyświetlanie
wprowadzonego do pola edycyjnego napisu jako tytułu okna.

public class MyImpl implements ActionListener{
    JFrame frame=null;
    JTextField jte = null;
public MyImpl(){
    super();
    frame = new JFrame();
    frame.getContentPane().setLayout(new FlowLayout());
    JButton b = new JButton("Naciśnij mnie");
    frame.setSize(300,100);
    jte = new JTextField(10);
    frame.setTitle("Tekst:");
    frame.getContentPane().add(jte);
    frame.getContentPane().add(b);

   b.addActionListener(this);
    frame.setVisible(true);
    frame.show();
}
public void actionPerformed(ActionEvent e){
    String s = jte.getText();
    frame.setTitle("Tekst:"+s);
}
public static void main(String s[]){
    MyImpl mi = new MyImpl();
}}

Oczywiście, jeden obiekt może „nasłuchiwać” zdarzeń
pochodzących z wielu innych obiektów – w tym wypadku konieczne jest rozróżnienie
ich źródeł. Poniższy przykład jest fragmentem metody z klasy, która
implementuje studenckiego klienta:

public void actionPerformed(ActionEvent evt){
if (evt.getSource().equals(loginButton))
//zdarzenie pochodzi z przycisku loginButton
else if(evt.getSource().equals(logoutButton))
//zdarzenie pochodzi z przycisku logoutButton
}

Zdarzenia mogą pochodzić z przycisków loginButton oraz
logoutButton. Do pobrania źródła zdarzenia służy metoda getSource() klasy
ActionEvent. Oczywiście obiekt klasy, która implementuje pokazaną powyżej
metodę actionPerformed() musi być zarejestrowany w obu przyciskach.

Współpraca „dwustronna” – ciąg dalszy

Wprowadzeniem do „dwustronnej” współpracy serwera z
klientem będzie, dla przejrzystości, kontynuacja bardzo prostego przykładu z
poprzedniego odcinka. Podejmując ważne decyzje projektowe należy przeprowadzać
pewne eksperymenty mające na celu sprawdzenie, czy dane rozwiązanie jest
odpowiednie3. Poniżej zaprezentowano zapisaną w języku IDL
definicję dwóch interfejsów. Interfejs Test jest używany pozornie
„normalnie” tj. służy do manipulowania od strony klienta obiektem, który
znajduje się po stronie serwera. Jeśli spojrzeć na niego nieco bardziej uważnie,
okaże się, iż jednym z jego argumentów jest referencja do zdefiniowanego
powyżej interfejsu TestCallBack. Ten ostatni interfejs jest używany w odmienny
od prezentowanego sposób – umożliwia bowiem dostęp od strony serwera do
obiektu znajdującego się po stronie klienta.

interface TestCallBack{
    void getTest(in long a);
};
interface Test{
    long f(in TestCallBack objRef , in string message);
};
4

Obiekt klasy MyImpl implementuje interfejs Test służący,
jak powiedziano, do przekazania referencji do obiektu MyCallBackImpl, który z
kolei implementuje interfejs TestCallBack. Interakcja pomiędzy serwerem (z
pominięciem szczegółów związanych z użyciem architektury CORBA):

  1. Klient tworzy obiekt klasy MyCallBackImpl,
  2. Klient uzyskuje referencję do obiektu klasy MyImpl,
  3. Klient przekazuje referencję do obiektu klasy
    MyCallBackImpl do serwera przy pomocy metody f interfejsu Test, który jest
    implementowany przez obiekt MyImpl,
  4. Od tego momentu serwer może inicjować interakcję z
    klientem – oczywiście program klienta musi nadal działać.


Rysunek 1.
Współpraca dwustronna

Poniżej zostanie przedstawiona przykładowa implementacja
klas MyCallBackImpl:

public class MyCallBackImpl extends TestCallBackPOA{
    public void getTest (int a){
        System.out.println("called back forom serwer "+a);
}}

oraz klasy MyImpl:

public class MyImpl extends TestPOA implements ActionListener{
    JFrame frame=null;
    public TestCallBack _objRef=null;
    JTextField jte = null;
public MyImpl(){
    super();
    frame = new JFrame();
    frame.getContentPane().setLayout(new FlowLayout());
    JLabel label = new JLabel("Proszę wpisać liczbę:");
    JButton b = new JButton("Prześlij");
    frame.setSize(300,100);
    jte = new JTextField(10);
    frame.setTitle("Status: oczekuje na klienta");
    frame.getContentPane().add(label);
    frame.getContentPane().add(jte);
    frame.getContentPane().add(b);
    b.addActionListener(this);
    frame.setVisible(true);
    frame.show();
}
public int f (TestCallBack objRef, String message){
    System.out.println("messgage = "+message);
    _objRef = objRef;
    frame.setTitle("Status: klient zarejestrował się");
    return 1;
}
public void actionPerformed(ActionEvent e){
    String s;
    Integer i;
    if (_objRef!=null){
        s = jte.getText();
        i = new Integer(s);
        _objRef.getTest(i.intValue());
    }
}}

Poniżej znajdują się klasa implementująca serwer:

public class Serv{
    public static void main(String args[]){
    try{
        ORB orb=ORB.init(args,null);
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
        rootpoa.the_POAManager().activate();
        MyImpl rej = new MyImpl();
        org.omg.CORBA.Object ref = rootpoa.servant_to_reference(rej);
        Test href = TestHelper.narrow(ref);
        org.omg.CORBA.Object objRef =
        orb.resolve_initial_references("NameService");
        NamingContextExt ncRef=NamingContextExtHelper.narrow(objRef);
    String name = "Test101";
        NameComponent path[] = ncRef.to_name( name );
    ncRef.rebind(path, href);
        System.out.println("Serwer działa ...");
        orb.run();
    }catch (Exception e){
        e.printStackTrace(System.out);
    }
}}

Oraz klienta:

public class Kli {
    static Test test;
        public static void main(String args[]){
        try{
        ORB orb = ORB.init(args, null);
        org.omg.CORBA.StringHolder s=new org.omg.CORBA.StringHolder("");
        org.omg.CORBA.Object objRef =
        orb.resolve_initial_references("NameService");
        NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
        String name = "Test101";
        test = TestHelper.narrow(ncRef.resolve_str(name));
        MyCallBackImpl callback = new MyCallBackImpl();
        POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
        rootpoa.the_POAManager().activate();
        org.omg.CORBA.Object ref = rootpoa.servant_to_reference(callback);
        TestCallBack href = TestCallBackHelper.narrow(ref);
        String s11 = new String("wiadomość od klienta");
        test.f(href, s11);
        orb.run();
        }catch(Exception e){
               
System.out.println("Błąd: "+e);
               
e.printStackTrace(System.out);
               
}
}}

Trzeba mieć oczywiście świadomość, że dla każdego z
interfejsów, w tym wypadku dwóch, narzędzie idlj wygeneruje cały
komplet plików.

  • W skład serwera wchodzą pliki: Serv.java Myimpl.java
    TestHelper.java Test.java TestPOA.java TestOperations.java TestCallBack.java
    TestCallBackOperations.java TestCallBackHelper.java TestCallBackHolder.java
    _TestCallBackStub.java
  • W skład klienta wchodzą pliki: Kli.java TestHelper.java
    TestHolder.java Test.java TestOperations.java _TestStub.java
    TestCallBackPOA.java MyCallBackImpl.java

Na marginesie tych rozważań warto zwrócić uwagę na to, iż
klient udostępniając i rejestrując obiekt klasy MyCallBackImpl, formalnie też
jest serwerem. W dalszym ciągu będą konsekwentnie stosowane wprowadzone w
poprzednim odcinku podział i nazewnictwo – mają one bowiem odzwierciedlać
funkcje, jakie poszczególne fragmenty aplikacji pełnią w systemie. Podsumowując
zaprezentowane rozwiązanie należy zwrócić uwagę na fakt, iż serwer
otrzymuje referencję do obiektu, który udostępnia klient bez udziału usługi
nazw – jest ona przekazana przez wywołanie funkcji f.

Przykładowa implementacja – zaczynamy

Po powyższych, może trochę nużących – lecz koniecznych
wprawkach – można przystąpić do implementacji. Aby to zrobić, należy –
jak już powiedziano – określić zestaw metod służących do współpracy
jednostki z klasą JPanelPrezentacji, a w dalszym ciągu – drugi zestaw służący
do współpracy studenckiego klienta z serwerem. Przy współpracy jednostki z
jej klasą interfejsową, użyteczne będą następujące metody:

  • insertString – służąca do wstawienia tekstu,
  • insertNewLine – przejście do nowej linii,
  • insertPicture – służąca do wstawienia obrazka,
  • insertTable – służąca do wstawienia tabelki,
  • setStyle – służąca do ustawienia stylu, jaki będzie
    używany przy następnej operacji,
  • clear – służąca do usunięcia całej zawartości z
    panelu prezentacji,
  • sendData – służąca do przesłania danych np. pliku,
  • insertComponent – służąca do wstawienia przycisku
    lub etykiety itp. do panelu edycyjnego,
  • ShowMessageBox – służąca do wyświetlania okna
    dialogowego z komunikatem,

Powyższy zestaw jest nieco nadmiarowy – w zasadzie
wszystko można byłoby załatwić przy pomocy przedostatniej z metod. Jednak
jej wykonanie wiąże się ze zinterpretowaniem polecenia – a więc pociąga
za sobą pewien nakład czasowy. Takie rozwiązanie dla typowych i często
powtarzanych czynności nie jest opłacalne. Z drugiej strony należy rozważyć,
czy zamiast omawianej, nie wyspecyfikować metod dedykowanych np. do wstawiania
przycisku, etykiety, pola edycyjnego i innych kontrolek. Oprócz czasu
wykonania, istotnym czynnikiem jest tutaj niezmienność interfejsu
komunikacyjnego pomiędzy jednostką i obiektem interfejsowym. Gdyby wszystko
wyspecyfikować „na sztywno”, to dodanie możliwości wstawienia nowej
kontrolki pociągnęłoby za sobą zbyt duże zmiany w kodzie aplikacji. Powyższy
zestaw metod jest po części pewnym „rozsądnym” podzbiorem zbioru wspólnego
metod klas JTextPane oraz Dokument. Inne będą oczywiście parametry
wymienionych metod – nie wchodzi bowiem w grę tworzenie przycisku po stronie
serwera a następnie przesłanie go do klienta. Należy wysłać po prostu nazwę
tworzonego komponentu, parametry konstruktora, i – co bardzo ważne – sposób
reakcji na zdarzenia generowane przez użytkownika. Sposób przesłania nazwy
jest oczywisty – wywołujemy insertComponet(„JButton”…), parametry wywołania
konstruktora mogą być przekazane w drugim argumencie tzn.: insertComponet(„JButton”,”Naciśnij
mnie”,…) – trzeba się tylko zastanowić, jak zapewnić ich właściwą
konwersję po stronie klienta. Znacznie bardziej ciekawy jest sposób określenia
reakcji na typowe zdarzenie użytkownika. Można pokusić się na przesyłanie
odnośnika do obiektu zdalnego, który obsłuży zdarzenie. Jest to jednak dość
zawiłe i pracochłonne. Znacznie lepiej jest mieć ujednoliconą metodę, przy
pomocy której klient powiadamia serwer o zajściu zdarzenia, przesyłając
szczegóły dotyczące go jako parametry tejże metody. Aby zastosować takie
rozwiązanie, wystarczy mieć możliwość jednoznacznej identyfikacji
wstawionych komponentów – wywołując po stronie serwera funkcję wstawiającą
komponent należy nadać mu pewien określony identyfikator np.:
insertComponent(„JButton”,”Naciśnijmnie”,”acx123ssd”). Projektując
powyższą współpracę należy zastanowić, się czy każde zdarzenie ma być
natychmiast odesłane do serwera, czy raczej ma być wstawione do jakiejś
kolejki i odesłane później np. po wystąpieniu jakiegoś innego zdarzenia. Załóżmy,
że student rozwiązuje test wybierając odpowiedzi przy pomocy kontrolek typu
JCheckBox. Nie ma sensu odsyłanie każdego zdarzenia, tym bardziej, że może
on wielokrotnie zmieniać wybrane przez siebie odpowiedzi. Dla systemu nie jest
wtedy istotne wystąpienie konkretnego zdarzenia, ale stan po zatwierdzeniu
testu. Pozornie jest to sprzeczne z tym, co zostało napisane pod koniec
ostatniego odcinka. Tak jednak nie jest, gdyż sprawdzenie poprawności i ocena
testu odbywa się po stronie serwera. Przyjęte tutaj uproszczenie ma na celu
ograniczeniu ruchu w sieci. Przy teście wielokrotnego wyboru składającego się
z 20 pytań, z których każde ma 4 możliwe odpowiedzi, może wystąpić nawet
więcej niż 80 interakcji klienta z serwerem. Podczas, gdy przyjęte założenie
ograniczy ilość interakcji do jednej. Tak więc omawiana powyżej metoda
przybierze ostateczną postać: insertComponet(„JCheckBox”, „Wykonanie
programu w języku C, rozpoczyna się od tej funkcji”,”fxde23d”,false);
Bardziej szczegółowy przykład interakcji pomiędzy jednostką a jej klasą
interfejsową (z pominięciem szczegółów związanych z rozproszeniem)
znajduje się na rysunku 3. Jednostka wstawia do swojego obiektu interfejsowego
napis „funkcja main służy do:”, a następnie tworzy w owym obiekcie dwa
komponenty typu JCheckBox odpowiednio z napisami „Od niej rozpoczyna się
wykonanie każdego programu w języku C.” oraz „Kończy ona wykonanie każdego
programu w C.”. Następnie wstawiany jest komponent typu JButton (przycisk) z
napisem „Zakończ”, po naciśnięciu którego zdarzenie wraz z danymi dotyczącymi
stanu kontrolek typu JCheckBox odsyłane jest do jednostki. Ekran stworzony w
powyższej interakcji przedstawiony został na rysunku 2.


Rysunek 2.
Przykładowa interakcja pomiędzy jednostką a klasą interfejsową

Diagram sekwencji na rysunku 3 został uproszczony – nie
uwzględniono bowiem przejść do nowej linii, aby go nie zaciemniać. Naciśnij
oraz Zaznacz nie są w rzeczywistości wywołaniami metod, lecz schematycznie
przedstawiają interakcję ze studentem. Nie uwzględniono też przesyłania
zdarzeń generowanych przez kontrolki. Następnym problemem, który wymaga dość
starannej analizy jest sposób przesyłania danych, a w rzeczywistości sposób
przesyłania plików. Podstawowe typy danych występujące
w języku IDL to:
any, boolean, char, double, float, long, long long, octet, short, string, wchar,
wstring. Typ any odpowiada dowolnemu typowi, octet reprezentuje ośmiobitową
daną, która nie podlega żadnym konwersjom podczas przekazywania. Typy wstring
oraz string są mapowane do typu String w języku Java, char oraz wchar do typu
char; z tym, że w zaprezentowanych poniżej interfejsach zostanie użyty typ
wstring, co jest związane z przesyłaniem polskich znaków; typ long jest
mapowany do int, long long do long. Inne typy nie wymagają omówienia, gdyż
odpowiadają analogicznie nazywającym się typom w języku java. Z powyższych
informacji wynika, iż do przesyłania danych nadają się tablice typu octet.
Przekazując dane należy przekazać ich rozmiar, właściwą tablicę z owymi
danymi oraz nazwę lub lepiej polecenie z argumentem np. „SAVE swallow.gif”
– zostanie to jednak dospecyfikowane później. Interfejs do obsługi klasy
JPanelPrezentacji został zaprezentowany poniżej. W linii rozpoczynającej się
od słowa typedef znajduje się definicja typu danych użytkownika, który nosi
nazwę dane i odpowiada sekwencji (tablicy) bajtów. W obrębie samego
interfejsu został zdefiniowany wyjątek JPanelPrezentacjiCallBack, posiadający
jedną składową typu string. W owej składowej może być przekazywana
informacja opisująca szczegóły zajścia wyjątku. Wszystkie metody w
interfejsie mogą rzucać wyjątek tego typu, co jest zapisane przy pomocy słowa
kluczowego reises.


Rysunek 3.
Ekran powstały w wyniku interakcji

typedef sequence<octet> dane;
interface JPanelPrezentacjiCallBack{
    exception JPPCallBackException{
        wstring s;
    };
    void insertString(in wstring s) raises (JPPCallBackException);
    void insertNewLine() raises (JPPCallBackException);
    void insertPicture(in wstring name) raises (JPPCallBackException);
    void insertTable(in wstring dane) raises (JPPCallBackException);
    void setStyle(in wstring style) raises (JPPCallBackException);
    void clear() raises (JPPCallBackException);
    void sendData(in long rozmiar, in dane d, in wstring polecenie) raises (JPPCallBackException);
    void insertComponent(in wstring typ, in wstring nazwa, in wstring id, in
boolean reakcja) raises (JPPCallBackException);
    void ShowMessageBox(in wstring msg);
};

Poniżej znajdują się definicje w języku IDL związane z
interakcją studenckiego klienta z serwerem. Dla uproszczenia zgrupowano razem
interakcję pomiędzy panelem prezentacji i jednostką, z innymi operacjami,
jakie może wykonywać student. W implementacji zostanie więc w miarę potrzeby
użyta delegacja metod. Struktura sJPPEvent posiada dwa pola, z których:
pierwsze określa identyfikator źródła zdarzenia taki, jak został przekazany
podczas wstawiania komponentu (np. acx123ssd); drugi określa argument związany
ze zdarzeniem. Jeśli komponentem generującym zdarzenie jest pole edycyjne, to
jest to tekst wpisany do tego pola, o ile CheckBox, to informacja o tym, czy
wymieniona kontrolka została zaznaczona.

struct sJPPEvent{
    wstring source;
    wstring arg;
};

Definicja typu, który będzie odpowiadać zdefiniowanej właśnie
strukturze.

typedef sJPPEvent JPPEvent;

Definicja typu, który jest kolejką zdarzeń. W ogólności
trzeba zakładać, że przesyła się wiele zdarzeń – a mówiąc bardzo dokładnie,
jedno zdarzenie (np. związane z zatwierdzeniem danych) i wiele opisów stanu.

typedef sequence<JPPEvent> JPPEvents;

Poniżej znajduje się definicja interfejsu JPP – podobnie
jak w zaprezentowanym powyżej interfejsie założono, iż każda z metod może
rzucić wyjątek. W przypadku tego interfejsu nazywa się on JPPException, ale
jego definicja i znaczenie jest identyczne, jak wyjątku zdefiniowanego w
interfejsie powyżej.

interface JPP{
    exception JPPException{
        wstring s;
    };
    void sendEvents(in JPPEvents jppevents) raises (JPPException);
    void help(in wstring arg) raises (JPPException);
    long next() raises (JPPException);
    boolean logout() raises (JPPException);
    boolean begin() raises (JPPException);
    void askTeacher(in wstring msg) raises (JPPException);
    long executeCommand(in wstring command) raises (JPPException);
    void askForRaport(in wstring desc) raises (JPPException);
    wstring getName() raises (JPPException);
};

Metoda SendEvents służy do wysłania zdarzeń, które zostały
wygenerowane w związku z interakcją z użytkownikiem (studentem). Metoda help
jest wysyłana wtedy, gdy użytkownik poprosi o pomoc. Metoda next powoduje
przejście do kolejnej jednostki, w wypadku gdy interakcją steruje student. Do
logowania się w systemie służy metoda login a do wylosowywania metoda logout.
Metoda begin służy do zapoczątkowania interakcji, jeśli interakcję tę ma
rozpocząć student. Metoda askTeacher służy do wysłania pytania (np. z prośbą
o dodatkowe wyjaśnienie) bezpośrednio do prowadzącego (wykładowcy), metoda
executeCommand służy do zlecenia serwerowi wykonania pewnego polecenia związanego
np. z prośbą o przesłanie pliku. Analizując drugi interfejs można dojść
do wniosku, że pewne jego metody odpowiadają metodom klasy Jednostka (np. next
– run). Spostrzeżenie to jest jak najbardziej trafne, z tym że celowo
zastosowano inne niż w klasie jednostka nazwy. Ma to na celu podkreślenie
faktu iż rozproszenie systemu uzyskane dzięki architekturze CORBA ma aspekt
techniczny. Interfejs StudLoginAction służy do logowania i rejestracji
studenta w systemie.

interface StudLoginAction{
    exception StudLoginException{
        wstring s;
    };
    void register(in wstring imie, in wstring nazwisko, in wstring PESEL,
                   
in wstring adres, in wstring numer_telefonu,
                   
in wstring adresEmail,out wstring loginName,
                   
out wstring passwd) raises (StudLoginException);
    JPP login(in wstring loginname, in wstring passwd,
                   
in JPanelPrezentacjiCallBack jppcb) raises (StudLoginException);
};

Poniżej zostały przedstawione klasy tworzące działający
szkielet aplikacji odpowiedzialny za współpracę pomiędzy klientem studenckim
a serwerem. Pozwalają one na przetestowanie komunikacji. Jako pierwsza została
przedstawiona właśnie klasa takiego klienta:

public class StudFrame extends JFrame implements ActionListener{
    static private ORB orb = null;
    static POA rootpoa = null;
    static NamingContextExt ncRef = null;
    static JPanelPrezentacji jPanelPrezentacji = null;
    static JPP jpp = null;
    BorderLayout borderLayout1 = new BorderLayout();
    JPanel jPanel1 = new JPanel();
    JButton loginButton = new JButton("Logowanie");
    JButton logoutButton = new JButton("Wylogowanie");
    JButton helpButton = new JButton("Pomoc");
    JButton askTeacherButton = new JButton("Pytanie do nauczyciela");
    JButton nextButton = new JButton("Dalej");
    static public JTextPane orderArea = new JTextPane();
    static public JPanel controlArea = new JPanel();
    static public JScrollPane orderAreaScroll = new JScrollPane(orderArea);
    public StudFrame(){
    super();
    try{jbInit();
            }catch (Exception e) {e.printStackTrace();}
    addWindowListener(new WindowAdapter(){
    public void windowClosing(WindowEvent e){
    setVisible(false);
    dispose();
    System.exit(0);
    }});
    }
    private void jbInit() throws Exception {
    this.getContentPane().setLayout(borderLayout1);
    this.setSize(800, 500);
    this.setTitle("Klient studencki 1.0");
    this.getContentPane().add(jPanel1, BorderLayout.CENTER);
    GridBagLayout gridbag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();
    jPanel1.setLayout(gridbag);
    c.fill = GridBagConstraints.BOTH;
    c.weightx=0.05;
    c.gridx = 1;
    c.gridy = 0;
    c.weighty = 1;
    c.ipady=10;
    c.gridheight=4;
    gridbag.setConstraints(controlArea,c);
    jPanel1.add(controlArea);
    orderAreaScroll.setVerticalScrollBarPolicy(
    JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDE);
    c.fill = GridBagConstraints.BOTH;
    c.weightx=0.95;
    c.gridx = 0;
    c.gridy = 1;
    c.weighty = 0.7;
    c.gridheight=1;
    c.ipady=10;
    gridbag.setConstraints(orderAreaScroll,c);
    jPanel1.add(orderAreaScroll);
   
controlArea.setBorder(BorderFactory.createTitledBorder("Sterowanie"));
    controlArea.setLayout(new BoxLayout(controlArea,BoxLayout.Y_AXIS));
    int buttonWidth = 180;
    int buttonHeight = 35;
    loginButton.setMaximumSize(new Dimension(buttonWidth,buttonHeight));
    controlArea.add(loginButton);
    logoutButton.setMaximumSize(new Dimension(buttonWidth,buttonHeight));
    controlArea.add(logoutButton);
    helpButton.setMaximumSize(new Dimension(buttonWidth,buttonHeight));
    controlArea.add(helpButton);
    askTeacherButton.setMaximumSize(new Dimension(buttonWidth,buttonHeight));
    controlArea.add(askTeacherButton);
    nextButton.setMaximumSize(new Dimension(buttonWidth,buttonHeight));
    controlArea.add(nextButton);
    loginButton.addActionListener(this);
    logoutButton.addActionListener(this);
    helpButton.addActionListener(this);
    askTeacherButton.addActionListener(this);
    nextButton.addActionListener(this);
    }
    public void actionPerformed(ActionEvent evt){
    try{
        if (evt.getSource().equals(loginButton)){
        String name = "LoginService";
        StudLoginAction loginAction=
    StudLoginActionHelper.narrow(ncRef.resolve_str(name));
    JPanelPrezentacjiCallBackImpl jPPCBI = new
    JPanelPrezentacjiCallBackImpl(orb,rootpoa,jPanelPrezentacji);
    org.omg.CORBA.Object ref = rootpoa.servant_to_reference(jPPCBI);
    JPanelPrezentacjiCallBack href = JPanelPrezentacjiCallBackHelper.narrow(ref);
    jpp = loginAction.login ("12actr3", "12f3c3d3", href);
    }
    else if(evt.getSource().equals(logoutButton)){
        jpp.logout();
    }
    else if(evt.getSource().equals(helpButton)){
        jpp.help("anything");
    }
    else if (evt.getSource().equals(askTeacherButton)){
        jpp.askTeacher("Kiedy ma Pan konsultacje?");
    }
    else if (evt.getSource().equals(nextButton)){
        jpp.next();
    }}catch(Exception e1){
                   
e1.printStackTrace();
                   
}
    }
    public static void main(String args[]){
    jPanelPrezentacji = new JPanelPrezentacji("aaa",orderArea);
        try{
                   
orb = ORB.init(args, null);
                   
org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
                   
ncRef = NamingContextExtHelper.narrow(objRef);
                   
rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
                   
rootpoa.the_POAManager().activate();
                   
StudFrame sf = new StudFrame();
                   
sf.setVisible(true);
                   
sf.show(true);
                   
orb.run();
    }catch(Exception e){
                   
e.printStackTrace(System.out);
}}}

W jej konstruktorze i metodzie jbInit zostaje zbudowany
interfejs użytkownika (rys. 5), metoda actionPerformed obsługuje zdarzenia
pochodzące z przycisków. W metodzie main zainicjowano ORB oraz POA. Po naciśnięciu
przycisku „logowanie” (obsługa w actionPerformed) zostaje wykonany kod
przedstawiony w ramce na rysunku 4. W linii 2 pobrana jest referencja do
zdalnego obiektu klasy StudLoginActionImpl; pozwala on na rejestrację i
zalogowanie się w systemie. Następnie tworzony jest – linia 3 – obiekt
klasy JPanelPrezentacjiCallBackImpl – pozwala on na interakcję z panelem
prezentacji od strony serwera. W linii 4 pobierana jest referencja do tego
obiektu, który jest jednocześnie aktywowany – służy do tego metoda
servant_to_reference. W linii 5 referencja ta jest poddawana konwersji do
referencji do interfejsu JPanelPrezentacjiCallBack. W linii 6 wywoływana jest
metoda login – jej parametrami są nazwa użytkownika, jego hasło oraz
stworzona przed chwilą referencja. Wywołana metoda zwraca referencję do
obiektu klasy JPPImpl; obiekt ten znajduje się po stronie serwera i dostarcza
funkcjonalności związanej z uczestnictwem w zdalnych zajęciach. Powyżej
opisane interakcje są schematycznie pokazane na rysunku 6.

  1. String name = „LoginService”;
  2. StudLoginAction loginAction=StudLoginActionHelper.narrow(ncRef.resolve_str(name));
  3. JPanelPrezentacjiCallBackImpl jPPCBI = new JPanelPrezentacjiCallBackImpl(orb,rootpoa,jPanelPrezentacji);
  4. org.omg.CORBA.Object ref = rootpoa.servant_to_reference(jPPCBI);
  5. JPanelPrezentacjiCallBack href =
    JPanelPrezentacjiCallBackHelper.narrow(ref);
  6. jpp = loginAction.login („12actr3”, „12f3c3d3”, href);

Rysunek 4. Fragment obsługujący logowanie


Rysunek 5.
Klient studencki


Rysunek 6.
Schematyczna interakcja

Poniżej znajduje się definicja klasy przykładowej, bardzo
prostej jednostki w postaci, w jakiej występuje ona w systemie rozproszonym –
wyświetla ona napis i tworzy przycisk (wytłuszczone linie):

public class Jednostka implements Runnable{
    private String _aName;
    private int _maxPuntkow;
    private Student _student;
    public Vector JPP_Studentow = new Vector();
    public Jednostka() {}
    public Jednostka(String aName){_aName = new String(aName);}
    public void MaxPutnktow(int m){_maxPuntkow = m;}
    public void run(){
        try{
            for (int i = 0;i<JPP_Studentow.size();i++){
               
JPPImpl jpp_stud = (JPPImpl)JPP_Studentow.elementAt(i);
               
jpp_stud._jppcb.insertString("Nowy Wykład n");
   

            
jpp_stud._jppcb.insertComponent ("JButton", "Witamy",
               
"witamy1z223", true);
               

}
            }catch(Exception ee){}
    }
    public int Ocen(){return 0;}
    public int BlednaReakcjaUzytkownika(){return 0;}
    public int CzyCzekacNaNastepna(){return 1; }
    String WezNazwe(){return _aName;}
    void UstawStudenta(Student student){_student = student;}
    public void NaZakonczenie(){}
            public void NaPoczatek(){}
            public void Pomoc(){}
}

Poniżej znajduje się klasa definiująca (działający)
panel prezentacji:

public class JPanelPrezentacji{
    private StudFrame mf = null;
    private JTextPane tp = null;
    public Document doc = null;
    private Style def = null;
    private Style regular;
    private Style bold;
    private Style _red;
    private Style _green;
    private Style _blue;
    private static int styleno=0;
    private Style curStyle = null;
    private String aName = null;
    public JPanelPrezentacji(){}
    public JPanelPrezentacji(String name, JTextPane jtp) {
    aName=name;
    tp=jtp;
    doc=tp.getDocument();
    def=StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
    regular=tp.addStyle("regular",def);
    StyleConstants.setFontFamily(def,"Java");
    bold=tp.addStyle("bold",def);
    StyleConstants.setBold(bold,true);
    _red=tp.addStyle("red",def);
    StyleConstants.setForeground(_red,Color.red);
    _green=tp.addStyle("green",def);
    StyleConstants.setForeground(_green,Color.green);
    _blue=tp.addStyle("blue",def);
    StyleConstants.setForeground(_blue,Color.blue);
    curStyle = regular;
    }
    public void doText(String s)throws BadLocationException{
        doc.insertString(doc.getLength(),s,curStyle);
        }
        void setStyle(String styleName){
                   
if (styleName.startsWith("BOLD")){
                   
curStyle = tp.getStyle("bold");
                   
}
                   
else if (styleName.startsWith("RED")){
                       
curStyle = tp.getStyle("red");
                   
}
                   
else if (styleName.startsWith("GREEN")){
                       
curStyle = tp.getStyle("green");
                   
}else if (styleName.startsWith("BLUE")){
                       
curStyle = tp.getStyle("blue");
                   
}else if (styleName.startsWith("PLAIN")){
                       
curStyle = tp.getStyle("regular");
                   
}
                   
else
                       
curStyle = tp.getStyle("regular");
    }
    private int doNewLine(String s, int i) throws BadLocationException{
                       
doc.insertString(doc.getLength(),"n",tp.getStyle("regular"));
            return i;
    }
    private int doPicture(String s, int i )throws BadLocationException{
            return i;
    }
public int insertComponent(JComponent comp) throws BadLocationException{
                   
Style compstyle = tp.addStyle("compstyle"+styleno,def);
        StyleConstants.setAlignment(compstyle,StyleConstants.ALIGN_CENTER);
        StyleConstants.setComponent(compstyle,comp);
        doc.insertString(doc.getLength()," ",tp.getStyle("compstyle"+styleno));
        return 1;
}}

W konstruktorze zarejestrowano kilka stylów, metoda doText służy
do wstawiania tekstu, setStyle do ustawiania stylu, doNewLine do przejścia do
nowej linii, doPicture do wstawienia obrazka (jeszcze nie zdefiniowana),
insertComponent służy do wstawienia komponentu.

Poniższa klasa realizuje interakcję z panelem prezentacji
od strony serwera. Dla uproszczenia zdefiniowano wyłącznie metodę
insertString oraz fragment metody insertComponent.


public class JPanelPrezentacjiCallBackImpl extends JPanelPrezentacjiCallBackPOA{
    ORB _orb = null;
    POA _poa = null;
    JPanelPrezentacji _jpp=null;
        public JPanelPrezentacjiCallBackImpl(ORB orb, POA poa, JPanelPrezentacji jpp){
            _orb = orb;
            _poa = poa;
            _jpp = jpp;
            }

   


public void insertString (String s) throws
    JPanelPrezentacjiCallBackPackage.JPPCallBackException{
    try{
    _jpp.doText(s);
    }catch(Exception e1){
    throw new JPanelPrezentacjiCallBackPackage.JPPCallBackException("Bł. wst.
tekstu");
    }}

    public void insertNewLine () throws
    JPanelPrezentacjiCallBackPackage.JPPCallBackException{}
    public void insertPicture (String name) throws
    JPanelPrezentacjiCallBackPackage.JPPCallBackException{}
        public void insertTable (String dane) throws
    JPanelPrezentacjiCallBackPackage.JPPCallBackException{}
        public void setStyle (String style) throws
    JPanelPrezentacjiCallBackPackage.JPPCallBackException{}
        public void clear () throws
    JPanelPrezentacjiCallBackPackage.JPPCallBackException{}
        public void sendData (int rozmiar, byte[] d, String polecenie) throws
    JPanelPrezentacjiCallBackPackage.JPPCallBackException{}
   

    
public void insertComponent (String typ, String nazwa, String id, boolean reakcja) throws
JPanelPrezentacjiCallBackPackage.JPPCallBackException{
    try{
        if (typ.equals("JButton"))
                   
_jpp.insertComponent(new JButton(nazwa));
    }catch(Exception e1){
    throw new JPanelPrezentacjiCallBackPackage.JPPCallBackException("Błąd
wst.tekstu");
    }}

    public void ShowMessageBox (String msg){}
}

Oto szkielet klasy, która realizuje współpracę
studenckiego klienta z serwerem. Metoda next tworzy jednostkę, która wchodzi w
interakcję ze studenckim klientem. Pozostałe metody wyświetlają napis po
stronie klienta, metoda sendEvents wymaga zdefiniowania.

public class JPPImpl extends JPPPOA{
    ORB _orb = null;
    POA _poa = null;
    public JPanelPrezentacjiCallBack _jppcb = null;
    public JPPImpl(ORB orb, POA poa, JPanelPrezentacjiCallBack jppcb){
            _orb = orb;
            _poa = poa;
            _jppcb = jppcb;
            }
    public void sendEvents (sJPPEvent[] jppevents) throws JPPPackage.JPPException{}
    public void help (String arg) throws JPPPackage.JPPException{
            try{
            _jppcb.insertString("Prośba o pomoc :"+arg);
            }catch(Exception ee){
                   
throw new JPPPackage.JPPException("Błąd prośby o pomoc");
            }}
   
public int next () throws JPPPackage.JPPException{
        try{
        Jednostka j = new Jednostka();
        j.JPP_Studentow.add(this);
        j.run();
        }catch(Exception ee){
        throw new JPPPackage.JPPException("Błąd nast. jednostki");
        }
        return 1;
        }

    public boolean logout () throws JPPPackage.JPPException{
        try{
        _jppcb.insertString("wylogowanie");
        }catch(Exception ee){
                   
throw new JPPPackage.JPPException("błąd wylogowania");
        }
        return true;
        }
    public boolean begin () throws JPPPackage.JPPException{
        try{
        _jppcb.insertString("rozpoczęcie");
        }catch(Exception ee){
               
throw new JPPPackage.JPPException("błąd rozpoczęcia");
        }
        return true;
    }
    public void askTeacher (String msg) throws JPPPackage.JPPException{
        try{
        _jppcb.insertString("pytanie o nauczyciela"+msg);
        }catch(Exception ee){
               
throw new JPPPackage.JPPException("błąd pytania o nauczyciela");
        }}
    public int executeCommand (String command) throws JPPPackage.JPPException{
        try{
               
_jppcb.insertString("Polecenie (komenda)"+command);
        }catch(Exception ee){
               
throw new JPPPackage.JPPException("Błąd polecenia (komenda)");
        }
        return 1;
    }
    public void askForRaport (String desc) throws JPPPackage.JPPException{
        try{
    _jppcb.insertString("Prośba o raport"+desc);
    }catch(Exception ee){
               
throw new JPPPackage.JPPException("Błąd prośby o raport");
    }}
    public String getName () throws JPPPackage.JPPException{
        try{
        _jppcb.insertString("Prośba o nazwę");
        }catch(Exception ee){
               
throw new JPPPackage.JPPException("Błąd obsł. prośby o nazwę");
        }
        return new String("Jednostka testowa");
    }
}

Poniżej znajduje się szkielet klasy, implementującej
funkcjonalność związaną z rejestracją studenta w systemie
i jego logowanie. Istotne jest to, że metoda login „spina” serwer z
klientem. Wywołanie tej ostatniej zwraca referencje do obiektu klasy JPPImpl.

public class StudLoginActionImpl extends StudLoginActionPOA{
    ORB _orb = null;
    POA _poa = null;
    public StudLoginActionImpl(ORB orb, POA poa){
            _orb = orb;
            _poa = poa;
            }
public void register (String imie, String nazwisko, String PESEL, String
adres,
    String numer_telefonu, String adresEmail, org.omg.CORBA.StringHolder
    loginName, org.omg.CORBA.StringHolder passwd) throws
    StudLoginActionPackage.StudLoginException{}
public JPP login (String loginname, String passwd, JPanelPrezentacjiCallBack
jppcb) throws StudLoginActionPackage.StudLoginException{
   

        
JPPImpl jppimpl = new JPPImpl(_orb,_poa, jppcb);
            org.omg.CORBA.Object ref=null;

            try{

   

        

ref = _poa.servant_to_reference(jppimpl);

            jppcb.insertString("Logowanie");
            }catch(Exception ee){
            ee.printStackTrace();
            }
   

        
JPP href = JPPHelper.narrow(ref);
            return href;
           

}
}

Poniżej znajduje się bardzo prosty serwer, służący do
przetestowania komunikacji przedstawionych klas.

public class Serv{
        public static void main(String args[]){
        try{
            ORB orb=ORB.init(args,null);
POA rootpoa = POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
            rootpoa.the_POAManager().activate();
StudLoginActionImpl loginAction = new StudLoginActionImpl(orb,rootpoa);
org.omg.CORBA.Object ref = rootpoa.servant_to_reference(loginAction);
            StudLoginAction href = StudLoginActionHelper.narrow(ref);
        org.omg.CORBA.Object objRef =
        orb.resolve_initial_references("NameService");
            NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
                   
String name = "LoginService";
            NameComponent path[] = ncRef.to_name( name );
                   
ncRef.rebind(path, href);
        orb.run();
            }catch (Exception e){
        e.printStackTrace(System.out);
            }
}}

Dla porządku należy przypomnieć, że jeśli chodzi o
obiekty klas pisanych „ręcznie” i związanych z komunikacją to:

  • JPanelPrezentacjiCallBackImpl – znajduje się po stronie
    klienta i korzysta z niego serwer,
  • JPPImpl – znajduje się po stronie serwera i korzysta z
    niego klient,
  • StudLoginActionImpl – znajduje się po stronie serwera
    i korzysta z niego klient.

Dalsza implementacja polegać będzie na zrealizowaniu obsługi
zdarzeń, umożliwieniu tworzenia różnych komponentów-kontrolek po stronie
klienta, realizacji przesyłania plików. Następnie należy zaimplementować pełną
funkcjonalność serwera i innych klientów.

Cdn.

Litereratura

[1] OMG IDL Syntax and Semantics, December 2001 www.omg.com
[2] OMG IDL to Java Language Mapping www.omg.com
[3] CORBA, Passing References to Remote Objects – Callback Methods home.att.net/~baldwin.rick/Advanced/Java636.html
[4] E. Gamma Design Patterns Addison-Wesley 1998
[5] materiały na www.sun.com


1 Dla niektórych przykładów taki ich zestaw
jest nieco nadmiarowy, ale nie ma to żadnego negatywnego wpływu, zwiększając
w znaczny sposób czytelność przykładów.
2 Lub być klasą abstrakcyjną.
3 Patrz także: „Projektowanie systemów informatycznych” część 2.
4 Dla zwiększenia szybkości można metodę getTest zadeklarować jako oneway void getTest(in long a).