Návrh webovej služby rozvrhu fakulty mechatroniky: Rozdiel medzi revíziami

Z Kiwiki
Skočit na navigaci Skočit na vyhledávání
(Vytvorená stránka „=Webové služby= ==Definícia webovej služby== ''Webová služba je softwarová aplikácia definovaná pomocou URI, ktorej interfejsy a väzby je možné definovať, opí…“)
 
d (Zamyká „Návrh webovej služby rozvrhu fakulty mechatroniky“ ([edit=sysop] (na neurčito) [move=sysop] (na neurčito)))
 
(7 medziľahlých úprav od 2 ďalších používateľov nie je zobrazených)
Riadok 1: Riadok 1:
=Webové služby=
+
[[Kategória:Bakalárske práce]]
==Definícia webovej služby==
+
[[Kategória:Webové služby]]
''Webová služba je softwarová aplikácia definovaná pomocou URI, ktorej interfejsy a väzby je možné definovať, opísať a vyhľadať ako artefakty XML. Podporuje priamu interakciu s inými softwarovými prostredníctvom správ zapísaných v jazyku XML a prenášanými protokolmi internetu.''<ref>Buranský, I.: XML a webové služby, Microsoft 2002</ref>
+
[[Kategória:Java]]
 +
{{Praca_uvod|2|Webové služby v Jave|Webové služby|Návrh webovej služby rozvrhu fakulty mechatroniky|Implementácia webových služieb v Jave|Klient webovej služby rozvrhu FM||||||||||}}
 +
__TOC__
 +
 
 +
= =
 +
==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.
 +
[[Súbor:csv_new.png|center|frame|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.
 +
[[Súbor:navrh.png|center|frame|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.
 +
[[Súbor:db.png|center|frame|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.
 +
[[Súbor:mySQL.png|center|frame|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.
 +
[[Súbor:umlSQL.png|center|frame|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.
 +
<source lang="java">
 +
//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;     
 +
    }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  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;
 +
    }
 +
</source>
 +
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ť.
 +
<source lang="java">
 +
  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();
 +
        }
 +
  }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  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");
 +
    }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  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;
 +
    }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  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();
 +
        }       
 +
    }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  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;
 +
    }
 +
</source>
 +
Ď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.
 +
<source lang="java">
 +
  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;
 +
  }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  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;
 +
    }
 +
</source>  
 +
==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 <BR> alebo <IMG> nie sú povolené, t.j. treba ich napísať takto <br/><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.
 +
[[Súbor:xmlFile.png|center|frame|Obr. 10 - XML dokument s rozvrhom]]
 
 
Webovými službami rozumieme akúkoľvek službu, ktorá je umiestnená niekde na internete a ku ktorej môžeme pristupovať pomocou štandardných webových protokolov ako sú napríklad HTTP alebo SMTP. Komunikácia webových služieb prebieha pomocou štandardizovaného jazyka XML, čo nám umožňuje nezávislosť na platforme i na programovacom jazyku.
+
===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.  
  
Webové služby majú tieto charakteristické znaky:
+
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.  
*založené na XML – umožňuje webovým službám byť nezávislé od operačného systému, od väzieb na sieťové protokoly
 
*voľná väzba – klient nie je viazaný so službou, takže pri zmene kódu na strane servera nie je nutná zmena na strane klienta, čo umožňuje väčšiu možnosť managementu softwaru
 
*„coarse-grained“ (granularita) – dáta sa vymieňajú občas, po prebehnutí väčšieho množstva výpočtov
 
*synchrónnosť a asynchrónnosť – pri synchrónnych službách klient dostane odpoveď  až po vykonaní operácie. Pri asynchrónnych môže klient zadávať funkcie a postupne bude dostávať odpovede 
 
*podpora vzdialeného volania procedúr ( Remote Procedure Calls – RPC) – webové služby umožňujú klientovi volať funkcie a metódy na vzdialených objektoch pomocou XML protokolu.
 
*podpora výmeny dokumentov – umožňuje výmenu kompletných dokumentov, od adries až po kompletné knihy
 
*opísateľnosť – webová služba by mala byť aspoň minimálne opísaná v čitateľnej forme, aby ju klient mohol ľahko implementovať. Ak služba používa SOAP, je potrebné aby pomocou XML boli popísane verejné funkcie, argumenty a návratové hodnoty
 
