Setting mail.smtp.connectiontimeout longens requests when working with office 365 - spring-boot

We are sending mails in an email service with org.springframework.mail.javamail.JavaMailSender via an office 365 account and SMTP and set the following parameters in application.yml:
spring:
mail:
host: ${EMAIL_HOST:smtp.office365.com}
port: ${EMAIL_PORT:587}
username: ${EMAIL_USERNAME}
password: ${EMAIL_PASSWORD}
properties:
mail:
smtp:
auth: true
connectiontimeout: 5000
timeout: 5000
writetimeout: 5000
starttls:
enable: true
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
The strange thing is: if we set the connectiontimeout to 5s, the service gets a response after 5s. If we set it to 20s, the o365 responds after 20s.
My expectation is that <connectiontimeout> is the maximum amount of time, that the sending may take and not the actual time.
Funny thing is that when setting another provider than office365, connectiontimeout works as expected.
Does anyone have this issue as well and maybe know how to solve that?
Our sender service:
#PostMapping
#ResponseStatus(HttpStatus.ACCEPTED)
public void sendMail(#RequestHeader(name = "X-API-KEY", required = true) String requestApiKey, #Valid #RequestBody EmailSendRequest email, HttpServletResponse response) {
if(!apiKey.equals(requestApiKey)){
LOGGER.error("Unauthorized api key" + requestApiKey);
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
try {
LOGGER.info("Received request to send mail Subject=[{}] To=[{}] From=[{}]", email.getSubject(), email.getTo(), email.getFrom());
MimeMessage message = mailSender.createMimeMessage();
message.setFrom(new InternetAddress(email.getFrom().getEmail()));
message.addRecipients(Message.RecipientType.TO, toAddressArray(email.getTo()));
message.addRecipients(Message.RecipientType.CC, toAddressArray(email.getCc()));
message.addRecipients(Message.RecipientType.BCC, toAddressArray(email.getBcc()));
message.setSubject(email.getSubject());
message.setSentDate(new Date());
Multipart multipart = new MimeMultipart();
MimeBodyPart messageText = new MimeBodyPart();
messageText.setContent(email.getContent().getValue(),
email.getContent().getType() == null ? DEFAULT_CONTENT_MIMETYPE : email.getContent().getType());
multipart.addBodyPart(messageText);
addAttachments(multipart, email.getAttachments());
message.setContent(multipart);
if(message.getRecipients(Message.RecipientType.TO) != null ||
message.getRecipients(Message.RecipientType.CC) != null ||
message.getRecipients(Message.RecipientType.BCC) != null)
{
mailSender.send(message);
}
else {
LOGGER.warn("Email not send! No recipients or all ignored.");
response.setHeader("X-Ignored","true");
}
LOGGER.info("Mail Subject=[{}] To=[{}}] From=[{}] successfully sent.",email.getSubject(),email.getTo(),email.getFrom());
} catch (MessagingException e) {
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]:", email.getSubject(), email.getTo(), email.getFrom(), e);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
} catch (MailSendException mailSendException) {
Exception[] exceptions = mailSendException.getMessageExceptions();
for (Exception e : exceptions){
if (e instanceof SMTPSendFailedException && (((SMTPSendFailedException)e).getReturnCode() == 554)){
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]: This sender mail address is not allowed.", email.getSubject(), email.getTo(), email.getFrom());
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
}
}
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]:", email.getSubject(), email.getTo(), email.getFrom(), mailSendException);
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR);
} catch (MailAuthenticationException e) {
LOGGER.error("Error sending mail Subject=[{}] To=[{}] From=[{}]: Wrong SMTP login credentials provided. \nMSG:{}", email.getSubject(), email.getTo(), email.getFrom(),e.getMessage());
throw new ResponseStatusException(HttpStatus.NETWORK_AUTHENTICATION_REQUIRED);
}
}

It seems, that the SocketFactory was responsible for this behaviour. Removing the following lines from application.yml makes the application work as expected:
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory

Related

Orderly message send back %RETRY%CONSUMERGROUP

