Memra

Sending: Session, MimeMessage, Transport

◈ 5 cards

Install the API, build a Session from Properties plus an Authenticator, fill a MimeMessage with TO, CC and BCC parsed from comma lists, and hand the result to Transport.send.

It is a dependency, not part of the JDK

java.net ships inside the JDK. Mail does not. You download it, put it on the classpath, and write down exactly how you did it — that write-up is a large share of the assignment's research marks.

There are two generations of one API, and they are not interchangeable:

generationpackagethe two jars
JavaMail 1.xjavax.mailjavax.mail-api + a com.sun.mail implementation
Jakarta Mail 2.xjakarta.mailjakarta.mail-api + an Angus Mail implementation

The rename happened when the Java EE specifications moved to the Eclipse Foundation, which could not keep the javax namespace. Class and method names are otherwise unchanged, so a tutorial written for one translates to the other by editing its imports — but both jars must come from the same generation, for the reason in the warning below.

You also need Jakarta Activation, which supplies DataHandler and FileDataSource. The implementation jar normally pulls it in; you find out in the next lesson, when attachments need it.

Session: configuration, not a connection

Session sounds like a connection. It is not. It is a Properties bundle plus an optional Authenticator, and building one touches the network zero times:

Properties props = new Properties();
props.put("mail.smtp.host", "smtp.example.net");
props.put("mail.smtp.port", "587");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");

mail.smtp.auth tells the transport to log in once it sees the server advertise AUTH. mail.smtp.starttls.enable tells it to upgrade the plaintext connection to TLS after EHLO, which is what the submission port 587 expects. Port 465 wants TLS from the very first byte instead — that is mail.smtp.ssl.enable, and you set one or the other, never both.

Credentials arrive through an Authenticator subclass whose getPasswordAuthentication() the transport calls back when it needs them, and you pass it to Session.getInstance(props, auth). Use a provider-issued app password, never your account password: Gmail and Outlook refuse plain account passwords for SMTP outright, and an app password is a per-program credential you can revoke without changing your login. Read it from the input file or the environment; never a string literal in source you submit.

MimeMessage: the message you are building

new MimeMessage(session) gives you an empty RFC 5322 message. Filling it is setFrom, setRecipients, setSubject, setText, and optionally setSentDate.

The recipient setter is where the assignment trips people. There are two of them and they differ by one letter. setRecipient (singular) takes exactly one Address. setRecipients (plural) takes an Address[]. The input file hands you CC: lee@example.net, ada@example.net — one comma-separated string — and the tool for that is InternetAddress.parse(String), which splits it, validates each address and returns the array the plural setter wants, throwing AddressException on anything malformed.

Message.RecipientType.BCC is a real recipient type alongside TO and CC — and notice what it is not. It is not a header. Transport.send asks the message for getAllRecipients(), which is TO plus CC plus BCC, and issues one RCPT TO per address; the writer then omits the Bcc: header from the bytes that go into DATA. That is the previous lesson's envelope/header split, implemented for you.

Worked example — reading the whole message from a file

The assignment specifies the entire message, credentials included, in one text file named on the command line:

Server: smtp.example.net
User: dana@example.org
Password: abcd-efgh-ijkl-mnop
To: sam@example.net
CC: lee@example.net, ada@example.net
BCC: quiet@example.net
Subject: lab notes
Body: Numbers attached tomorrow.
Second line of the body.

Nine lines, and one of them changes the shape of your parser. Everything above Body: is a key: value pair. Body: is the last key, and everything after it is body text — including later lines that happen to contain a colon, and including blank lines. The body ends at EOF, not at a terminator, so the loop must stop splitting and start appending the moment it sees that key:

String line;
StringBuilder body = new StringBuilder();
boolean inBody = false;
while ((line = in.readLine()) != null) {
    if (inBody) { body.append(line).append("\n"); continue; }
    int colon = line.indexOf(':');
    if (colon < 0) continue;
    String key = line.substring(0, colon).trim().toUpperCase();
    String value = line.substring(colon + 1).trim();
    if (key.equals("BODY")) { inBody = true; body.append(value).append("\n"); }
    else fields.put(key, value);
}

A parser that splits every line on the first colon instead will silently truncate any body line containing one, and a parser that reads the body eagerly will lose the blank lines that make it readable. After that the send is mechanical: properties, session, message, three recipient sets, subject, text, Transport.send.

parse the header blockgetInstance(props, auth)new MimeMessage(session)setSubject, setTextInternetAddress.parseinput filethe whole message, one filePropertieshost, port, auth, TLSSessionconfig plus AuthenticatorMimeMessagefrom, subject, textrecipientsTO, CC, BCC arraysTransport.sendfirst network callBCC becomes extra RCPT TOcommands, never a header.
Only the last box touches the network. Everything before it is local object construction, which is why a typo in mail.smtp.host does not fail until Transport.send.
propertyvalue hereeffectmail.smtp.hostsmtp.example.netwhere to connectmail.smtp.port587the submission portmail.smtp.authtruesend AUTH after EHLOmail.smtp.starttls.enabletrueupgrade to TLS in sessionmail.smtp.ssl.enablefor port 465TLS from the first byteProperty values are Strings, even "true".
Set starttls.enable for port 587 or ssl.enable for port 465 — one or the other. Setting both, or neither, is the second most common reason a first send never connects.
NORMAL ~/memra/learn/comp-348/javamail-sending-mimemessage utf-8 LF