*objavitelnosť- webová služba by sa mala dať jednoducho nájsť vo verejnom registri
 
 
==Použitie webových služieb==
 
Je snaha vytvoriť automatizovanú sieť služieb, ktoré by medzi sebou komunikovali bez interakcie s užívateľom a tak užívateľa odbremenili od opakujúcich sa úkonov.  
 
  
Máme spoločnosť STROJ s.r.o., ktorá chce zakúpiť software, ktorý zautomatizuje nákup súčiastok. Priebeh operácie by vypadal nasledovne:  
+
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.  
#skladová aplikácia sa pripojí na centralizovaný register webových služieb a vyhľadá si službu, ktorá poskytuje objednávkový systém súčiastok. Register vráti informáciu o webovej službe spoločnosti Súčiastky a.s. a ukazateľ na popis tejto služby.
+
[[Súbor:umlXML.png|center|frame|Obr. 11 - UML diagram triedy XMLManipulator]]
#skladová aplikácia sa spojí s objednávkovou službou Súčiastky a.s. a obdrží popis služby
+
#popis služby obsahuje všetky potrebné detaily na pripojenie sa so službou. Potom nie je pre skladovú aplikáciu problém automaticky objednávať potrebné súčiastky
+
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ť.  
  
Novým trendom v informačných technológiách je vývoj inteligentného agenta. Je snaha vytvoriť software, ktorý by predstavoval automatizovaného elektronického asistenta, ktorý by odbremenil užívateľa od opakujúcich sa operácii, pomáhal by pri plnení úloh a učil sa od užívateľa. Inteligentných agentov môžeme rozdeliť do niekoľkých skupín:
+
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.
*'''trhový agent''' – tento agent prehľadáva sieť www a získava informácie o tovare a službách. Dokáže zistiť najnižšiu cenu, dostupnosť a objednať Vám zvolený tovar až k Vám domov. Ukážkou môže byť Amazon.com
+
<source lang="java">
*'''osobný agent''' – tento agent sa chová podľa vášho chovania. Môže čítať emaily, vyznačovať vám dôležité informácie na www stránkach, zisťovať informácie o zvolenom objekte alebo udalosti, hrať hru ako váš protihráč alebo vypĺňať formuláre za vás
+
  public String getXml(ArrayList<String> hladane, int typSub) {
*'''monitorovací a dozorný agent''' – tiež známy ako „predvídavý“ agent. Slúži na pozorovanie a dohľad nad vecami. Napríklad NASA má takéhoto agenta, ktorý sleduje stavy skladov, dopĺňa chýbajúce súčiastky a dozerá na dátumy spotreby jedla v sklade. Takýto agent môže byť pripojený aj na komplexnú sieť počítačov a sledovať nastavenia jednotlivých počítačov v sieti
+
        ResultSet rs = null;
*'''informačný agent''' – (data mining) tento agent slúži na to, že prezerá rôzne zdroje informácii a hľadá v nich zvolenú informáciu. A podľa zvolenej informácie dokáže vykonať aj určitú akciu
+
       
 +
        try {
 +
            DocumentBuilderFactory factory =
 +
                    DocumentBuilderFactory.newInstance();
 +
            DocumentBuilder builder = factory.newDocumentBuilder();
 +
            Document doc = builder.newDocument();
 +
            Element rozvrh = doc.createElement("rozvrh");
 +
            doc.appendChild(rozvrh);
  
Webové služby sú ideálnym nástrojom na tvorbu takýchto inteligentných agentov, ktorý sú teraz v popredí výskumu informačných technológii. Aj z tohto dôvodu získavajú webové služby veľkú popularitu a svoje miesto v informačných technológiách.
+
            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);
 +
                }
 +
            }
  
V dnešných dňoch je skloňovaným pojmom v informačno-komunikačných technológiách SOA. SOA je architektonický štýl navrhnutý na dosiahnutie voľného prepojenia heterogénnych softvérových komponentov. Táto architektúra má niekoľko vrstiev, z ktorých jednu tvoria webové služby. Webové služby vytvárajú úžitkové metódy, operácie s entitami a vyhľadávacie metódy v systémových dátach. Webové služby umožňujú zabaliť logiku, integráciu kódu treťostranných nástrojov  a implementáciu z aplikačnej vrstvy.
+
            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;
 +
    }
 +
  }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  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();
 +
    }
 +
