Maintain

Migrating to 8.0

Migration guidance for the email-governance, validation, conversion, DKIM, S/MIME, and parser changes introduced in Simple Java Mail 8.0.

§

Defaults and overrides move to send time

Impact: Code that inspects an Email immediately after building it may no longer see configured defaults.

In 7.x, the email builder copied global defaults into every new Email. Since 8.0, buildEmail() keeps only the values supplied by the caller. The Mailer completes the draft with its effective defaults and overrides before validation and MIME conversion.

// 7.x, with a default From address configured:
Email email = EmailBuilder.startingBlank().buildEmail();
email.getFromRecipient(); // the configured default was already present

// Current API: the draft contains only the values supplied here.
Email draft = EmailBuilder.startingBlank()
	.withRecipients(new RecipientBuilder()
		.withAddress("ops@example.com")
		.withType(TO)
		.build())
    .withSubject("Status")
	.withPlainText("All systems operational.")
    .buildEmail();
mailer.sendMail(draft); // the configured From is applied during sending

// Complete it explicitly only when you need to inspect the final values:
Email completed = EmailBuilder.copying(draft)
    .buildEmailCompletedWithDefaultsAndOverrides(mailer.getEmailGovernance());

The same draft can use different defaults when sent through different Mailers, and sending does not change the original draft. A raw ContentTransferEncoding may remain null; MIME generation then falls back to quoted-printable.

§

Inspect or validate the governed email

Impact: mailer.validate(email) validates the supplied draft, not the form completed for sending.

Email ready = mailer.getEmailGovernance()
    .produceEmailApplyingDefaultsAndOverrides(draft);

mailer.validate(ready);

The no-argument buildEmailCompletedWithDefaultsAndOverrides() applies property defaults. Pass mailer.getEmailGovernance() when you need that Mailer's programmatic defaults and overrides as well.

§

Per-email governance controls

Impact: Version 8 added override opt-outs and field-level controls. On the current API, configure them after choosing how the email starts.

// 7.x: only the pre-start defaults opt-out existed.
Email email = EmailBuilder.ignoringDefaults()
    .startingBlank()
    .buildEmail();

// Current API: start first, then configure governance for this email.
Email email = EmailBuilder.startingBlank()
    .ignoringDefaults()
    .ignoringOverrides()
    .buildEmail();

// Or keep governance but exclude selected fields.
Email keepOwnSubject = EmailBuilder.startingBlank()
    .withSubject("Do not replace this")
    .dontApplyDefaultValueFor(EmailProperty.REPLYTO_RECIPIENT)
    .dontApplyOverrideValueFor(EmailProperty.SUBJECT)
    .buildEmail();

These choices stay with the Email until a Mailer applies governance. See Mailer-level defaults and overrides for the complete merge rules.

§

Default S/MIME signing uses email defaults

Impact: The dedicated signByDefaultWithSmime(...) Mailer methods were removed.

Default signing now uses the same defaults Email as every other governed field. This keeps signing policy in one place and lets the Mailer reuse the prepared signing configuration.

// 7.x:
Mailer oldMailer = MailerBuilder
    .withSMTPServer("smtp.example.com", 587, username, password)
    .signByDefaultWithSmime(pkcs12Config)
    .buildMailer();

// Current API:
SmimeSigningConfig signing = SmimeSigningConfig.builder()
    .pkcs12Config(pkcs12Config)
    .build();

Email signingDefaults = EmailBuilder.startingBlank()
    .signWithSmime(signing)
    .buildEmailCompletedWithDefaultsAndOverrides(); // retain property defaults too

Mailer mailer = MailerBuilder
    .withSMTPServer("smtp.example.com", 587, username, password)
    .withEmailDefaults(signingDefaults)
    .buildMailer();

withEmailDefaults(...) replaces the property-derived defaults Email; it does not layer another Email on top. Put every default you want to retain on the supplied instance.

§

Governance and conversion APIs

Impact: This affects code that constructed EmailGovernance directly or passed a PKCS12 config to EmailConverter.emailToMimeMessage(...).

EmailGovernance became an interface. Configure the Mailer, then reuse its effective governance when a standalone conversion must follow the same defaults, overrides, and signing data.

// 7.x:
EmailGovernance governance = new EmailGovernance(
    validator, pkcs12Config, defaults, overrides, maxSize);
MimeMessage message = EmailConverter.emailToMimeMessage(
    email, session, pkcs12Config);

// Current API:
Mailer mailer = MailerBuilder
    .withSMTPServer("smtp.example.com", 587, username, password)
    .withEmailValidator(validator)
    .withEmailDefaults(defaults)
    .withEmailOverrides(overrides)
    .withMaximumEmailSize(maxSize)
    .buildMailer();

MimeMessage message = EmailConverter.emailToMimeMessage(
    email, session, mailer.getEmailGovernance());

The plain converter overloads now apply property defaults. The governance overload applies that Mailer's defaults and overrides, but it does not validate the Email or enforce its maximum size.

§

Lenient validation includes completeness

Impact: Code using disablingAllClientValidation(true) may proceed where 7.x threw for a missing sender or recipient.

Completeness, address, and CRLF-injection checks still run, but their findings are logged as warnings instead of blocking the send. The setting does not add missing fields; use it only when a custom or nonstandard send path can handle the resulting message.

§

Receipt fallbacks and custom mailers

Impact: Receipt addresses and the Email passed to a CustomMailer now include governance results.

A receipt request without an explicit address is resolved after defaults and overrides: first to Reply-To, then to From. A CustomMailer receives the same completed Email used for validation and MIME generation, so remove any workaround that reapplies Mailer defaults itself.

Email email = EmailBuilder.startingBlank()
	.withRecipients(new RecipientBuilder()
		.withAddress("ops@example.com")
		.withType(TO)
		.build())
	.withPlainText("All systems operational.")
    .withReturnReceiptTo()
    .withDispositionNotificationTo()
    .buildEmail();

// Mailer defaults or overrides may provide Reply-To or From before sending.
mailer.sendMail(email);
§

DKIM configuration

Impact: File-based DKIM builders need one method rename. Version 8 also introduced property-backed DKIM defaults.

// 7.x:
DkimConfig oldConfig = DkimConfig.builder()
    .dkimPrivateKeyData(keyFile)
    .dkimSigningDomain("example.com")
    .dkimSelector("mail")
    .build();

// Current API:
DkimConfig config = DkimConfig.builder()
    .dkimPrivateKeyPath(keyFile)
    .dkimSigningDomain("example.com")
    .dkimSelector("mail")
    .build();
simplejavamail.dkim.signing.private_key_file_or_data=file:dkim-private-key.der
simplejavamail.dkim.signing.selector=mail
simplejavamail.dkim.signing.signing_domain=example.com

The property defaults are loaded once per Mailer. See DKIM signing for current key formats, algorithms, canonicalization, and the narrow use of header exclusions.

§

Copying and Outlook parsing

Impact: Email copies keep more state, while malformed empty Outlook header names no longer stop conversion.

EmailBuilder.copying(email) now preserves content-transfer encoding and S/MIME signing and encryption state. Outlook .msg parsing skips null or empty header names while retaining valid headers and values.

§

Low-level API removals

Impact: Only code using Simple Java Mail internals or the low-level S/MIME helper needs changes here.

MailerHelper.signAndOrEncryptMessageWithSmime(...) lost its fourth Pkcs12Config argument; signing data now comes from the Email. The deprecated Email.internalSetId(...) and wasMergedWithSmimeSignedMessage() methods were removed.