I have a question about the method of RocektMQ ConsumeMessageOrderlyService.sendMessageBack, The method comment:max reconsume times exceeded then send to dead letter queue。
But the message was actually sent to %RETYE%ConsumerGroup, this means that the message will then be consumed by the same group of consumers,and I have tried, the message was indeed sent first to SCHEDULE_TOPIC_XXXX, then delivered to %RETRY%ConsumerGroup, and never sent to Dead-Letter Queue.
this is the source code of ConsumeMessageOrderlyService.sendMessageBack:
public boolean sendMessageBack(final MessageExt msg) {
try {
// max reconsume times exceeded then send to dead letter queue.
Message newMsg = new Message(MixAll.getRetryTopic(this.defaultMQPushConsumer.getConsumerGroup()), msg.getBody());
MessageAccessor.setProperties(newMsg, msg.getProperties());
String originMsgId = MessageAccessor.getOriginMessageId(msg);
MessageAccessor.setOriginMessageId(newMsg, UtilAll.isBlank(originMsgId) ? msg.getMsgId() : originMsgId);
newMsg.setFlag(msg.getFlag());
MessageAccessor.putProperty(newMsg, MessageConst.PROPERTY_RETRY_TOPIC, msg.getTopic());
MessageAccessor.setReconsumeTime(newMsg, String.valueOf(msg.getReconsumeTimes()));
MessageAccessor.setMaxReconsumeTimes(newMsg, String.valueOf(getMaxReconsumeTimes()));
MessageAccessor.clearProperty(newMsg, MessageConst.PROPERTY_TRANSACTION_PREPARED);
newMsg.setDelayTimeLevel(3 + msg.getReconsumeTimes());
this.defaultMQPushConsumer.getDefaultMQPushConsumerImpl().getmQClientFactory().getDefaultMQProducer().send(newMsg);
return true;
} catch (Exception e) {
log.error("sendMessageBack exception, group: " + this.consumerGroup + " msg: " + msg.toString(), e);
}
return false;
}
I am confused about it.

How to use RemoteFileTemplate<SmbFile> in Spring integration?

I've got a Spring #Component where a SmbSessionFactory is injected to create a RemoteFileTemplate<SmbFile>. When my application runs, this piece of code is called multiple times:
public void process(Message myMessage, String filename) {
StopWatch stopWatch = StopWatch.createStarted();
byte[] bytes = marshallMessage(myMessage);
String destination = smbConfig.getDir() + filename + ".xml";
if (log.isDebugEnabled()) {
log.debug("Result: {}", new String(bytes));
}
Optional<IOException> optionalEx =
remoteFileTemplate.execute(
session -> {
try (InputStream inputStream = new ByteArrayInputStream(bytes)) {
session.write(inputStream, destination);
} catch (IOException e1) {
return Optional.of(e1);
}
return Optional.empty();
});
log.info("processed Message in {}", stopWatch.formatTime());
optionalEx.ifPresent(
ioe -> {
throw new UncheckedIOException(ioe);
});
}
this works (i.e. the file is written) and all is fine. Except that I see warnings appearing in my log:
DEBUG my.package.MyClass Result: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>....
INFO org.springframework.integration.smb.session.SmbSessionFactory SMB share init: XXX
WARN jcifs.smb.SmbResourceLocatorImpl Path consumed out of range 15
WARN jcifs.smb.SmbTreeImpl Disconnected tree while still in use SmbTree[share=XXX,service=null,tid=1,inDfs=true,inDomainDfs=true,connectionState=3,usage=2]
INFO org.springframework.integration.smb.session.SmbSession Successfully wrote remote file [path\to\myfile.xml].
WARN jcifs.smb.SmbSessionImpl Logging off session while still in use SmbSession[credentials=XXX,targetHost=XXX,targetDomain=XXX,uid=0,connectionState=3,usage=1]:[SmbTree[share=XXX,service=null,tid=1,inDfs=false,inDomainDfs=false,connectionState=0,usage=1], SmbTree[share=XXX,service=null,tid=5,inDfs=false,inDomainDfs=false,connectionState=2,usage=0]]
jcifs.smb.SmbTransportImpl Disconnecting transport while still in use Transport746[XXX/999.999.999.999:445,state=5,signingEnforced=false,usage=1]: [SmbSession[credentials=XXX,targetHost=XXX,targetDomain=XXX,uid=0,connectionState=2,usage=1], SmbSession[credentials=XXX,targetHost=XXX,targetDomain=null,uid=0,connectionState=2,usage=0]]
INFO my.package.MyClass processed Message in 00:00:00.268
The process method is called from a Rest method, which does little else.
What am I doing wrong here?