</source>
 +
 
 +
==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.
 +
[[Súbor:umlYahoo.png|center|frame|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.
 +
<source lang="java">
 +
  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();
 +
    }
 +
</source>
 +
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
 +
<source lang="java">
 +
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);
  
==Technológie používané webovými službami==
+
  } catch (Exception e) {
Existuje množstvo technológii, ktoré využívajú webové služby a ešte mnoho ich bude vyvinutých. Vízia webových služieb je celosvetová integrácia, ktorá však nie je možná, pokiaľ všetok software od rôznych firiem nebude podporovať základné technológie webových služieb. Medzi primárne technológie webových služieb patria:
+
      e.printStackTrace();
*'''Simple Object Access Protocol (SOAP)''' – zabezpečuje základnú obaľovaciu štruktúru pre transportovanie XML dokumentov cez štandardné sieťové protokoly ako sú HTTP, SMTP a FTP. Poskytuje jednoduchú štruktúru pre volanie RPC, čo umožňuje .NET klientovi vyvolať EJB´s (Enterprise Java Beans) a opačne Java klientovi vyvolať .NET Component pomocou SOAP
+
      return "Web services request failed";           
*'''XML – RPC''' – je to jednoduchý protokol, ktorý využíva XML na volanie vzdialených procedúr . Požiadavka je zakódovaná do XML a poslaná pomocou HTTP POST.  
+
  } finally {
*'''Web Services Descripting Language(WSDL)''' – WSDL je XML technológia, ktorá popisuje rozhranie webových služieb štandardizovanou cestou. Popisuje vstupné a výstupné parametre služby, štruktúru funkcii a protokoly, pomocou ktorých služba komunikuje. Po prečítaní WSDL klient rozumie, ako služba funguje
+
      try {
*'''Universal Description, Discovery, and Integration (UDDI)''' -  poskytuje celosvetový register (XML) webových služieb. V registri si môžeme nájsť webovú službu pomocou mena, kategórie alebo identifikátora. Potom stačí len implementovať požadovanú službu do vlastného kódu aplikácie.
+
            if (in != null)
 +
                in.close();
 +
      } catch (IOException ex) {
 +
          return "Chyba pri zatvarani InputStream";
 +
      }
 +
  }
 +
}
 +
</source>
 +
==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.
 +
[[Súbor:umlTomcat.png|center|frame|Obr. 13 - UML diagram triedy TomcatSluzba]]
  
==Tvorba klienta webovej služby==
 
  
#Identifikovať a nájsť požadovanú webovú službu, najlepšie pomocou UDDI
+
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).
#Nájsť popis služby. Pre SOAP je to WSDL dokument a pre XML-RPC treba nájsť zrozumiteľný popis pre integráciu služby.
+
<source lang="java">
#Napísať kód klientskej aplikácie. Pre WSDL je možnosť použiť automatického generovania kódu pomocou špecializovaných nástrojov.
+
  @WebService()
#Spustiť aplikáciu, ktorá vyvolá webovú službu
+
  @SOAPBinding(style = SOAPBinding.Style.RPC)
  
==Tvorba webovej služby (servera)==
+
  public class TomcatSluzba {...}
#Napísať zdrojový kód aplikácie
+
</source>
#Obaliť zdrojový kód do SOAP alebo XML-RPC obaľovača (w rapper)
+
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
#Popísať službu. Pre SOAP službu treba napísať WSDL dokument
+
<source lang="java">
#Spojazdniť webovú službu. Záleží od vašich požiadaviek, či vytvoríte samostatný server alebo umiestnite na webový server
+
  @WebMethod(operationName = "vratRozvrh")
#Zverejnenie webovej služby. Zaregistrovanie do UDDI
+
  public String vratRozvrh(
 +
            @WebParam(name = "hladane") String hladane,
 +
            @WebParam(name = "typSuboru") int typSub) throws UnsupportedEncodingException {
  
==Popísanie webovej služby==
+
        xmlApache = new XMLManipulator();
Pri komunikácii webových služieb (web-services) je potrebné popísať komunikáciu týchto služieb štandardizovanou formou a na to slúži WSDL. WSDL popisuje, čo služba robí, ako vyvolať jej operácie a kde sa nachádza. WSDL definuje pomocou XML jazyka webové služby ako komunikáciu dvoch koncových bodov (endpoints) schopných výmeny správ. WSDL poskytuje dokumentáciu pre distribuované systémy a podáva návod pre automatizáciu detailov vyskytujúcich sa pri komunikácii aplikácii. Pre vývojárov client-side aplikácii WSDL popisuje všetky potrebné informácie, ktoré služba vyžaduje.  
+
        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++;
 +
        }
  
