CzechIdM – Java – E-maily s přílohou
Minulý týden jsem naučil náš Identity Manager CzechIdM odesílat e-maily s přílohami. Situací, ve kterých se to může hodit, je celá řada: předvyplněný formulář v PDF k tisku, konfigurační soubor k nově zřízené VPN, certifikát nebo třeba aktuální verze telefonního seznamu. V článku vás rychle s novou funkcí seznámím, naučím vás ji používat a společně se podíváme, jak CzechIdM tvoří instanci třídy MimeMessage, která se pro odesílání e-mailu z Javy standardně používá.
Třída Application
Identity Manager CzechIdM ctí obvyklé rozdělení na datovou, aplikační a prezentační vrstvu. Odesílání e-mailu provádí samozřejmě vrstva aplikační – tuto službu poskytuje především procesům, které jsou napsány na míru zákazníkovi. Univerzálním rozhraním aplikační vrstvy pro zákaznické procesy je třída Application. Na ní coby programátoři a administrátoři CzechIdM najdete spoustu užitečných statických metod, logováním do auditního logu počínaje a ověřováním práv přihlášeného uživatele konče.
Odesílání e-mailů mají na starost dvě statické metody: sendEmailToIdentity a sendEmailToAddress. Obě jsou přetížené, tedy existují ve více variantách s různými počty parametrů. My se budeme zabývat pouze těmi nejobecnějšími, ve kterých může programátor uvést parametrů nejvíc. Jsou to:
Application.sendEmailToIdentity(String userName, String emailTemplate, Map params, String language, List<EmailAttachment> attachments) Application.sendEmailToAddress(String address, String emailTemplate, Map params, String language, List<EmailAttachment> attachments)
Volání metod
Parametr userName, respektive address určují adresáta zprávy (buď názvem identity v CzechIdM nebo přímo e-mailovou adresou), emailTemplate označuje šablonu, která má být pro sestavení zprávy použita a params značí atributy zprávy, které mají být do šablony vloženy. Parametrem language lze zvolit jazyk, pokud šablona existuje ve více jazykových mutacích. Zadáte-li null, zvolí se první nalezená šablona. Poslední parametr – attachments – je nový a značí seznam příloh k odeslání.
Instance třídy EmailAttachment představuje jednu přílohu. CzechIdM umí posílat přílohy dvěma způsoby – buď odešle soubor existující někde na disku, nebo dynamicky generované pole bytů. Třída EmailAttachment představuje jen zapouzdřené informace o jedné příloze, jako programátor nebo administrátor bohatě vystačíte s jejími dvěma konstruktory:
EmailAttachment(String path, String fileName) EmailAttachment(byte[] bytes, String mimeType, String fileName)
První značí přílohu uloženou v již existujícím souboru na disku (cesta v parametru path), druhý slouží k odeslání pole bytů (parametr bytes). Pomocí parametru fileName lze přílohu přejmenovat pro oči adresáta. Parametr mimeType umožní blíže specifikovat, jak mají být byty interpretovány – jestli jako obrázek, text, video…
Příklad
Následující kód odesílá přílohu postupně oběma způsoby, jako pole bytů i jako existující soubor na disku:
import eu.bcvsolutions.idm.app.Application; import eu.bcvsolutions.idm.app.email.EmailAttachment; HashMap params = new HashMap(); //mapa parametru. Temito parametry bude doplnena e-mailova sablona, #{attr1} v sablone bude nahrazeno retezcem "val1" params.put("attr1", "val1"); params.put("attr2", "val2"); params.put("attr3", "val3"); //seznam priloh, ktere v mailu odesleme List attachments = new ArrayList(); //prilohy lze pridat dvema zpusoby - bud primym predanim cesty k souboru a nazvu, pod jakym ma byt predan uzivateli EmailAttachment attach1 = new EmailAttachment("/opt/attachments/ahoj.txt", "ahoj.txt"); //nebo primym predanim pole bytu vcetne mime typu a nazvu souboru byte[] bytes = {0x41, 0x48, 0x4F, 0x4A}; EmailAttachment attach2 = new EmailAttachment(bytes, "text/plain", "ahoj.txt"); attachments.add(attach1); attachments.add(attach2); Application.sendEmailToAddress("moje.adresa@test.com", "sablonaTestovaci", params, "cz", attachments);
Jak to funguje uvnitř
Nejdůležitější částí kódu je sestavení MimeMessage, tedy instance třídy, která bude následně odeslána. V CzechIdM se o tento úkol stará metoda createMimeMessage na třídě IdMMailerQueueListener. Netradiční je především použití třídy Multipart, která umožňuje sestavit zprávu z více částí, přičemž některými částmi jsou právě přibalované přílohy:
private MimeMessage createMimeMessage(Session session, MapMessage mapMessage) throws AddressException, MessagingException, JMSException { Charset charset = Charset.forName("UTF-8"); MimeMessage mimeMessage = new MimeMessage(session); mimeMessage.setFrom(new InternetAddress(mapMessage.getString(Constants.EMAIL_FROM))); InternetAddress [] recipients = { new InternetAddress(mapMessage.getString(Constants.EMAIL_TO)) }; mimeMessage.setRecipients(javax.mail.Message.RecipientType.TO, recipients); mimeMessage.setSubject(mapMessage.getString(Constants.EMAIL_SUBJECT), charset.name()); mimeMessage.setSentDate(new java.util.Date()); Multipart multipart = new MimeMultipart(); // cast s textem MimeBodyPart messageBodyPart = new MimeBodyPart(); //naplnim cast textem messageBodyPart.setText(mapMessage.getString(Constants.EMAIL_CONTENT), charset.name()); messageBodyPart.setHeader("Content-Type", mapMessage.getString(Constants.EMAIL_CONTENT_TYPE) + "; charset=" + charset.name()); multipart.addBodyPart(messageBodyPart); //zpracovani priloh, ktere jsou z technickych duvodu predany serializovane byte[] serializedList = mapMessage.getBytes(Constants.EMAIL_ATTACHMENTS); ObjectInputStream ois = null; List<EmailAttachment> attachs = new ArrayList<EmailAttachment>(); if (serializedList != null) { try { ois = new ObjectInputStream(new ByteArrayInputStream(serializedList)); attachs = (List<EmailAttachment>) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (ois != null) { try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } } //postupne pridam do zpravy vsechny prilohy, ktere mi byly zadany for (EmailAttachment attach : attachs) { messageBodyPart = new MimeBodyPart(); DataSource source = null; //podle typu prilohy se rozhodnu, jak budu nacitat data switch(attach.getType()) { case FILE: File attachment = new File(attach.getFilePath()); if (!attachment.canRead() || attachment.isDirectory() || attachment.isHidden()) { Application.logError("Attachment " + attachment.getAbsolutePath() + " was not sent!", new Object[0]); throw new RuntimeException("Attachment failed: " + attach.getFilePath()); } source = new FileDataSource(attachment); break; case BYTES: source = new ByteArrayDataSource(attach.getBytes(), attach.getMimeType()); break; default: throw new RuntimeException("Unknown EmailAttachment Type " + attach.getType()); } messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(attach.getFileName()); multipart.addBodyPart(messageBodyPart); } // pridam vsechny casti do zpravy mimeMessage.setContent(multipart); mimeMessage.saveChanges(); //zprava je pripravena k odeslani return mimeMessage; }
Závěr
V článku jsem vás naučil odesílat prostřednictvím CzechIdM e-maily s přílohou a nechal jsem vás nahlédnout do jedné z klíčových procedur aplikační vrstvy, která používá běžné mechanismy Javy. Kdybyste se chtěli na cokoli zeptat, napište mi na vojtech.matocha@bcvsolutions.eu.