390 likes | 491 Views
Mre žno računarstvo. Chapter 19 The JavaMail API. Uvodna priča. Kako poslati mail iz Java apleta ili aplikacije? JavaMail API je standardna Java ekstenzija koja obezbeđuje biblioteku klasa za email klijente.
E N D
Mrežno računarstvo Chapter 19 The JavaMail API
Uvodna priča • Kako poslati mail iz Java apleta ili aplikacije? • JavaMail API je standardna Java ekstenzija koja obezbeđuje biblioteku klasa za email klijente. • Programi koriste JavaMail API kako bi komunicirali sa SMTP, POP i IMAP serverima i slali i primali email. • Ovaj API nas štiti od detalja niskog nivoa protokola
Upotreba • pisanje standardnih email klijenata • mailing list menadžeri • veće aplikacije koje šalju ili primaju manji email • aplet može koristiti email da pošalje podatke bilo kom procesu ili osobi na Internetu koja ima email adresu, koristeći web serverov SMTP server kao jednostavan proksi da zaobiđe uobičajena bezbednosna ograničenja koja se odnose na to sa kim aplet može da komunicira • Obrnuto, aplet može komunicirati sa IMAP serverom na hostu apleta kako bi primio podatke sa proizvoljno puno hostova širom neta.
Šta je JavaMail API? • JavaMail API je reprezentacija prilično visokog nivoa osnovnih komponenata proizvoljnog email sistema. • Komponente su predstavljene apstraktnim klasama paketa javax.mail • Npr. apstraktna klasa javax.mail.Message predstavlja email poruku. Ona deklariše apstraktne metode za dohvatanje i postavljanje raznih informacija o poruci, kao što su pošiljalac i primalac, datum slanja, subject poruke. • Apstraktna klasa javax.mail.Folder predstavlja kontejner poruka. Ona deklariše apstraktne metode za dohvatanje poruke iz foldera, premeštanje poruka iz foldera u folder i brisanje poruka iz foldera
Sve ove klase su apstraktne jer ne prave nikakve pretpostavke o tome kako se email čuva ili šalje između mašina • Konkretne potklase ovih klasa specijalizuju apstraktne klase za određene protokole i mail formate. • Ako želimo da radimo sa standardnim Internet email-om, možemo koristiti javax.mail.MimeMessage umesto javax.mail.Message, javax.mail.InternetAddress umesto javax.mail.Address i com.sun.mail.imap.IMAPStore umesto javax.mail.Store.
Korišćeni protokol formati i pridružene klase su određene uglavnom jednom linijom kôda u našem programu koja imenuje protokol. Promena imena protokola čini 90% posla da program umesto jednog protokola (npr. POP) koristi drugi (npr. IMAP)
Servis provajderi implementiraju pojedinačne protokole. Servis provajder je grupa konkretnih potklasa apstraktnih JavaMail API klasa koje specijalizuju opšti API za određeni protokol i mail format. • Pošto mail-ovi stižu sa mreže u vremenskim trenucima koje nije moguće predvideti, JavaMail API se zasniva na mehanizmu događaja za rukovanje dolazećom poštom. Ovo je potpuno ista šema kao kod AWT. • java.mail.event paket definiše razne vrste događaja, kao i pridružene listener interfejse i adapterske klase za ove događaje
mail.jar • email poruke mogu sadržati višejezične tekstove kao i multimediju. JavaMail API koristi JavaBeans Activation Framework (JAF) za opisivanje i prikaz ovih sadržaja. • JavaMail API je standardna Java ekstenzija, nije deo JDK ili JRE. Zato je nužno download-ovati ga odvojeno i instalirati na sopstvenom sistemu. Dostupan je na http://www.oracle.com/technetwork/java/index-138643.html • To je .zip arhiva koja sadrži dokumentaciju, sample code i, najvažnije od svega, mail.jar fajl. Ovaj fajl sadrži .class fajlove koji implementiraju JavaMail API. Da bi se kompajlirali i pokretali primeri iz ovog poglavlja, neophodno je dodati ovaj fajl u class path, bilo dodavanjem njegove putanje u CLASSPATH environment promenljivu ili smeštanjem mail.jar u naš jre/lib/ext direktorijum
activation.jar • JavaBeans Activation Framework (JAF) je takođe standardna Java ekstenzija. • Može se preuzeti sa: http://www.oracle.com/technetwork/java/javase/downloads/index-135046.html • Tu se nalazi activation.jar fajl koji takođe treba smestiti u class path. • VAŽNO: Sastavni je deo Java SE 6 i 7, tako da za njih nije potrebno posebno ga dodavati!
Slanje email-a • Osnovna email potreba • email klijenti i mailing list menadžeri su jedina vrsta programa koji primaju email-ove • ali sve vrste programa ih šalju • npr. web browser-i mogu poslati HTML forme putem email-a. • Books & Writers izvršava popularni servis koji prati rang prodaje knjiga autora na Amazon.com i obaveštava ih periodično email-om. • U današnjem uvrnutom svetu, daleko najjednostavniji način da obavestite korisnike o događaju kada oni ne sede za računarom na kome se izvršava program jeste poslati im email.
Koraci • JavaMail API obezbeđuje sve što je potrebno programu kako bi poslao email. Da bi poslao poruku, program prati sledećih 8 jednostavnih koraka: • postavljanje mail.host svojstva da pokazuje na lokalni mail server • pokretanje mail sesije metodom Session.getInstance() • kreiranje novog Message objekta, verovatno instanciranjem jedne od konkretnih potklasa klase Message • postavljanje From: adrese poruke • postavljanje To: adrese poruke • postavljanje Subject: poruke • postavljanje sadržaja poruke • slanje poruke metodom Transport.send() Poredak ovih koraka nije strogo fiksiran. Npr. koraci 4 do 7 mogu seizvršiti bilo kojim redom. Pojedinačno, svaki od koraka je prilično jednostavan.
koraci 1 i 2 • Prvi korak je postavljanje svojstava mail sesije. Jedino svojstvo koje treba postaviti da bi se mail poslao je mail.host. Ono se konfiguriše java.util.Properties objektom. Sledeće parče kôda postavlja mail.host svojstvo na mail.cloud9.net: Properties props = new Properties(); props.put("mail.host", "mail.cloud9.net"); • Naravno, naši programi će postavljati ovo svojstvo na ime našeg mail servera.(zagmail je potrebnopostavitijoš dva svojstva) • Ova svojstva se koriste kako bi se dobio Session objekat metodom Session.getInstance(): Session mailConnection = Session.getInstance(props, null); • Session objekat predstavlja komunikaciju između programa i jednog mail servera. Drugi argument metoda getInstance(), ovde null, je javax.mail.Authenticator koji traži šifru od korisnika ako je server zahteva. Više reči o tome biće kasnije.
korak 3 • Session objekat se koristi za konstruisanje novog Message objekta: Message msg = new MimeMessage(mailConnection); • Ovde je zadata klasa MimeMessage pošto znamo da ćemo slati Internet email. • Međutim, ovo je mesto gde se zaista eksplicitno bira format email poruke. U nekim slučajevima, ovo ne mora biti neophodno, ako se može prekopirati format dolazeće poruke
koraci 4 i 5 – I • Sada kada imamo Message objekat, treba da postavimo njegova polja i sadržaj. From: adresa i To: adresa će biti javax.mail.internet.InternetAddress objekti. Može se zadati samo email adresa ili adresa zajedno sa imenom: Address bill = new InternetAddress("god@microsoft.com", "Bill Gates"); Address elliotte = new InternetAddress("elharo@metalab.unc.edu");
koraci 4 i 5 – II • Metod setFrom() nam omogućuje da kažemo ko šalje poruku postavljanjem From:. Nema zaštite od zloupotrebe. Prilično je jednostavno da se pretvaramo da smo Bill Gates: msg.setFrom(bill); • Metod setRecipient() je za nijansu komplikovaniji. Ne zadajemo samo adresu na koju će poruka biti poslata, već kako se adresa koristi, tj. kao To:, Cc: ili Bcc: polje. Za ovo postoje 3 mnemoničke konstante klase Message.RecipientType: Message.RecipientType.TO Message.RecipientType.CC Message.RecipientType.BCC • Npr. msg.setRecipient(Message.RecipientType.TO, elliotte);
koraci 6, 7 i 8 • Subject se postavlja kao string. Npr. msg.setSubject("You must comply"); • Telo se takođe postavlja kao string. Međutim, pored tog teksta treba proslediti i MIME type teksta. Najuobičajeniji je text/plain. Npr. msg.setContent("Resistance is futile. You will be assimilated! ", "text/plain"); • Najzad, statički Transport.send() metod konektuje se na mail server određen mail.host svojstvom i šalje poruku njenim putem: Transport.send(msg);
Primer 1 • Objedinjava sve ovo u program koji šalje poruku. (Bitno je znati primaoca i pošiljaoca, subject i telo poruke)
Primer 2: Slanje email-a iz Aplikacije • Primer 1 je jednostavna aplikacija koja šalje fiksiranu poruku na poznatu adresu i sa zadatim subject-om. • Jednom kada smo videli kako se to radi, pravolinijski je zameniti stringove koji određuju poruku, adresu, subject i telo podacima koji se čitaju iz komandne linije, GUI-ja, baze podataka ili nekog drugog izvora. • Primer 2 je veoma jednostavan GUI za slanje email-a. Sav kôd za slanje mail-a je u metodu actionPerformed() i izgleda veoma slično main() metodu iz Primera 1. Velika razlika je što se sada host, subject, From: adresa, To: adresa i tekst poruke čitaju iz GUI komponenata u vreme izvršavanja programa umesto da budu ukodirani kao string konstante u source kôd. Ostatak kôda odnosi se na podešavanje GUI-ja i ima malo sa JavaMail API-jem.
Primer 2: napomene • Ovo je daleko od idealnog. GUI bi morao biti jasnije odvojen od kôda za mail. I bilo bi bolje otvoriti dijalog o grešci ako nešto pođe po zlu, nego samo štampati stack trace izuzetka na System.err. • Međutim, pošto nas ništa od toga ne bi naučilo nečemu o JavaMail API, to je ostavljeno za vežbu
Primer 3: Slanje email-a iz Apleta • U terminima GUI-ja i JavaMail API-ja, nema razlike između slanja email-a iz apleta i iz aplikacije. • Međutim, security manager browser-a nam se može naći na putu. Kako i sve ostalo, tako i JavaMail API ne može zaobići uobičajene restrikcije mrežnih konekcija za aplete. Aplet koji želi da pošalje mail i dalje može komunicirati samo sa hostom sa kog je sam aplet došao. • Na sreću, mnogi hostovi koji izvršavaju web servere takođe izvršavaju i SMTP servere. Ako je to slučaj, prilično je pravolinijski da aplet pošalje mail. JavaMail API i Java Activation Framework nisu uključeni u većinu browser-a, ali pošto su implementirani u čistoj Javi u paketu javax, browser-i mogu download-ovati neophodne klase sa servera. Npr. ovaj APPLET element referiše ne samo kôd samog apleta, već takođe i mail.jar i activation.jar fajlove za JavaMail API i Java Activation Framework, redom:
<APPLET CODE=SMTPApplet ARCHIVE="activation.jar,mail.jar" WIDTH=600 HEIGHT=400> <PARAM NAME="to" VALUE= "hamp@sideview.mtsterling.ky.us"> <PARAM NAME="subject" VALUE="Hay Orders"> <PARAM NAME="from" VALUE="none"> </APPLET> za Java SE 6 i 7 radi i bez activation.jar
Primer 3 je jednostavanapletkojišalje email. Adresa na koju se email šalje i subject se čitaju iz PARAM tagova. Adresa sa koje se šalje email se takođe čita iz PARAM taga, ali korisnik ima opciju da to promeni. Tekst koji se šalje korisnik ukucava u tekstualno polje. Konačno, server se određuje na osnovu codebase apleta
Primer 3: napomene • Ispravno ponašanje ovog apleta zavisi od nekoliko spoljašnjih faktora: • browser mora podržavati bar Java1.1 sa security modelom ne restriktivnijim od podrazumevanog • mail.jar i activation.jar fajlovi moraju biti dostupni u codebase apleta • web server koji opslužuje aplet mora takođe biti i SMTP server koji želi da prosledi mail od klijentskog sistema do sistema primaoca. Ovo može biti problem (i dovesti do izuzetka: javax.mail.SendFailedException: 550 <hamp@sideview.mtsterling.ky.us”> ... Relaying denied) • Međutim, trebalo bi da budemo u mogućnosti da pošaljemo email bar na adrese koje se nalaze u domenu web servera. Možemo da podesimo neku od tih adresa da automatski forward-uje poruke njihovom eventualnom primaocu
Primanje mail-a • Kompleksnije je od slanja • SMTP koristi samo 14 različitih komandi, a jednostavan email klijent se može implementirati korišćenjem samo 5 od njih • POP3, međutim, ima 12 komandi, ali klijent treba da koristi skoro sve • IMAP ima 24 različite komande
JavaMail API je dizajniran sa pretpostavkom da server može vratiti zaglavlja odvojeno od poruka kojima pripadaju, pretraživati mailbox-ove, obezbeđivati smeštaj za poruke umesto da to radi klijent itd. • JavaMail API nam obezbeđuje manje nego što nam treba za klijentski orijentisane protokole pristupa mail-ovima, kao što je POP3, koji pretpostavlja da klijent čuva i održava arhivu mail-ova, ali nam i dalje pruža alat za download-ovanje mail-a sa servera. Samo treba da implementiramo sopstveni sistem smeštaja na klijentu
Mi ćemo videti jednostavniji POP protokol (u nastavku ima i IMAP) • Iz perspektive JavaMail-a, IMAP se može videti kao POP plus neke komande za manipulaciju folderima. Za jednostavne programe koji operišu samo INBOX folderom, POP i IMAP klijenti su manje-više isti
koraci • Ima oko 12 koraka za čitanje udaljenog mailbox-a (broj koraka može neznatno varirati, pošto su neki koraci opcioni ili se mogu kombinovati sa ili zameniti drugima): • postavljanje svojstava koja ćemo koristiti za konekciju • konstruisanje Authenticator-a koji ćemo koristiti za konekciju • Dohvatanje Session objekta metodom Session.getDefaultInstance() • Korišćenje metoda getStore() sesije za dohvatanje Store • Konektovanje na store • dohvatanje INBOX foldera iz store metodom getFolder() • otvaranje INBOX foldera • otvaranje foldera unutar INBOX foldera i ponavljanje ovoga neophodan broj puta da bi se stiglo do foldera koji se traži • dohvatanje poruka iz foldera kao niza Message objekata • iteriranje kroz niz poruka, procesiranje svake od njih korišćenjem metoda klase Message. Npr. možemo štampati svaku poruku ili prosto prikazati pošiljaoca, subject i druge značajne informacije u GUI-ju • zatvaranje foldera • zatvaranje store
korak 1 – svojstva sesije • Svaki od ovih koraka ponaosob je prilično jednostavan. • Prvi je postavljanje svojstava za mail sesiju. Svojstva koja bismo želeli da postavimo uključuju mail.host, mail.store.protocol, mail.user, mail.pop3.user i mail.pop3.host. • Međutim, ne moramo da postavimo baš nijedno od ovih. Ako će Session biti korišćen samo za dohvatanje mail-a, prazan Properties objekat je dovoljan. Npr. Properties props = new Properties();
koraci 2 i 3 • Dalje, želimo da kreiramo instancu javax.mail.Authenticator klase (pravilnije, instancu njene konkretne potklase) koja može tražiti šifru od korisnika. Za sada, prosto ćemo ukodirati te vrednosti i proslediti null umesto stvarnog Authenticator-a. Popravićemo ovo kasnije: Authenticator a = null; • Dalje, koristimo ove Properties i Authenticator objekte da dobijemo Session instancu: Session session = Session.getDefaultInstance(props, a);
koraci 4 i 5 • Tražimo od sesije store za provajdera. Ovde, želimo provajder za POP3: Store store = session.getStore("POP3"); • Konačno, spremni smo da se zaista konektujemo na store koristeći metod connect(). Treba proslediti host na koji se konektujemo i korisničko ime i šifru koji će se koristiti: store.connect("mail.cloud9.net", "elharo", "my_password"); • Možemo proslediti null za šifru da ukažemo da se prethodno zadati Authenticator treba pobrinuti za šifru
korak 6 • Sada kada je store konektovan, spremni smo da otvorimo folder u store. Ovaj korak je više orijentisan na IMAP nego na POP, pošto POP serveri ne vode računa o različitim folderima. Za svrhe JavaMail API-ja, POP3 provajderi koriste folder sa imenom INBOX: Folder inbox = store.getFolder("INBOX");
korak 7 • Folder je zatvoren kada ga dohvatimo. Možemo vršiti neke operacije na zatvorenom folderu, uključujući njegovo brisanje ili preimenovanje, ali ne možemo dohvatati poruke iz zatvorenog foldera. Prvo ga moramo otvoriti. • Folder možemo otvoriti sa pristupom samo za čitanje, prosleđivanjem mnemoničke konstante Folder.READ_ONLY metodu open(), ili konstante Folder.READ_WRITE za pristup za čitanje i pisanje: inbox.open(Folder.READ_ONLY);
korak 9 • Sada smo spremni da download-ujemo poruke metodom getMessages(), koji vraća niz koji sadrži sve poruke u folderu: Message [] messages = inbox.getMessages(); • Klasa Message obezbeđuje mnogo metoda za rad sa pojedinačnim porukama. Ona poseduje metode za dohvatanje raznih polja iz zaglavlja poruke, dohvatanje sadržaja poruke, odgovaranje na poruku i drugo. • Sada, uradićemo najprostiju stvar koja se može zamisliti – odštampati svaku poruku na System.out koristeći metod writeTo() poruke:
koraci 10, 11 i 12 for(int i = 0; i < messages.length; i++){ System.out.println("----------- Message " + (i+1) + "--------------"); messages[i].writeTo(System.out); } • Nakon što smo završili sa porukama, zatvaramo folder i zatim zatvaramo store metodima close(): inbox.close(false); store.close(); Argument false metoda close() foldera ukazuje da ne želimo da server zaista izbriše bilo koju obrisanu poruku iz foldera. Prosto želimo da raskinemo našu konekciju sa ovim folderom.
Primer 4: POP3Client • Primer objedinjuje sve ovo u jednostavan program koji download-uje i štampa sadržaj određenog POP mailbox-a. Poruke se prosto štampaju na System.out u podrazumevanom načinu kodiranja. Serveri, korisnička imena i ostalo je sve ukodirano. Međutim, primer brzo demonstrira većinu ključnih tačaka primanja mailova korišćenjem JavaMail API-ja. Napredniji program bi uključio odgovarajući GUI. • Jedina izmena koja je potrebna da bi program radio za IMAP bila bi postavljanje provider promenljive na imap umesto na pop3.
Password Authentication • Ugrađivanje šifara u source code (kao u Primeru 4) je jako loša ideja. Ako se zahteva šifra, treba je zahtevati od korisnika u vreme izvršavanja programa. Dalje, kada korisnik kuca šifru, ona ne treba da bude prikazana na ekranu. Idealno, ona ne treba ni da se šalje kao čist tekst preko mreže, iako trenutno mnogo klijenata i servera to radi.
Kada se otvara konekcija ka message store, JavaMail API nam dopušta da obezbedimo javax.mail.Authenticator objekat koji može da koristi za dobijanje korisničkog imena i šifre. Authenticator je apstraktna klasa • Kada provajder treba da zna korisničko ime i šifru, poziva metod getPasswordAuthentication() korisnički definisane potklase klase Authenticator. Ovaj metod vraća PasswordAuthentication objekat koji sadrži ove informacije: protected PasswordAuthentication getPasswordAuthentication()
Primer 5. GUI authenticator • Da bi se u naše programe dodala password autentifikacija u vreme izvršavanja programa, izvede se potklasa klase Authenticator i predefiniše getPasswordAuthentication() metodom koji ume da bezbedno traži šifru od korisnika. Jedan koristan alat za ovaj proces jeste JPasswordField Swing komponenta. • Primer 5 demonstrira Swing-zasnovanu Authenticator potklasu koja otvara dijalog koji traži da korisnik unese svoje korisničko ime i šifru
Primer 6 • Većina ovog kôda samo rukuje GUI-jem. • JPasswordField čuva šifru kao niz karaktera tako da kada završimo rad sa šifrom, možemo je prepisati nulama. Ovo znači da šifra postoji u memoriji kraće i manje je verovatno da će sistem virtualne memorije swap-ovati program na disk i ostaviti šifru tamo kao čist tekst. Međutim, PasswordAuthentication čuva šifre kao stringove, koji su nepromenljivi i time mogu nenamerno biti sačuvani na disku • Modifikovanje POP klijenta kako bi podržao ovaj stil autentifikacije je pravolinijsko. To demonstrira Primer 6. Menjamo ugrađeno korisničko ime i šifru null-ovima i prosleđujemo MailAuthenticator instancu kao drugi argument metoda connect(). • Jedina preostala izmena je da pozivamo System.exit() na kraju main() metoda, pošto se program više neće završiti kada se završi main() nakon što je pokrenuta AWT nit.