Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mailer: register default mailer beans correctly #43524

Merged
merged 2 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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("quarkus-reactive@quarkus.io").from("from-record@quarkus.io").subject("test mailer")
.sendAndAwait();
assertEquals(1, mockMailbox.getMailMessagesSentTo("quarkus-reactive@quarkus.io").size());
MailMessage message = mockMailbox.getMailMessagesSentTo("quarkus-reactive@quarkus.io").get(0);
assertEquals("from-record@quarkus.io", 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