Skip to content

Commit

Permalink
fix(citrus-mail): jakarta mail implementation
Browse files Browse the repository at this point in the history
The implemented workaround allows jakarta-mail to find the corresponding classes when opening a new session.
The reported without the workaround error is:

```shell
java.lang.IllegalStateException: Not provider of jakarta.mail.util.StreamProvider was found
    at org.citrusframework.mail.server.MailServerTest.testMultipartMessageSplitting(MailServerTest.java:378)
```

Report in PR: citrusframework/citrus-simulator#163 (comment).
  • Loading branch information
bbortt committed Jul 30, 2023
1 parent 957b791 commit 7339378
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 305 deletions.
4 changes: 0 additions & 4 deletions connectors/citrus-docker/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,6 @@
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>

<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
Expand Down
17 changes: 0 additions & 17 deletions endpoints/citrus-mail/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,10 @@
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>

<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>jakarta.mail</artifactId>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
</dependency>
<dependency>
<groupId>com.sun.activation</groupId>
<artifactId>jakarta.activation</artifactId>
</dependency>

<!-- Test scoped dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
* @since 1.4
*/
public class MailClient extends AbstractEndpoint implements Producer, InitializingPhase {
/** Logger */

private static Logger log = LoggerFactory.getLogger(MailClient.class);

/**
Expand All @@ -51,7 +51,6 @@ public MailClient() {

/**
* Default constructor using endpoint configuration.
* @param endpointConfiguration
*/
public MailClient(MailEndpointConfiguration endpointConfiguration) {
super(endpointConfiguration);
Expand All @@ -69,7 +68,8 @@ public void send(Message message, TestContext context) {
}

MimeMailMessage mimeMessage = getEndpointConfiguration().getMessageConverter().convertOutbound(message, getEndpointConfiguration(), context);
getEndpointConfiguration().getJavaMailSender().send(mimeMessage.getMimeMessage());

withModifiedClassloader(() -> getEndpointConfiguration().getJavaMailSender().send(mimeMessage.getMimeMessage()));

ByteArrayOutputStream bos = new ByteArrayOutputStream();
Message mailMessage;
Expand All @@ -91,6 +91,28 @@ public void send(Message message, TestContext context) {
log.info(String.format("Mail message was sent to host: '%s://%s:%s'", getEndpointConfiguration().getProtocol(), getEndpointConfiguration().getHost(), getEndpointConfiguration().getPort()));
}

/**
* The implemented workaround allows jakarta-mail to find the corresponding classes when opening a new session. The
* reported without the workaround error is:
* <pre>
* java.lang.IllegalStateException: Not provider of jakarta.mail.util.StreamProvider was found
* at org.citrusframework.mail.server.MailServerTest.testMultipartMessageSplitting(MailServerTest.java:378)
* </pre>
* <p>
* {@see https://github.com/jakartaee/mail-api/issues/665}
*/
private void withModifiedClassloader(Runnable runnable) {
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader(getClass().getClassLoader());

try {
runnable.run();
} finally {
thread.setContextClassLoader(contextClassLoader);
}
}

/**
* Creates a message producer for this endpoint for sending messages
* to this endpoint.
Expand All @@ -103,8 +125,6 @@ public Producer createProducer() {
/**
* Creates a message consumer for this endpoint. Consumer receives
* messages on this endpoint.
*
* @return
*/
@Override
public Consumer createConsumer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@

package org.citrusframework.mail.server;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import java.util.stream.Collectors;
import javax.xml.transform.Source;

import com.icegreen.greenmail.mail.MailAddress;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup;
import jakarta.mail.AuthenticationFailedException;
import jakarta.mail.Session;
import jakarta.mail.internet.MimeMessage;
import org.citrusframework.exceptions.CitrusRuntimeException;
import org.citrusframework.mail.client.MailEndpointConfiguration;
import org.citrusframework.mail.message.CitrusMailMessageHeaders;
Expand All @@ -36,23 +36,23 @@
import org.citrusframework.mail.model.MailResponse;
import org.citrusframework.message.Message;
import org.citrusframework.server.AbstractServer;
import com.icegreen.greenmail.mail.MailAddress;
import com.icegreen.greenmail.user.GreenMailUser;
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetup;
import jakarta.mail.AuthenticationFailedException;
import jakarta.mail.Session;
import jakarta.mail.internet.MimeMessage;
import org.springframework.mail.javamail.MimeMailMessage;

import javax.xml.transform.Source;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Stack;
import java.util.stream.Collectors;

/**
* Mail server implementation starts new SMTP server instance and listens for incoming mail messages. Incoming mail messages
* are converted to XML representation and forwarded to some message endpoint adapter (e.g. forwarding mail content to
* a message channel).
*
* By default incoming messages are accepted automatically. When auto accept is disabled the endpoint adapter is invoked with
* <p>
* By default, incoming messages are accepted automatically. When auto accept is disabled the endpoint adapter is invoked with
* accept request and test case has to decide accept outcome in response.
*
* <p>
* In case of incoming multipart mail messages the server is able to split the body parts into separate XML messages
* handled by the endpoint adapter.
*
Expand Down Expand Up @@ -105,6 +105,7 @@ protected void startup() {
deliver(msg.getMessage());
return user;
});

smtpServer.start();
}

Expand Down Expand Up @@ -140,8 +141,8 @@ public boolean accept(String from, List<MailAddress> recipients) {
return acceptResponse.isAccept();
}

public void deliver(MimeMessage msg) {
MimeMailMessage mimeMailMessage = new MimeMailMessage(msg);
public void deliver(MimeMessage mimeMessage) {
MimeMailMessage mimeMailMessage = new MimeMailMessage(mimeMessage);
MailMessage request = messageConverter.convertInbound(mimeMailMessage, getEndpointConfiguration(), null);
Message response = invokeEndpointAdapter(request);

Expand All @@ -161,7 +162,6 @@ public void deliver(MimeMessage msg) {

/**
* Invokes the endpoint adapter with constructed mail message and headers.
* @param mail
*/
protected Message invokeEndpointAdapter(MailMessage mail) {
if (splitMultipart) {
Expand All @@ -174,10 +174,7 @@ protected Message invokeEndpointAdapter(MailMessage mail) {
/**
* Split mail message into several messages. Each body and each attachment results in separate message
* invoked on endpoint adapter. Mail message response if any should be sent only once within test case.
* However latest mail response sent by test case is returned, others are ignored.
*
* @param bodyPart
* @param messageHeaders
* However, latest mail response sent by test case is returned, others are ignored.
*/
private Message split(BodyPart bodyPart, Map<String, Object> messageHeaders) {
MailMessage mailRequest = createMailMessage(messageHeaders, bodyPart.getContent(), bodyPart.getContentType());
Expand Down Expand Up @@ -209,10 +206,6 @@ private void fillStack(Message message, Stack<Message> responseStack) {

/**
* Creates a new mail message model object from message headers.
* @param messageHeaders
* @param body
* @param contentType
* @return
*/
protected MailMessage createMailMessage(Map<String, Object> messageHeaders, String body, String contentType) {
return MailMessage.request(messageHeaders)
Expand All @@ -236,123 +229,126 @@ public MailEndpointConfiguration getEndpointConfiguration() {
}

/**
* Return new mail session if not already created before.
* @return
* Return a new mail session if not already created before.
* <p>
* The implemented workaround allows jakarta-mail to find the corresponding classes when opening a new session. The
* reported without the workaround error is:
* <pre>
* java.lang.IllegalStateException: Not provider of jakarta.mail.util.StreamProvider was found
* at org.citrusframework.mail.server.MailServerTest.testMultipartMessageSplitting(MailServerTest.java:378)
* </pre>
* <p>
* {@see https://github.com/jakartaee/mail-api/issues/665}
*/
public synchronized Session getSession() {
if (this.mailSession == null) {
this.mailSession = Session.getInstance(this.javaMailProperties);
Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader(getClass().getClassLoader());

try {
this.mailSession = Session.getInstance(this.javaMailProperties);
} finally {
thread.setContextClassLoader(contextClassLoader);
}
}

return this.mailSession;
}

/**
* Is auto accept enabled.
* @return
*/
public boolean isAutoAccept() {
return autoAccept;
}

/**
* Enable/disable auto accept feature.
* @param autoAccept
*/
public void setAutoAccept(boolean autoAccept) {
this.autoAccept = autoAccept;
}

/**
* Gets the mail message marshaller.
* @return
*/
public MailMarshaller getMarshaller() {
return marshaller;
}

/**
* Sets the mail message marshaller.
* @param marshaller
*/
public void setMarshaller(MailMarshaller marshaller) {
this.marshaller = marshaller;
}

/**
* Gets the Java mail properties.
* @return
*/
public Properties getJavaMailProperties() {
return javaMailProperties;
}

/**
* Sets the Java mail properties.
* @param javaMailProperties
*/
public void setJavaMailProperties(Properties javaMailProperties) {
this.javaMailProperties = javaMailProperties;
}

/**
* Gets the server port.
* @return
*/
public int getPort() {
return port;
}

/**
* Sets the server port.
* @param port
*/
public void setPort(int port) {
this.port = port;
}

/**
* Gets the smtp server instance.
* @return
*/
public GreenMail getSmtpServer() {
return smtpServer;
}

/**
* Sets the smtp server instance.
* @param smtpServer
*/
public void setSmtpServer(GreenMail smtpServer) {
this.smtpServer = smtpServer;
}

/**
* Gets the split multipart message.
* @return
*/
public boolean isSplitMultipart() {
return splitMultipart;
}

/**
* Sets the split multipart message.
* @param splitMultipart
*/
public void setSplitMultipart(boolean splitMultipart) {
this.splitMultipart = splitMultipart;
}

/**
* Gets the message converter.
* @return
*/
public MailMessageConverter getMessageConverter() {
return messageConverter;
}

/**
* Sets the message converter.
* @param messageConverter
*/
public void setMessageConverter(MailMessageConverter messageConverter) {
this.messageConverter = messageConverter;
Expand Down
Loading

0 comments on commit 7339378

Please sign in to comment.