Návrh webovej služby rozvrhu fakulty mechatroniky

Z Kiwiki
Verzia z 18:36, 28. február 2010, ktorú vytvoril Juraj (diskusia | príspevky) (Zamyká „Návrh webovej služby rozvrhu fakulty mechatroniky“ ([edit=sysop] (na neurčito) [move=sysop] (na neurčito)))
(rozdiel) ← Staršia verzia | Aktuálna úprava (rozdiel) | Novšia verzia → (rozdiel)
Skočit na navigaci Skočit na vyhledávání

Návrh služby

Našou úlohou je vytvoriť webovú službu rozvrhu fakulty mechatroniky v jazyku Java. Služba má poskytnúť rozvrh vo formáte HTML pre klienta, ktorý chce zobraziť rozvrh ako webovú stránku a vo formáte XML pre klienta, ktorý by chcel ďalej spracovávať rozvrh alebo napríklad pre zobrazenie na mobilných telefónoch. Služba musí poskytovať funkciu nahrania nového rozvrhu. Súčasný rozvrh sa nachádza na stránke: http://fm.tnuni.sk/rozvrh/

Pri návrhu služby bolo nutné vychádzať zo súčasného stavu, t.j. rozvrh sa pre čo najľahšie spracovanie exportuje z Excelu do súboru CSV.

Obr. 5 - CSV súbor rozvrhu FM

CSV súbor je textový súbor, v ktorom sú premenné oddelené čiarkou, nakoľko v slovenskom jazyku je čiarka bežná, použijeme bodkočiarku. V prvom riadku sa nachádzajú názvy stĺpcov tabuľky, niekedy sa tento riadok nazýva aj hlavička.

Služba som navrhol tak, že služba načíta a uloží CSV súbor do MySQL databázy. Rozhodol som sa použiť databázu, pretože pomocou jazyka SQL je oveľa ľahšie získať požadované dáta ako keby sa mali získavať z CSV súboru. Jazyk SQL, ktorý slúži na prácu s databázami, využijeme napríklad pri výpise mien učiteľov alebo krúžkov v rozvrhu. Prípadne by sa dal jazyk SQL využiť aj na štatistické účely, napríklad koľko má určitý učiteľ za týždeň predmetov a podobne. Ďalej je nutné spracovať výpis z databázy a prekonvertovať ho do požadovaného formátu, v našom prípade XML alebo HTML. Pre ukážku komunikácie medzi dvoma webovými službami, som navrhol metódu, ktorá zvolený vyučovací predmet, vloží do vyhľadávacieho reťazca pre službu Yahoo Search.

Obr. 6 - Návrh služby rozvrhu fakulty mechatroniky

Návrh služby je vidieť na obrázku. V bielo-žltých obdĺžnikoch sú triedy, ktoré budú napísané v jazyku Java. Neskôr si popíšeme jednotlivé triedy, ich premenné a metódy.

Služba musí byť spustená na nejakom aplikačnom servery. Z voľne dostupných máme na výber server GlassFish V2 od firmy Sun Microsystems a Apache Tomcat od open-source združenia The Apache Software Foundation. Rozhodli sme sa použiť server Tomcat, pretože je primárne určený pre webové služby a servlety, pretože GlassFish primárne slúži pre aplikácie J2EE, dodatočne pre webové služby a servlety.

Služba je napísaná vo vývojovom prostredí NetBeans IDE 6.1, ktoré je voľne dostupná a open-source. Prostredie poskytuje veľké množstvo zásuvných modulov, z ktorých pre nás dôležitý je Web Services, ktorý je určený pre tvorbu webových služieb. Obsahuje nástroje pre vytvorenie a testovanie webovej služby. S prostredím sú dodávané obidva spomínané aplikačné servery.

Návrh databázových tabuliek

Na začiatok je nutné si definovať základné pojmy. MySQL je relačná databáza, čo znamená, že informácie sú pospájané navzájom. Jednoduchá databáza sa skladá z jednej tabuľky (table), zložitejšie databázy sa skladajú z viacerých tabuliek. Tabuľky predstavujú zoznam riadkov a stĺpcov. Riadky v tabuľke sa nazývajú záznamy (record) a stĺpce sa nazývajú polia (field). Záznam predstavuje ucelený rad informácii, napríklad o produkte (výrobné číslo, cena, rozmery,...) a pole predstavuje iba jednu položku tohto záznamu, napríklad cena produktu.

Obr. 7 - Nákres databázy

