OghamJavaMailClasspathConsistencyCheckConfiguration.java
package fr.sii.ogham.spring.email;
import fr.sii.ogham.core.util.IOUtils;
import fr.sii.ogham.email.sender.impl.JavaMailSender;
import fr.sii.ogham.email.sender.impl.JavaxMailSender;
import fr.sii.ogham.spring.email.condition.JavaMailClasspathConsistencyCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* There can be a mess in the classpath due to Spring Boot dependency management
* that forces versions:
* <ul>
* <li>Ogham uses the dependency {@code org.eclipse.angus:angus-mail:2.0.1+}, which imports:
* <ul>
* <li>{@code jakarta.mail:jakarta.mail-api:2.1.0+}</li>
* <li>{@code jakarta.activation:jakarta.activation-api:2.1.1+}</li>
* <li>{@code org.eclipse.angus:angus-activation:2.0.0+}</li>
* </ul>
* </li>
* <li>Spring Boot 2 dependency management has these rules:
* <ul>
* <li>{@code jakarta.mail:jakarta.mail-api:1.6.7-}</li>
* <li>{@code jakarta.activation:jakarta.activation-api:1.2.2-}</li>
* </ul>
* </li>
* </ul>
* <p>
* But, there is a mess in the packages. The dependency {@code jakarta.mail:jakarta.mail-api:1.6.7-}
* declares classes in package {@code javax.mail} while {@code jakarta.mail:jakarta.mail-api:1.6.7+}
* declares classes in package {@code jakarta.mail}.
* <p>
* So, when using Ogham with Spring Boot 2, we try:
* <ul>
* <li>either to use Jakarta mail using {@code org.eclipse.angus:angus-mail:2.0.1+} but it fails
* because it doesn't find {@code jakarta.activation.DataHandler} (since in the classpath the
* {@code jakarta.activation:jakarta.activation-api} is in version {@code 1.2.2-} which defines
* {@code javax.activation.DataHandler}.
* </li>
* <li>or to use Javax mail using {@code jakarta.mail:jakarta.mail-api:1.6.7-} but it may fail
* because Ogham doesn't bring Javax mail implementation dependencies to avoid classpath
* mess and issues at runtime. So when trying to send en email using Javax mail, there is no implementation
* available. But it is even worse than no implementation available. In the sources of
* {@code jakarta.mail:jakarta.mail-api:1.6.7-}, there is a direct link to the Sun implementation
* which may not be present in the classpath (at least {@code com.sun.mail.util.MailLogger}).
* So, instead of indicating that there is no Javax mail implementation present, it
* fails with {@code java.lang.NoClassDefFoundError: com/sun/mail/util/MailLogger} which
* is not really developer friendly and hard to understand why it happens.
* </li>
* </ul>
* <p>
* Therefore, this class is used to help the developer to understand what to do to fix this.
*/
@Configuration
// Only check classpath consistency if ogham-email-javamail-jakarta and/or ogham-email-javamail-javax are present.
// If none is present, we don't care about classpath inconsistencies since we won't send mail with Ogham Java mail.
@Conditional({
OghamJavaMailClasspathConsistencyCheckConfiguration.JakartaOrJavaxSenderClassPresent.class,
JavaMailClasspathConsistencyCondition.class
})
@ConditionalOnProperty(value = "ogham.email.javamail.check-classpath-consistency", havingValue = "true", matchIfMissing = true)
@AutoConfigureBefore({OghamJavaMailConfiguration.class, OghamJavaxMailConfiguration.class})
@AutoConfigureAfter(MailSenderAutoConfiguration.class)
public class OghamJavaMailClasspathConsistencyCheckConfiguration {
private static Logger LOG = LoggerFactory.getLogger(OghamJavaMailClasspathConsistencyCheckConfiguration.class);
@PostConstruct
public void explainPossibleIssueIfUsingJavaMail() {
try {
LOG.warn("\n{}", IOUtils.toString(getClass().getResourceAsStream("/javamail-classpath-clash-explanation.adoc"), UTF_8));
} catch (IOException e) {
LOG.warn("/!\\ You may experience classpath issues while sending emails with Ogham using Java Mail and Spring Boot >= 2.2.x < 3.0.x.\n" +
"\n" +
"Failed to load explanations. Please visit Ogham website for more information.");
}
}
public static class JakartaOrJavaxSenderClassPresent extends AnyNestedCondition {
public JakartaOrJavaxSenderClassPresent() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnClass({JavaMailSender.class})
static class JavaMailSenderPresent {
}
@ConditionalOnClass({JavaxMailSender.class})
static class JavaxMailSenderPresent {
}
}
}