Azure - EventHub Consumer Binding With AMQP Web Sockets

I'm trying to set up a very basic proof of concept to link a Spring application to an Azure Event Hub. I keep getting this log in my startup:
[2021-11-17 16:49:18.343 ERROR 25816 --- [ main] o.s.cloud.stream.binding.BindingService : Failed to create consumer binding; retrying in 30 seconds
...
Caused by: java.lang.IllegalArgumentException: Cannot use a proxy when TransportType is not AMQP Web Sockets.
at com.azure.messaging.eventhubs.EventHubClientBuilder.getConnectionOptions(EventHubClientBuilder.java:764) ~[azure-messaging-eventhubs-5.10.2.jar:5.10.2]
at com.azure.messaging.eventhubs.EventHubClientBuilder.buildConnectionProcessor(EventHubClientBuilder.java:705) ~[azure-messaging-eventhubs-5.10.2.jar:5.10.2]
at com.azure.messaging.eventhubs.EventHubClientBuilder.buildAsyncClient(EventHubClientBuilder.java:638) ~[azure-messaging-eventhubs-5.10.2.jar:5.10.2]
at com.azure.messaging.eventhubs.EventProcessorClient.<init>(EventProcessorClient.java:89) ~[azure-messaging-eventhubs-5.10.2.jar:5.10.2]
at com.azure.messaging.eventhubs.EventProcessorClientBuilder.buildEventProcessorClient(EventProcessorClientBuilder.java:585) ~[azure-messaging-eventhubs-5.10.2.jar:5.10.2]
...
This is despite the fact that I defined beans for EventHubProducerAsyncClient, EventHubConsumerAsyncClient, and EventProcessorClient that explicitly set the transport type to AMQP_WEB_SOCKETS. See here:
Producer:
#Bean
public EventHubProducerAsyncClient eventHubProducerAsyncClient() {
return new EventHubClientBuilder()
.connectionString(connectionString)
.proxyOptions(new ProxyOptions(
ProxyAuthenticationType.NONE,
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("my proxy address", 8080)),
null, null
))
.transportType(AmqpTransportType.AMQP_WEB_SOCKETS)
.buildAsyncProducerClient();
}
Consumer:
#Bean
public EventHubConsumerAsyncClient eventHubConsumerAsyncClient() {
return new EventHubClientBuilder()
.connectionString(connectionString)
.proxyOptions(new ProxyOptions(
ProxyAuthenticationType.NONE,
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("my proxy address", 8080)),
null, null
))
.transportType(AmqpTransportType.AMQP_WEB_SOCKETS)
.consumerGroup(DEFAULT_CONSUMER_GROUP_NAME)
.buildAsyncConsumerClient();
}
EventProcessor:
private static void onEvent(EventContext eventContext) {
PartitionContext partition = eventContext.getPartitionContext();
LOGGER.info("Received events from partition: " + partition.getPartitionId());
EventData event = eventContext.getEventData();
LOGGER.info("Sequence number: " + event.getSequenceNumber());
LOGGER.info("Contents: " + new String(event.getBody(), StandardCharsets.UTF_8));
}
#Bean
public EventProcessorClient eventProcessorClient() {
BlobContainerAsyncClient blobClient = new BlobContainerClientBuilder()
.connectionString(storageConnectionString)
.containerName(storageContainerName)
.buildAsyncClient();
return new EventProcessorClientBuilder()
.connectionString(connectionString)
.consumerGroup(DEFAULT_CONSUMER_GROUP_NAME)
.checkpointStore(new BlobCheckpointStore(blobClient))
.processEvent(eventContext -> onEvent(eventContext))
.processError(context -> {
LOGGER.error("Error occurred on partition: %s. Error: %s%n",
context.getPartitionContext().getPartitionId(), context.getThrowable());
})
.processPartitionInitialization(initializationContext -> {
LOGGER.info("Started receiving on partition: %s%n",
initializationContext.getPartitionContext().getPartitionId());
})
.processPartitionClose(closeContext -> {
LOGGER.info("Stopped receiving on partition: %s. Reason: %s%n",
closeContext.getPartitionContext().getPartitionId(),
closeContext.getCloseReason());
})
.proxyOptions(new ProxyOptions(
ProxyAuthenticationType.NONE,
new Proxy(Proxy.Type.HTTP, new InetSocketAddress("my proxy address", 8080)),
null, null
))
.transportType(AmqpTransportType.AMQP_WEB_SOCKETS)
.buildEventProcessorClient();
}
Am I missing something obvious here? I don't understand how it can complain about TransportType not being AMQP Web Sockets when its explicitly defined.