Pri návrhu databázy je nutné si určiť účel databázy, ktorým je uloženie rozvrhu fakulty. Ďalším krokom je rozdelenie informácii do tabuliek. Celý rozvrh z CSV súboru si uložíme do tabuľky s názvom „rozvrhfm“. Tabuľka v databáze bude vyzerať podobne ako v CSV súbore, s tým rozdielom, že mená vyučujúcich ukladáme spolu aj s titulmi v jednom poli. Ako pomocné budú slúžiť tabuľky „ucitelia“, v ktorej ukladáme jednotlivo tituly pred menom a za menom, meno a priezvisko a „administratori“, ktorá slúži na uloženie mien a hesiel ľudí, ktorí budú môcť spravovať službu rozvrhu. Polia vytvoríme tak, ako sú v CSV súbore. Pre reťazce zvolíme dátový typ VARCHAR, ktorý predstavuje reťazec s premenlivou dĺžkou a pre číselné hodnoty, dátový typ INTEGER, predstavujúci celé číslo. V ďalších kapitolách si vysvetlíme jednotlivé triedy webovej služby, ich funkcie a metódy. Metódy sú podrobne popísané pomocou komentárov v kóde služby. Zdrojové kódy vo forme TXT súborov sa nachádzajú na CD v adresári Webová služba. V práci budú metódy popísané menej podrobne, resp. vysvetlená len ich funkcia a účel.

Obr. 8 - Zobrazenie databázy v programe MySQL Query Browser

Trieda SQLManipulator

Táto trieda sprostredkováva komunikáciu služby s databázou MySQL, vytvára tabuľky, vkladá záznamy do databázy a naopak získava informácie z databázy podľa požiadavky služby, resp. klienta služby. Trieda bude pristupovať do databázy ako užívateľ root, z toho dôvodu, aby mala všetky právomoci pri práci s informáciami. Na obrázku 8 je vidieť UML diagram triedy SQLManipulator, na ktorom vidíme všetky premenné a metódy triedy. Ďalej si popíšeme dôležité metódy tejto triedy a ich funkciu.

Obr. 9 - UML diagram triedy SQLManipulator

Trieda má dva konštruktory a to jeden bez parametrov, ktorý nastaví premenné na štandardné hodnoty. Druhým konštruktorom môžeme parametre nastaviť na požadované hodnoty. Názvy premenných sú dostačujúco vysvetľujúce svoj účel.

 //Konštruktor bez parametrov
   public SQLManipulator() {
        SQLManipulator.user = null;                             
        SQLManipulator.password = null;                         
        SQLManipulator.sqlDriver = "com.mysql.jdbc.Driver";     
        SQLManipulator.sqlAdress = 
              "jdbc:mysql://localhost:3307/rozvrh?useUnicode=true&characterEncoding=utf8";//adresa MySQL servera
    }

 //Konštruktor s parametrami
   public SQLManipulator
            (String csvString, String user, String password, String sqlDriver, String sqlAddress) {
        
        this.csvRetazec = csvString;
        SQLManipulator.user = user;
        SQLManipulator.password = password;
        SQLManipulator.sqlDriver = sqlDriver;
        SQLManipulator.sqlAdress = sqlAddress;       
    }

Dôležitá metóda je getConnection, ktorá slúži na vytvorenie spojenia s databázou. Toto spojenie predstavuje trieda Connection, ktorá obsahuje metódy na vykonávanie SQL príkazov a získanie informácii z databázy vo forme špeciálnej výsledkovej tabuľky - ResultSet. Takto môžeme z jazyka Java volať príkazy jazyka SQL.

  public static Connection getConnection() {
    try {
            Class.forName(sqlDriver);
            if (user == null) {user = "root";}
            if (password == null) {password = "";}
            Connection con = DriverManager.getConnection(sqlAdress, user, password);
            return con;
        } catch (Exception ex3) {
            ex3.printStackTrace();
        }
        return null;
    }

