Skip to content

Commit

Permalink
Merge pull request #43524 from mkouba/issue-43518
Browse files Browse the repository at this point in the history
Mailer: register default mailer beans correctly
  • Loading branch information
mkouba authored Sep 26, 2024
2 parents d9c37eb + 40020d0 commit 5accd78
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package io.quarkus.mailer.deployment;

import java.io.File;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
Expand All @@ -11,8 +13,12 @@
import jakarta.inject.Singleton;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassType;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.MethodInfo;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
Expand All @@ -35,6 +41,7 @@
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.mailer.MailTemplate;
import io.quarkus.mailer.MailTemplate.MailTemplateInstance;
import io.quarkus.mailer.Mailer;
import io.quarkus.mailer.MailerName;
import io.quarkus.mailer.MockMailbox;
Expand All @@ -56,6 +63,7 @@
public class MailerProcessor {

static final DotName MAIL_TEMPLATE = DotName.createSimple(MailTemplate.class.getName());
static final DotName MAIL_TEMPLATE_INSTANCE = DotName.createSimple(MailTemplateInstance.class.getName());

static final DotName MAILER_NAME = DotName.createSimple(MailerName.class);

Expand Down Expand Up @@ -105,7 +113,7 @@ MailersBuildItem generateMailerSupportBean(MailerRecorder recorder,
.anyMatch(i -> i.hasDefaultedQualifier() ||
// we inject a MailTemplate and it is not named
(MAIL_TEMPLATE.equals(i.getType().name()) && i.getRequiredQualifier(MAILER_NAME) == null))
|| !index.getIndex().getAnnotations(CheckedTemplate.class).isEmpty();
|| isTypeSafeMailTemplateFound(index.getIndex());

Set<String> namedMailers = mailerInjectionPoints.stream()
.map(i -> i.getRequiredQualifier(MAILER_NAME))
Expand All @@ -124,6 +132,35 @@ MailersBuildItem generateMailerSupportBean(MailerRecorder recorder,
return new MailersBuildItem(hasDefaultMailer, namedMailers);
}

private boolean isTypeSafeMailTemplateFound(IndexView index) {
// Find all occurences of @CheckedTemplate
Collection<AnnotationInstance> checkedTemplates = index.getAnnotations(CheckedTemplate.class);
for (AnnotationInstance annotation : checkedTemplates) {
if (annotation.target().kind() == Kind.CLASS) {
ClassInfo target = annotation.target().asClass();
if (target.isRecord()) {
// Java record that most likely implements MailTemplateInstance
return true;
}
for (MethodInfo method : target.methods()) {
if (Modifier.isStatic(method.flags()) && method.returnType().name().equals(MAIL_TEMPLATE_INSTANCE)) {
// Target declares a static method that returns MailTemplateInstance
return true;
}
}
}
}

Collection<ClassInfo> mailTemplateInstances = index.getAllKnownImplementors(MAIL_TEMPLATE_INSTANCE);
for (ClassInfo mailTemplateInstance : mailTemplateInstances) {
if (mailTemplateInstance.isRecord()) {
// Java record that implements MailTemplateInstance found
return true;
}
}
return false;
}

@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
void generateMailerBeans(MailerRecorder recorder,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.quarkus.mailer;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.Arc;
import io.quarkus.mailer.MailTemplate.MailTemplateInstance;
import io.quarkus.test.QuarkusUnitTest;
import io.vertx.ext.mail.MailMessage;

public class MailTemplateRecordNoInjectionTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addClasses(confirmation.class)
.addAsResource("mock-config.properties", "application.properties")
.addAsResource(new StringAsset(""
+ "<html>{name}</html>"), "templates/MailTemplateRecordNoInjectionTest/confirmation.html"));

@Test
public void testMailTemplateRecord() {
// Intentionally use programmatic lookup to obtain the MockMailbox
MockMailbox mockMailbox = Arc.container().instance(MockMailbox.class).get();
new confirmation("Ondrej").to("[email protected]").from("[email protected]").subject("test mailer")
.sendAndAwait();
assertEquals(1, mockMailbox.getMailMessagesSentTo("[email protected]").size());
MailMessage message = mockMailbox.getMailMessagesSentTo("[email protected]").get(0);
assertEquals("[email protected]", message.getFrom());
assertEquals("<html>Ondrej</html>", message.getHtml());
}

record confirmation(String name) implements MailTemplateInstance {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import io.quarkus.mailer.reactive.ReactiveMailer;
import io.quarkus.qute.TemplateInstance;
import io.smallrye.common.annotation.CheckReturnValue;
import io.smallrye.mutiny.Uni;

/**
Expand Down Expand Up @@ -117,10 +118,20 @@ default MailTemplateInstance setAttribute(String key, Object value) {
* @return a {@link Uni} indicating when the mails have been sent
* @see ReactiveMailer#send(Mail...)
*/
@CheckReturnValue
default Uni<Void> send() {
throw new UnsupportedOperationException();
}

/**
* Sends all e-mail definitions and blocks the current thread while waiting for the result.
*
* @see #send()
*/
default void sendAndAwait() {
send().await().indefinitely();
}

/**
* The returned instance does not represent a specific template but a delegating template.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import java.lang.reflect.Field;

import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.Default;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.spi.Annotated;
import jakarta.enterprise.inject.spi.AnnotatedParameter;
import jakarta.enterprise.inject.spi.InjectionPoint;
import jakarta.inject.Singleton;
Expand All @@ -31,7 +31,7 @@ public class MailTemplateProducer {
Instance<Template> template;

@Produces
MailTemplate getDefault(InjectionPoint injectionPoint) {
MailTemplate getDefault(InjectionPoint injectionPoint, @Any Instance<ReactiveMailer> reactiveMailer) {

final String name;
if (injectionPoint.getMember() instanceof Field) {
Expand All @@ -50,7 +50,7 @@ MailTemplate getDefault(InjectionPoint injectionPoint) {
return new MailTemplate() {
@Override
public MailTemplateInstance instance() {
return new MailTemplateInstanceImpl(getReactiveMailer(injectionPoint),
return new MailTemplateInstanceImpl(getReactiveMailer(injectionPoint, reactiveMailer),
template.select(new LocationLiteral(name)).get().instance());
}

Expand All @@ -59,7 +59,7 @@ public MailTemplateInstance instance() {

@Location("ignored")
@Produces
MailTemplate get(InjectionPoint injectionPoint) {
MailTemplate get(InjectionPoint injectionPoint, @Any Instance<ReactiveMailer> reactiveMailer) {
Location path = null;
for (Annotation qualifier : injectionPoint.getQualifiers()) {
if (qualifier.annotationType().equals(Location.class)) {
Expand All @@ -74,21 +74,18 @@ MailTemplate get(InjectionPoint injectionPoint) {
return new MailTemplate() {
@Override
public MailTemplateInstance instance() {
return new MailTemplateInstanceImpl(getReactiveMailer(injectionPoint),
return new MailTemplateInstanceImpl(getReactiveMailer(injectionPoint, reactiveMailer),
template.select(new LocationLiteral(name)).get().instance());
}
};
}

public static ReactiveMailer getReactiveMailer(InjectionPoint injectionPoint) {
Annotated annotated = injectionPoint.getAnnotated();
MailTemplateMailerName mailerName = annotated.getAnnotation(MailTemplateMailerName.class);

static ReactiveMailer getReactiveMailer(InjectionPoint injectionPoint, Instance<ReactiveMailer> reactiveMailer) {
MailTemplateMailerName mailerName = injectionPoint.getAnnotated().getAnnotation(MailTemplateMailerName.class);
if (mailerName != null) {
return Arc.container().instance(ReactiveMailer.class, MailerName.Literal.of(mailerName.value())).get();
return reactiveMailer.select(MailerName.Literal.of(mailerName.value())).get();
}

return Arc.container().instance(ReactiveMailer.class).get();
return reactiveMailer.select(Default.Literal.INSTANCE).get();
}

/**
Expand Down

0 comments on commit 5accd78

Please sign in to comment.