Java na webovém serveru: implementujeme Jabber
Dnes si povíme, jak vytvořit pro naši aplikaci webový chat. A nebude to chat ledajaký, použijeme oblíbený protokol XMPP (Jabber) a napojíme se na existující server. Díky tomu si spolu budou moci povídat jak náhodní kolemjdoucí, kteří přišli na web, tak i uživatelé klasických IM klientů.
Seriál Java na webovém serveru
- Java na webovém serveru: SOAP webové služby
- Java na webovém serveru: hlasování a grafy v SVG
- Java na webovém serveru: Komentáře a integrace s Texy
- Java na webovém serveru: AJAX formuláře
- Java na webovém serveru: implementujeme Jabber
Pro komunikaci XMPP protokolem použijeme knihovnu Smack, která nám přináší javovské API a odstíní nás od nízkoúrovňové komunikace s Jabber serverem.
Jako obvykle si nejprve si aktualizujeme zdrojové kódy aplikace pomocí Mercurialu:
$ hg pull $ hg up "16. díl"
Případně je můžete stáhnout jako bzip2 archiv přes web.
Základní práce se Smack knihovnou
Smack je klientská XMPP knihovna od autorů Jabber serveru Openfire. Pomocí následujícího kódu se připojíme k serveru a autentizujeme:
ConnectionConfiguration nastaveni = new ConnectionConfiguration("doména-server");
spojeni = new XMPPConnection(nastaveni);
spojeni.connect();
spojeni.login("jméno", "heslo", "zdroj");
Připojení a autentizace jsou oddělené do dvou kroků, protože některé servery umožňují anonymní přístup a některé operace lze provádět už před přihlášením (např. založení nového účtu).
Práce s knihovnou je poměrně jednoduchá a intuitivní. Pomocí následujícího kódu vstoupíme do místnosti a odešleme do ní zprávu:
MultiUserChat muc = new MultiUserChat(spojeni, "název_místnosti");
muc.sendMessage("ahoj");
Příjem zpráv je realizován pomocí posluchačů (listener), které zaregistrujeme, a kteří pak zpracovávají události. Jedná se o stejný princip, jakým se obsluhují události např. ve Swingu (GUI). Posluchače zaregistrujeme pomocí volání metody muc.addMessageListener() a musí implementovat metodu processPacket() z rozhraní PacketListener:
public void processPacket(Packet packet) {
if (packet instanceof Message) {
Message m = (Message) packet;
String od = StringUtils.parseResource(m.getFrom());
String text = m.getBody();
/** uděláme něco se zprávou… */
}
}
EJB komponenta
Možná teď přemýšlíte, jak skloubit dohromady navazování spojení s chatovacím serverem a bezestavový HTTP protokol, který používáme na webu. Budeme se přihlašovat k Jabber serveru s každým HTTP požadavkem a po jeho vyřízení spojení zahazovat? Ne, tohle naštěstí není potřeba, Java nabízí řešení v podobě EJB komponent, které „žijí“ na serveru po celou dobu běhu aplikace a mohou tak držet jedno trvalé XMPP spojení. V rámci jednotlivých HTTP požadavků se pak k této komponentě připojíme a využijeme jejích služeb. Toto téma jsme už nakousli v díle srovnávajícím PHP a Javu. Dnes se dostaneme k praktické ukázce.
Základem naší komponenty je třída cz.frantovo.nekurak.ejb.ChatEJB
@Singleton
@Startup
public class ChatEJB implements ChatRemote {
private static final Logger log = Logger.getLogger(ChatRemote.class.getSimpleName());
private Nastaveni nastaveni;
private Collection<Spojeni> spojeni = new ArrayList<Spojeni>();
@Override
public void posliZpravu(String mistnost, String prezdivka, String zprava) throws NekurakVyjimka {
MistnostPripojena mp = najdiMistnost(mistnost);
if (mp == null) {
throw new NekurakVyjimka("Místnost s tímto názvem neexistuje", null);
} else {
try {
mp.posliZpravu(new ZpravaChatu(prezdivka, zprava));
} catch (Exception e) {
log.log(Level.SEVERE, "Selhalo odesílání zprávy", e);
throw new NekurakVyjimka("Zprávu se nepodařilo odeslat.", e);
}
}
}
/**
* @param mistnost název místnosti včetně zavináče a serveru
* @param poradoveCislo pořadové číslo poslední zprávy, kterou jsme dostali
* @return všechny novější zprávy než dané pořadové číslo
* @throws NekurakVyjimka
*/
@Override
public Collection<ZpravaChatu> getZpravy(String mistnost, int poradoveCislo) throws NekurakVyjimka {
MistnostPripojena mp = najdiMistnost(mistnost);
if (mp == null) {
throw new NekurakVyjimka("Místnost s tímto názvem neexistuje", null);
} else {
return mp.getZpravy(poradoveCislo);
}
}
public ChatEJB() throws NekurakVyjimka {
/** TODO: vyřešit lépe. */
nastaveni = new SpravceNastaveni().getNastaveni();
}
@PreDestroy
public void odpoj() {
for (Spojeni s : spojeni) {
s.odpoj();
}
}
@PostConstruct
public void inicializuj() throws NekurakVyjimka, NamingException {
pripojXMPP();
}
private void pripojXMPP() throws NekurakVyjimka {
try {
for (UcetRobota u : nastaveni.getUctyRobota()) {
Spojeni s = new Spojeni(u);
spojeni.add(s);
}
} catch (Exception e) {
throw new NekurakVyjimka("Chyba při připojování.", e);
}
}
/**
* @param nazev Název místnosti, kterou hledáme.
* @return nalezená místnost, nebo null, pokud místnost nebyla nalezena.
*/
private MistnostPripojena najdiMistnost(String nazev) {
for (Spojeni s : spojeni) {
for (MistnostPripojena mp : s.getMistnosti()) {
if (mp.porovnejNazev(nazev)) {
return mp;
}
}
}
return null;
}
Důležité jsou zde použité anotace. @Singleton říká, že EJB komponenta bude v systému jen jedna, což v našem případě znamená, že z našeho serveru povede jen jedno XMPP spojení na Jabber server, bez ohledu na to, kolik klientů náš server bude obsluhovat. Pomocí anotace @Startup říkáme, že se komponenta má vytvořit hned po nasazení aplikace (deploy). Jinak by se totiž vytvořila až ve chvíli, kdy by ji poprvé někdo potřeboval. Anotací @PreDestroy pak označíme metodu, která se postará o korektní ukončení navázaného XMPP spojení – zavolá se např. při vypínání aplikačního serveru nebo deaktivaci aplikace.
Na straně klienta
Nad EJB vrstvou máme webové rozhraní (viz chat.jsp), které zpřístupňuje funkcionalitu komponenty webovému prohlížeči. V něm pomocí AJAXu odesíláme zprávy do chatovací místnosti a periodicky kontrolujeme zda přišly nějaké nové zprávy. Tento kód naleznete v souboru chat.js.
Závěr
V dnešním díle jsme se naučili pracovat s XMPP protokolem v Javě, což se nám může hodit i jinde než na webu – např. při tvorbě IM klienta nebo XMPP služby. Webovou část našeho chatu bychom později mohli přepsat tak, aby nevyžadovala periodickou kontrolu nových zpráv, např. s použitím moderní technologie Webových socketů. K tomu je ale potřeba podstatnější zásah do naší komponenty – je třeba ji upravit, aby posílala nové zprávy všem přihlášeným klientům a ne jen pasivně čekala, až se jí někdo zeptá, jaké jsou nové zprávy.
Odkazy
- XMPP – Extensible Messaging and Presence Protocol.
- Smack – Knihovna pro práci s XMPP (Jabberem).
- Smack – dokumentace ke knihovně.
- New Features in EJB 3.1 – Novinky v EJB 3.1 (např. Singletony).
Školení Google+ pro firmy

- Jak využít Google+ pro firemní komunikaci a marketing.
- Čím se liší Google+ od Twitteru a Facebooku z pohledu firemního využití.
- Jak využít Google+ v souladu s pravidly užívání.
- Založení Google+ Page (Stránky) krok po kroku, včetně praktických tipů.
Detailní informace o školení Google+ »
Seriál Java na webovém serveru
- Java na webovém serveru: SOAP webové služby
- Java na webovém serveru: hlasování a grafy v SVG
- Java na webovém serveru: Komentáře a integrace s Texy
- Java na webovém serveru: AJAX formuláře
- Java na webovém serveru: implementujeme Jabber
Přehled názorů
Tento text je již více než dva měsíce starý. Chcete-li na něj reagovat v diskusi, pravděpodobně vám již nikdo neodpoví. Pro řešení aktuálních problémů doporučujeme využít naše diskusní fórum.