Can't seem to send email with jhipster application

I'm trying to make a simple form that when submited sends and email to a fixed email.
I'm using spring and i've searched on how to configure the application.yml and i'm using the mailsend method that seems to have been generated with my jhipster application.
I've built my FE service to connect to the back end :
sendForm(): Observable<any>{
return this.http.post(SERVER_API_URL + 'api/sendForm', "");
}
i've built the onsubmit method to make the subscribe to the method above:
onSubmit() {
this.auth.sendForm().subscribe( data => {
console.log(data);
})
}
i've hard coded the mail resource just to mock an email to make sure its working:
#PostMapping("/sendForm")
public void sendForm() {
this.mailService.sendEmail("mymail#gmail.com","Header","texto",false,true);
}
the sendMail method that im sending the information for the mail submition is autogenerated and I believe it should be working
#Async
public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) {
log.debug("Send email[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}",
isMultipart, isHtml, to, subject, content);
// Prepare message using a Spring helper
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
try {
MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, StandardCharsets.UTF_8.name());
message.setTo(to);
message.setFrom(jHipsterProperties.getMail().getFrom());
message.setSubject(subject);
message.setText(content, isHtml);
javaMailSender.send(mimeMessage);
log.debug("Sent email to User '{}'", to);
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.warn("Email could not be sent to user '{}'", to, e);
} else {
log.warn("Email could not be sent to user '{}': {}", to, e.getMessage());
}
}
}
and heres my application-dev.yml (i'm still on dev)
spring:
profiles:
active: dev
mail:
host: smtp.gmail.com
port: 587
username: gmailuserid#gmail.com #Replace this field with your Gmail username.
password: ************ #Replace this field with your Gmail password.
protocol: smtp
tls: true
properties.mail.smtp:
auth: true
starttls.enable: true
ssl.trust: smtp.gmail.com
the errors im getting goes as follows:
org.springframework.mail.MailSendException: Mail server connection failed; nested exception is com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localh
ost, 25; timeout -1;
nested exception is:
java.net.ConnectException: Connection refused: connect. Failed messages: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 25; timeo
ut -1;
All I expect is a mail with the mock i've used and I cant seem to be able to put this working.
I hope i've not made the post to long and that everything is well explained.
Thank you in advance for anyone willing to help
Apparently somewhy the properties-prod.yml wasnt being loaded to the server. I had to create a config file with all the configurations for it to work

How to validate credentials in EWS Java API

I am using EWSJavaAPI 1.1.5. I am trying to login with invalid credentials, but I don't get any exceptions.
Please advise how to detect and handle invalid login.
Here is my code:
String host = "myhost";
ExchangeService service = null;
try {
service = new ExchangeService();
ExchangeCredentials credentials = new WebCredentials("wrongemail",
"wrongpass");
service.setCredentials(credentials);
service.setUrl(new java.net.URI("https://" + host
+ "/EWS/Exchange.asmx"));
} catch (Exception e) {
e.printStackTrace();
}
Found it, i had to bind the service to a folder:
Folder.bind(service, WellKnownFolderName.Inbox);
and if the credentials are wrong, HttpErrorException is thrown.

Resources