Metódu vytvorTabulkuSQL je potrebné zavolať pri prvom spustení služby a vždy keď sa nahráva nový rozvrh. Metóda si vytvorí spojenie s databázou a potom otestuje, či existuje tabuľka RozvrhFM, ak áno, zmaže ju. Keď vytvárame tabuľku Adminstratori, testovanie či tabuľka existuje je rozdielne, pretože existujúcu tabuľku nechceme zmazať.

  public void vytvorTabulkuSQL() {
        try {
            con = SQLManipulator.getConnection();
            testujTabulku ("RozvrhFM");
            
            sql = "CREATE TABLE RozvrhFM ( " + " poradie_dna integer, " + " den VARCHAR (5) NOT NULL," + " cas VARCHAR (20) NOT NULL," + 
                  " vyucujuci VARCHAR (50)," + " zabezpecuje VARCHAR (20)," + " rocnik VARCHAR (10) NOT NULL," + 
                  " odbor VARCHAR (20) NOT NULL," + " forma VARCHAR (20) NOT NULL," + " stupen VARCHAR (10) NOT NULL," + 
                  " kruzok VARCHAR (10)," + " nazov VARCHAR (50) NOT NULL," + " miestnost VARCHAR (20) NOT NULL," + 
                  " druh VARCHAR(10) NOT NULL," + " poznamka VARCHAR (50)," + " poradie_tyzdna VARCHAR (20));";
            Statement stm = con.createStatement();
            stm.execute(sql);
            
            if (!testujTabulkuBool("Administratori")) {
                sql = "CREATE TABLE Administratori (uziv_id varchar (20) PRIMARY KEY NOT NULL, heslo varchar (40) NOT NULL);";
                stm.execute(sql);
            }
            con.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
   }

Metóda vlozDataURL slúži na skopírovanie súboru zo servera TnUAD na adrese: http://www.fm.tnuni.sk/rozvrh/rozvrh.csv. Tento súbor je potom uložený na server, na ktorom sa nachádza webová služba. Metóda porovnáva aktuálnosť súboru na školskom serveri s lokálnym súborom. Ak lokálny súbor je novší, neprepíše ho starším súborom zo serveru. Nakoniec táto metóda zavolá metódu vlozData, ktorá bude popísaná ako nasledujúca.

  public boolean vlozDataURL (String urlAdresa) {       
        SQLManipulator sqlMan = new SQLManipulator();
        
        try {
            URL url = new URL (urlAdresa);
            URLConnection urlCon = url.openConnection();
            InputStream is = url.openStream();
            FileOutputStream fos = null;
            
            File subor = new File ("rozvrhCopy.csv");
            
            datum = urlCon.getLastModified();
            datum2 = subor.lastModified();
            
            if (datum2 < datum) {
                fos = new FileOutputStream (subor);
                int oneChar;
            
                while ((oneChar = is.read()) != -1) {
                    fos.write(oneChar);                    
                }
                is.close();
                fos.close();
            }
            else
                System.out.println ("Lokalny subor je novsi, nie je nutny prepis...");           
        } catch (Exception ex) {
            ex.printStackTrace();
        }         
        return sqlMan.vlozData("rozvrhCopy.csv");
    }

Na prácu s CSV súborom použijeme triedu CSVReader. Je to parser pre parsovanie textových informácii zo súboru alebo z komunikačného kanála – streamu. Vďaka polymorfizmu má táto trieda dve metódy vlozData, z ktorej jedna nemá vstupný parameter, takže využíva reťazec csvRetazec, ktorý transformuje na vstupný prúd(InputStream). Slúži pre ten prípad, že služba bude spracovávať rozvrh, ktorý jej bude poslaný ako reťazec. Druhá metóda vlozData má vstupný parameter reťazec, ktorý predstavuje názov súboru, v ktorom je uložený rozvrh. Metóda vlozData využíva konštruktor tejto triedy CsvReader (String csvFile, char delimiter, Charset charset), kde vstupom je CSV súbor, vytvorený metódou vlozDataURL. Ďalším parametrom je deliaci znak – v našom prípade bodkočiarka. Posledným parametrom je znaková sada. MySQL databáza používa kódovanie znakov UTF-8, toto kódovanie znakov je vhodné pre jazyky ako je slovenčina, čeština a podobné. Je nutné si načítať názvy stĺpcov, ktoré sa nachádzajú v prvom riadku CSV súboru. Pomocou cyklu while načítavame záznamy z CSV súboru a pomocou SQL príkazu INSERT INTO ich vkladáme do databázy. Metódu sú takmer rovnaké, rozdielom je spracovanie vstupu, preto bude popísaná iba metóda vlozData(String subor), ktorú využijeme aj v práci.

  public boolean vlozData (String subor) {
        try{
            con = SQLManipulator.getConnection();
            
            CsvReader reader = new CsvReader (subor, znak, Charset.forName("utf-8"));
            
            reader.readHeaders();            
            
            int[] prvky = {9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
            int riadky = 0;
            
            while (reader.readRecord()) {
                PreparedStatement st;
                st = con.prepareStatement("INSERT INTO RozvrhFM VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");

                st.setString(1, reader.get(reader.getHeader(0)));
                st.setString(2, reader.get(reader.getHeader(1)));
                st.setString(3, reader.get(reader.getHeader(2)));

                String meno =
                        reader.get(reader.getHeader(4)) + reader.get(reader.getHeader(5)) + " " + 
                        reader.get(reader.getHeader(6)) + " " + reader.get(reader.getHeader(7)) + 
                        reader.get(reader.getHeader(8));
                st.setString(4, meno);

                for (int i = 5,  j = 0; i < 16; i++, j++) {
                    st.setString(i, reader.get(reader.getHeader(prvky[j])));
                }
                                
                riadky += st.executeUpdate();

            }
            reader.close();
            reader = new CsvReader (subor, znak, Charset.forName("utf-8"));
            vytvorUcitelov (reader);
            System.out.println("Pocet ulozenych riadkov: " + riadky);
            return true;
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

Metóda vlozData zavolala ešte nepopísanú metódu vytvorUcitelov. Metóda kombinuje postup vytvorenia tabuľky ako metóda vytvorTabulkuSQL a vkladanie dát do databázy ako metóda vlozData, preto kód tejto metódy nie je potrebné uvádzať.

Metóda update vytvorí inštanciu triedy SQLManipulator a zavolá metódy vytvorTabulkuSQL a vlozDataURL. Pomocou tejto metódy môže klient aktualizovať rozvrh v databáze bez toho, aby posielal službe nejaký CSV súbor. Metódy testujTabulku a testujTabulkuBool slúžia pre metódy vlozData a vlozUcitelov na testovanie, či daná tabuľka existuje s tým rozdielom, že metóda testujTabulku, existujúcu tabuľku zmaže a nemá návratovú hodnotu. Metóda testujTabulkuBool vracia hodnotu true, ak tabuľka existuje a false, ak tabuľka v databáze neexistuje. Princíp obidvoch funkcii je rovnaký, preto je uvedený kód len prvej spomínanej.

  private void testujTabulku (String menoTab) {
	try {
            con = SQLManipulator.getConnection();
            DatabaseMetaData meta = con.getMetaData();
            ResultSet result = meta.getTables(null, null, menoTab, null);
            
            if (result.next()) {
                sql = "DROP TABLE " + menoTab + ";";
                Statement stm = con.createStatement();
                stm.execute(sql);
            }
        } catch (SQLException ex) {
            ex.printStackTrace();
        }        
    }

Nasledujú funkcie, ktoré v databáze vyhľadávajú a vracajú požadované informácie. Najdôležitejšou z nich je metóda hladaj, ktorej návratová hodnota je ResultSet, čo je špeciálna tabuľka s rozvrhom. Metóda má vstupný parameter zoznam hľadaných položiek vo forme ArrayList. Podmienkou je, aby počet položiek v zozname súhlasil s počtom polí v databáze. Výstupný rozvrh je zoradený podľa dňa, času a krúžku.

  public ResultSet hladaj(ArrayList<String> polozka) 
            throws UnsupportedEncodingException {

        con = SQLManipulator.getConnection();
        ResultSet vysledok = null;

        sql = "SELECT * FROM RozvrhFM WHERE ";

        if (polozka.size() != stlpce.length) {
            System.out.println("Nesúhlasí počet stĺpcov a hladaných položiek!!!");
            polozka.ensureCapacity(stlpce.length);
        }

        for (int i = 0; i < polozka.size(); i++) {
            if (polozka.get(i) != null) {
                String preklad = new String (polozka.get(i).toString().getBytes("iso-8859-1"),"utf-8");
                
                sql = sql.concat(stlpce[i] + " LIKE \"%" + preklad + "%\" ");
            } else {
                sql = sql.concat(stlpce[i] + " LIKE \"%\" ");
            }

            if (i < polozka.size() - 1) {
                sql = sql.concat(" AND ");
            }
        }

        try {
            Statement stm = con.createStatement();
            sql = sql.concat(" ORDER BY poradie_dna, cas, kruzok");
            vysledok = stm.executeQuery(sql);
        } catch (Exception ex) {
            System.out.println("Chyba v SQL!!!");
            ex.printStackTrace();
        }

        return vysledok;
    }

Ďalšou metódou je getZoznam, táto metóda vracia všetky záznamy poľa v databáze. Vstupný parameter je reťazec s názvom poľa v databáze, ktoré chceme vypísať, napríklad miestnosť alebo krúžok. Výstupná hodnota na rozdiel od predchádzajúcej metódy je zoznam – ArrayList. Tento zoznam sa dá jednoducho prekonvertovať na reťazec, pomocou štandardnej metódy toString. Z tohto výpisu odstránime na začiatku a na konci hranatú zátvorku a máme výpis záznamov z databázy, kde deliaci znak je čiarka.

  public ArrayList getZoznam(String polozka) {
    ArrayList<String> zoznam = new ArrayList<String>();
    con = SQLManipulator.getConnection();
    try {
        sql = new String("SELECT DISTINCT " + polozka + " FROM RozvrhFM ORDER BY " + polozka);
        Statement stm = con.createStatement();
        ResultSet vysledok = stm.executeQuery(sql);
        while (vysledok.next()) {
           zoznam.add(vysledok.getString(polozka));}
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return zoznam;
  }

Poslednou metódou je getUcitelia, ktorá nemá žiadny vstupný parameter. Metóda slúži na vypísanie mien vyučujúcich z tabuľky Ucitelia. Meno vyučujúceho sa nachádza vo výpise len raz, čo zabezpečuje príkaz DISTINCT v SQL jazyku. Tento výpis je vkladaný do zoznamu ArrayList. Tento zoznam je prekonvertovaný na reťazec, z ktorého odstránim hranaté zátvorky . Tento reťazec, s menami vyučujúcich aj s titulmi je návratovou hodnotou tejto metódy.

  public String getUcitelia () {
        ArrayList<String> ucitelia = new ArrayList<String> ();        
        con = SQLManipulator.getConnection();
        try {
            sql =
                    new String("SELECT DISTINCT  CONCAT(titul_pred, krstne_meno,\" \"" +
                    ", priezvisko,\" \", titul_za, \" \", naj_titul) AS Ucitel FROM ucitelia " +
                    "ORDER BY priezvisko, krstne_meno;");
            Statement stm = con.createStatement();
            ResultSet vysledok = stm.executeQuery(sql);

            while (vysledok.next()) {
              ucitelia.add(vysledok.getString("Ucitel"));  
            }
            
            String uprav = ucitelia.toString();
            uprav = uprav.replace("[", "");
            uprav = uprav.replace("]", "");
            
            return uprav;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

Trieda XMLManipulator

XML

Jazyk XML je odvodený od jazyka SGML (Standard Generalized Markup Language) , podobne ako jazyk HTML. Slúži na výmenu informácii medzi aplikáciami. Jazyk XML oddeľuje spôsob formátovania dokumentu od jeho obsahu, to znamená, že XML dokument obsahuje informácie (napríklad názov knihy, počet strán a pod.), ale nie je v ňom napísané, ako sa majú informácie zobraziť. Na rozdiel od jazyka HTML je XML „prísnejší“, čo znamená, že treba dodržať tieto pravidlá:

  • každá značka musí mať začiatok a koniec, t.j. musia byť spárované. Nespárované značky z HTML ako
    alebo <IMG> nie sú povolené, t.j. treba ich napísať takto
    <img/>
  • každý XML dokument musí mať iba jeden koreňový element, takzvaný root element, ostatné sú odvodené. Kríženie značiek je zakázané, napríklad
    <nazov> <pocet kusov> </nazov> </pocet kusov>
  • všetky značky píšeme malými písmenami
  • hodnoty atribútov sú vždy v úvodzovkách

Webová služba rozvrhu fakulty vracia rozvrh aj vo forme dokumentu XML. Takto formátovaný rozvrh je ľahko spracovateľný aj inou aplikáciou, napr. J2ME aplikácia spustená na mobilnom telefóne pošle požiadavku webovej službe a tá odošle ako odpoveď rozvrh vo forme XML.

Koreňový (root) element je <rozvrh>, ktorý má potomkov <info> a <den> . Počet potomkov elementu <info> je závislí od počtu vyhľadávaných výrazov, ale daný potomok sa môže vyskytnúť v dokumente len raz. Maximálny počet je deväť a môžu to byť tieto: <forma>, <rok>, <kruzok>, <druh>, <odbor>, <kruzok>, <miestnost>, <ucitelia>, <predmet> Ďalší element je <den> s atribútom nazov, ktorý obsahuje názov dňa. Potomkom tohto elementu sú elementy <predmet>, ktoré popisujú názov predmetu, čas konania a podobne, pre daný deň, resp. zvolenú miestnosť. Na obrázku 10 je vidieť, ako vypadá XML dokument, keď sme zadali vyhľadať rozvrh v miestnosti Alf 002.

Obr. 10 - XML dokument s rozvrhom

Popis triedy XMLManipulator

Potom ako sme si uložili rozvrh FM z CSV súboru do databázy, je potrebné získať informácie späť, podľa požiadaviek klienta a výpis z databázy konvertovať do požadovanej formy, t.j. do XML dokumentu alebo HTML stránky. Táto trieda slúži na konvertovanie výpisu z databázy na požadovaný formát.

Na spracovanie výpisu z databázy na XML dokument použijeme DOM parser. Tento parser spracuje celý dokument naraz, uloží ho do pamäte a vytvorí stromovú štruktúru, narozdiel od SAX parsera, ktorý spracováva dokument sekvenčne. DOM parser bol vyvinutý konzorciom W3C v polovici 90.-tých rokov. Využíva sa na platformovo a jazykovo nezávislú reprezentáciu HTML a XML dokumentov.

Metódy tejto triedy budeme volať v triede TomcatSluzba, ktorá vytvorí rozhranie medzi klientom a službou. Tak umožníme klientovi, aby si sám zvolil formu výpisu rozvrhu FM.

Obr. 11 - UML diagram triedy XMLManipulator

Prvá dve metódy addHladane a getHladane sú štandardné metódy typu seter/getter, ktoré slúžia na nastavenie alebo získanie privátnej premennej triedy, ktorou je v tomto prípade zoznam typu ArrayList, v ktorom sú uložené položky, ktoré chceme v databáze vyhľadať.

Metóda getXML vytvorí rozhranie Document, ktoré umožňuje pracovať s informáciami pomocou stromovej štruktúry (elementov). Metóda vytvorí takýto dokument, v ktorom budú uložené informácie z databázy v predpísanej forme, viď. napríklad obrázok 10. Nakoniec táto metóda zavolá metódu convertToXML, ktorá má dva vstupné parametre. Prvým je dokument, obsahujúci požadované informácie a druhým je celé číslo, podľa ktorého určujeme výstupný formát. Číslo 1 znamená, že chceme výstup vo forme XML dokumentu, 2 vo forme HTML dokumentu. Číslo 3 používa iná metóda, ktorú ďalej spomeniem.

  public String getXml(ArrayList<String> hladane, int typSub) {
        ResultSet rs = null;
        
        try {
            DocumentBuilderFactory factory =
                    DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.newDocument();
            Element rozvrh = doc.createElement("rozvrh");
            doc.appendChild(rozvrh);

            Element info = doc.createElement("info");
            rozvrh.appendChild(info);
            for (int i = 0;i<new SQLManipulator().stlpce.length; i++){
                if (hladane.get(i) != null) {
                    Element infoPolozka = doc.createElement(new SQLManipulator().stlpce[i]);   
                    infoPolozka.appendChild(doc.createTextNode(hladane.get(i).toString());
                    info.appendChild(infoPolozka);
                }
            }

            rs = new SQLManipulator().hladaj(hladane);
            ResultSetMetaData rsmd = rs.getMetaData();
            int colCount = rsmd.getColumnCount();
            
            String denstary = "nie";
            Element den = null;
            
            while (rs.next()) {
                String dennovy = rs.getString("den");
                
                if (! dennovy.equals(denstary)) { 
                 den = doc.createElement("den");
                 rozvrh.appendChild(den); 
                 den.setAttribute("nazov", rs.getString("den"));
                 denstary = dennovy;
                }

                Element predmet = doc.createElement("predmet");
                den.appendChild(predmet);
                for (int ii = 1; ii <= colCount; ii++) {
                    String columnName = rsmd.getColumnName(ii);
                    Object value = rs.getObject(ii);
                    if (ii != 2 && ii != 11) {
                        if (value.equals("")) {
                            predmet.setAttribute(columnName, "&nbsp;"); }
                        else {
                            predmet.setAttribute(columnName, value.toString());}
                    } else if (ii == 11) {
                        if (value.equals("")) {
predmet.appendChild(doc.createTextNode("&nbsp;"));
                        } else {
            predmet.appendChild(doc.createTextNode(value.toString()));
                        }}}
            }
            String vysledok = convertToXml(doc, typSub);
            return vysledok;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
  }

Metóda convertToXML využíva DOM parser, ktorý pracuje s dátami uloženými v dokumente (v stromovej štruktúre). Dôležitá je trieda Transformer, ktorá pretransformuje dokument s rozvrhom do výstupného prúdu. Ako vstupný pre Transformer môžeme použiť buď dokument, ak chceme, aby výstup bol XML dokument. Ak však chceme výstup mať vo formáte HTML, musíme použiť triedu StreamSource, ktorá umožní transformáciu dokumentu podľa formátovacieho dokumentu XSLT. Tento dokument slúži na definovanie tvaru výstupného dokumentu. Ukážka formátovacieho XSLT dokumentu je na CD v adresári Webová služba.

  public String convertToXml(Document doc, int typSub)
            throws TransformerConfigurationException, TransformerException {

        DOMSource domSource = new DOMSource(doc);
        TransformerFactory tf = TransformerFactory.newInstance();
        
        Transformer transformer = null;
        StreamSource zdroj1 = new StreamSource (
                getClass().getResource("zobrazR.xslt").toString());
        StreamSource zdroj2 = new StreamSource (
                getClass().getResource("zobrazS.xslt").toString());
        
        switch (typSub) {
            case (1): transformer = tf.newTransformer(); break;
            case (2): transformer = tf.newTransformer(zdroj1); break;
            case (3): transformer = tf.newTransformer(zdroj2); break;
            default: return "Zle zadany typ suboru!";
        }

     transformer.setOutputProperty(OutputKeys.ENCODING, "iso-8859-1");

        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        java.io.StringWriter sw = new java.io.StringWriter();
        StreamResult sr = new StreamResult(sw);
        transformer.transform(domSource, sr);
        return sw.toString();
    }

Trieda TestYahoo

Táto trieda slúži len ako ukážka komunikácie dvoch webových služieb. Spoločnosť Yahoo poskytuje vyhľadávaciu webovú službu Yahoo Search. Túto službu voláme pomocou HTTP a služba nám vráti výsledok hľadania vo forme XML dokumentu. Spoločnosť Yahoo po bezplatnej registrácii pošle e-mailom jedinečné ID, ktorým sa bude identifikovať aplikácia využívajúca službu Yahoo Search, v tomto prípade webová služba rozvrhu FM.

Obr. 12 - UML diagram triedy TestYahoo

Najskôr si spomenieme dve pomocné metódy tejto triedy a to metódu zmenInputStreamNaString, ktorá transformuje vstupný prúd InputStream na reťazec, využívajúc triedy BufferedReader a StringBuilder.

  public String zmenInputStreamNaString(InputStream is) {
        BufferedReader buf = new BufferedReader(new InputStreamReader(is, Charset.forName("utf-8")));
        StringBuilder sb = new StringBuilder();

        String riadka = null;

        try {
            while ((riadka = buf.readLine()) != null) {
                sb.append(riadka + "\n");
            }
        } catch (IOException ex) {
            return "Chyba vytvarania reťazca!";
        } finally {
            try {
                is.close();
            } catch (IOException ex) {
                return "Chyba pri zatvarani InputStream";
            }
        }

        return sb.toString();
    }

A druhú metódu zmenStringNaDocument, ktorá transformuje reťazec na dokument. Túto metódu používame, keď chceme odpoveď služby Yahoo Search, ktorá je vo forme XML dokumentu pretransformovať na HTML. Takto konvertovaný reťazec na dokument vložíme ako vstupný parameter metóde convertToXML z triedy XMLManipulator, ktorá sa postará o túto transformáciu. V tomto prípade je nutné ako druhý parameter tejto metódy zvoliť celé číslo 3, ktoré znamená, že sa má použiť iný formátovací súbor XSLT, ako v prípade rozvrhu.

Trieda hladaj, má vstupný parameter hľadaný reťazec, ktorý treba vložiť do vyhľadávacej požiadavky. Tá vyzerá nasledovne: http://search.yahooapis.com/WebSearchService/V1/webSearch?appid=EvdriYPV34GwOYnxZHI7ZQogndvFaRwxPYNNFRGF0urtl240seyfbrrv3Dp_Xu2MSRMzFQ--&query=" HLADANÝ VÝRAZ" Ako je vidieť, parametre sa posielajú metódou HTTP/GET. Dôležitým parametrom je appid, ktoré nám pridelila firma Yahoo. Nasleduje parameter query, do ktorého vkladám hľadaný výraz. Zoznam parametrov, ktoré môžeme použiť je na stránke: http://developer.yahoo.com/search/web/V1/webSearch.html

public String hladaj(String hladane) {
  String request = "http://search.yahooapis.com/WebSearchService/V1/webSearch?" +   
                   "appid=EvdriYPV34GwOYnxZHI7ZQogndvFaRwxPYNNFRGF0urtl240seyfbrrv3Dp_Xu2MSRMzFQ--" +
                   "&query=" + hladane + "&results=10&country=cz";
  InputStream in = null;
  try {
      URL url = new URL(request);
      in = url.openStream();
      return zmenInputStreamNaString(in);

  } catch (Exception e) {
      e.printStackTrace();
      return "Web services request failed";            
  } finally {
      try {
            if (in != null)
                in.close();
      } catch (IOException ex) {
          return "Chyba pri zatvarani InputStream";
      }
  }
}

Trieda TomcatSluzba

Posledná trieda TomcatSluzba slúži na vytvorenie webovej služby. Zabezpečuje komunikáciu s klientom a vytvorenie SOAP správy. Trieda tvorí rozhranie medzi funkčným kódom služby a klientom služby. Metódy tejto triedy sú verejne prístupné, to znamená, že ich môžeme volať vzdialene.

Obr. 13 - UML diagram triedy TomcatSluzba


Dôležité je na začiatku triedy definovať, že sa jedná a webovú službu a akým štýlom budeme môcť so službou komunikovať. V tomto prípade pomocou vzdialených volaní RPC (Remote Procedure Calls).

  @WebService()
  @SOAPBinding(style = SOAPBinding.Style.RPC)

  public class TomcatSluzba {...}

Metóda vratRozvrh predstavuje hlavný cieľ bakalárskej práce a to vytvorenie služby rozvrhu fakulty mechatroniky. Klient odošle službe reťazec, obsahujúci položky, podľa ktorých sa má v databáze hľadať a formát, v akom chce rozvrh dostať. Podmienkou je aby reťazec obsahujúci hľadané položky, obsahoval trinásť položiek oddelených výkričníkom, napríklad: “null! null! null! null! null! null! null! null! null! null! null! null! null“. Ak pošlem službe takýto reťazec, vráti celý rozvrh fakulty. Znova je dôležité uviesť anotáciu @WebMethod(...), ktorá určuje, že sa jedná o metódu webovej služby a je tak verejne prístupná. Popis jednotlivých anotácii sa nachádza v adresári Dokumenty v súbore anotácie.pdf a zoznam všetkých anotácií sa nachádza na stránke: http://edocs.bea.com/workshop/docs81/doc/en/core/index.html

  @WebMethod(operationName = "vratRozvrh")
  public String vratRozvrh(
            @WebParam(name = "hladane") String hladane,
            @WebParam(name = "typSuboru") int typSub) throws UnsupportedEncodingException {

        xmlApache = new XMLManipulator();
        StringTokenizer token = new StringTokenizer(hladane, "!");
        int pocet = 0;
        while (token.hasMoreTokens()) {
            String zaznam = token.nextToken();
            if (!zaznam.equals("null")) {
                xmlApache.addHladane(zaznam);
            } else {
                xmlApache.addHladane(null);
            }
            pocet++;
        }

        if (pocet != 13) {
            return "Chyba: Nesúhlasí počet hladaných položiek.";
        } else {
            String navrat = xmlApache.getXml(xmlApache.getHladane(), typSub);
            String pom = new String (navrat.getBytes("utf-8"), "iso-8859-1");
            if (navrat != null) {
                pom = pom.replace("amp;", "");
                return pom;
            } else {
                return "Chyba: Hladanie v DB nefunguje.";

            }
        }
    }

Metóda vratPolozky, slúži na výpis záznamov z databázy. Vstupným parametrom je názov poľa (stĺpca) v databáze.

  @WebMethod(operationName = "vratPolozky")
  public String vratPolozky(
            @WebParam(name = "polozka") String polozka) {
        try {
            sqlApache = new SQLManipulator();
            String vystup = sqlApache.getZoznam(polozka).toString();
            vystup = vystup.replace("[", "");
            vystup = vystup.replace("]", "");
            vystup = new String(vystup.getBytes("utf-8"), "iso-8859-1");
            
            return vystup;
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
        return "Chyba v metóde vratPolozky()";
    }

Metóda vratUcitelov slúži na výpis mien učiteľov z tabuľky Ucitelia. Jej princíp je rovnaký ako vyššie uvedená metóda a preto nebude uvedený.

Metóda ulozRozvrh slúži ako administrácia rozvrhu. Vstupným parametrom je rozvrh fakulty vo forme CSV súboru. Metóda vytvorí potrebné tabuľky, zmaže staré údaje a uloží nové do databázy.

  @WebMethod(operationName = "ulozRozvrh")
  public String ulozRozvrh(@WebParam(name = "csvRetazec")
    String csvRetazec) { 
        try {
            csvRetazec = new String (csvRetazec.getBytes("iso-8859-1"), "utf-8");
            sqlApache = new SQLManipulator();
            sqlApache.nacitajCSV(csvRetazec);
            sqlApache.vytvorTabulkuSQL();
            sqlApache.vytvorUcitelov();

            String odpoved = new String();
            if (sqlApache.vlozData()) {
                odpoved = "Vloženie údajov do databázy prebehlo v poriadku";
            } else {
                odpoved = "Chyba pri vkladaní údajov do databázy";
            }
            odpoved = new String(odpoved.getBytes("utf-8"), "iso-8859-1");
            return odpoved;
        
        } catch (UnsupportedEncodingException ex) {
            return ex.toString();
        }
    }

Nasledujúcou metódou tejto triedy je metóda hladajYahoo, ktorá má vstupný parameter hľadaný výraz. Zavolá metódy z triedy TestYahoo, dostane odpoveď od webovej služby Yahoo Search vo forme XML dokumentu. Tento dokument potom pretransformuje pomocou metódy convertToXml z triedy XMLManipulator. Výsledok je HTML stránka, na ktorej sa nachádzajú odkazy na stránky, v ktorých sa vyskytuje hľadaný výraz.

    @WebMethod(operationName = "hladajYahoo")
    public String hladajYahoo(@WebParam(name = "hladanaPolozka")
    String hladanaPolozka) {
        try {
            String vstup  = new String(hladanaPolozka.getBytes("iso-8859-1"), "utf-8");
            System.out.println ("Hladam: " + vstup);
            
            TestYahoo yahoo = new TestYahoo();
            String pom = yahoo.hladaj(vstup);
            
            System.out.println(pom);
            Document doc = yahoo.zmenStringNaDocument(pom);
            
            String vystup = new XMLManipulator().convertToXml(doc, 3);
            vystup  = new String(vystup.getBytes("utf-8"), "iso-8859-1");
            System.out.println(vystup);
           
            if (!vystup.equals(null))
                return vystup;
            else
                return "Zadaný reťazec sa nenašiel";
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "Chyba: Služba vyhľadávania v Yahoo nefunguje.";
    }
  }

Metóda updateRozvrh volá metódu update z triedy SQLManipulator. Jej význam bol vysvetlený vyššie. Posledná metóda tejto triedy je infoDate, ktorá posiela klientovi informácie o poslednej zmene súboru na servery fakulty a lokálnom súbore.

    @WebMethod(operationName = "infoDate")
    public String infoDate() {
        try {      
             String odpoved = "Posledná zmena suboru URL: " + date1 + 
                "<BR>Posledna zmena lokalneho suboru: " + date2;
        
            return new String(odpoved.getBytes("utf-8"), "iso-8859-1");
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
        return "Chyba pri zistovani datumov zmien suborov";
    }

Kompilácia služby

Potom ako máme napísaný kód služby je nutné ho skompilovať. Vo vývojovom prostredí NetBeans IDE 6.1, si klikneme v záložke Projects pravým tlačítkom myši na projekt webovej služby rozvrhu FM a v menu si zvolíme položku Build. Ďalej sa o všetko postará vývojové prostredie, ktoré vytvorí WAR súbor.

WAR súbor je archivačný súbor webovej služby, v ktorom sú uložené všetky potrebné súbory pre spustenie webovej služby na aplikačnom servery. Štruktúru súboru si môžeme pozrieť na priloženom CD v adresári Webová služba\Štruktúra WAR súboru, kde je WAR súbor rozbalený.