Podrobnejšie informácie o WSDL dokumente a jeho elementoch nájdete v dokumente WSDL-Web Services Description Language.pdf, ktorý sa nachádza na priloženom CD v adresári Dokumenty.
+
        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.";
  
==Java a webové služby==
+
            }
Firma Sun Microsystems vyvinula v roku 1995 objektovo orientovaný programovací jazyk Java. Java sa syntakticky najviac približuje jazyku C++. Java sa radí medzi hybridné jazyky. To znamená, že je súčasne prekladaná i interpretovaná. Program sa najprv preloží do špeciálneho tvaru nazývaného bajtkód (bytecode), ktorý potom analyzuje a interpretuje špeciálny program nazývaný virtuálny stroj Javy (Java Virtual Machine – JVM). Java patrí medzi najbezpečnejšie jazyky, preto je vo veľkej forme využívaná pre internetové aplikácie.
+
        }
To ju predurčuje pre tvorbu webových služieb.
+
    }
 +
</source>
 +
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.
 +
<source lang="java">
 +
  @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()";
 +
    }
 +
</source>
 +
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ý.
  
Keďže Java je moderný jazyk preto sa stále dynamicky rozvíja. Stále sa vyvíjajú nové balíky, ktoré uľahčujú vytváranie webových služieb. Príkladom je balík javax.jws, ktorý umožňuje programátorovi služby sústrediť čas len na vývoj funkčného kódu služby a nemusí sa zaoberať o spracovávanie SOAP správy, vytváranie spojenia s klientom a iných úkonov súvisiacich s webovými službami. Viac informácii o SOAP sa nachádza v dokumente SOAP.pdf na priloženom CD v adresári Dokumenty.
+
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.
 +
<source lang="java">
 +
  @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();
  
==Príklady existujúcich webových služieb==
+
            String odpoved = new String();
V dnešnej dobe sa na internete dá nájsť veľké množstvo webových služieb a medzi ne patria:
+
            if (sqlApache.vlozData()) {
*National Weather Service  - NORA (National Oceanic and Atmospheric Administration´s) vyvinula webovú službu, ktorá poskytuje svetové počasie. Popis služby sa nachádza na:http://www.weather.gov/forecasts/xml/DWMLgen/wsdl/ndfdXML.wsdl
+
                odpoved = "Vloženie údajov do databázy prebehlo v poriadku";
*Vyhľadávacie služby napríklad Google Search a Yahoo Search, ktorú použijeme ako príklad v našej službe
+
            } else {
*Lokalizačné služby napríklad Google Map alebo Microsoft MapPoint Web Service vyvinutá spoločnosťou Microsoft, ktorá umožňuje pomocou súradníc nájsť určitý bod v mapách alebo naplánovať trasu, informácie sa nachádzajú na stránke: http://www.microsoft.com/mappoint/products/webservice/default.mspx
+
                odpoved = "Chyba pri vkladaní údajov do databázy";
*Lighting Calculation Service  - webová služba vypočíta silu osvetlenia v miestnosti. Jej WSDL popis nájdeme na URL: http://www.fold1.com/SOAP/foldcalc.php?wsdl
+
            }
*RC Filter – webová služba simuluje jednoduchý RC filter. Poskytovateľ je Vanguard Software. WSDL sa nachádza na:
+
            odpoved = new String(odpoved.getBytes("utf-8"), "iso-8859-1");
http://wiki.vanguardsw.com/bin/ws.dsb?wsdl/Engineering/Electrical/RC%20Filter
+
            return odpoved;
 +
       
 +
        } catch (UnsupportedEncodingException ex) {
 +
            return ex.toString();
 +
        }
 +
    }
 +
</source>
 +
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.
 +
<source lang="java">
 +
    @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.";
 +
    }
 +
  }
 +
</source>
 +
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.
 +
<source lang="java">
 +
    @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";
 +
    }
 +
</source>
 +
==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.
  
'''Odkazy'''
+
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ý.
<references/>
 

Aktuálna revízia z 18:36, 28. február 2010

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ý.